diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index cca6c69..c1dbc96 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 1 + fetch-depth: 0 - name: Set up Poetry run: | pipx install poetry diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8e2dd19..97d3410 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,8 +8,8 @@ env: PYTHON_VERSION: '3.12' jobs: - lint: - name: Lint source code + flake8: + name: Flake8 runs-on: ubuntu-latest steps: - name: Checkout repository @@ -33,6 +33,6 @@ jobs: run: | pip install poetry make install - - name: Run linter + - name: Run Flake8 run: | poetry run flake8 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..82314a5 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,38 @@ +name: Security + +on: + workflow_dispatch: + push: + +env: + PYTHON_VERSION: '3.12' + +jobs: + bandit: + name: Bandit + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Set up Poetry + run: | + pipx install poetry + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + cache: poetry + - name: Set up Poetry environment + env: + PYTHON_VERSION: ${{ env.PYTHON_VERSION }} + run: | + poetry env use ${PYTHON_VERSION} + - name: Install Python dependencies + run: | + pip install poetry + make install + - name: Run Bandit + run: | + poetry run bandit -r mkdocs_exporter diff --git a/Makefile b/Makefile index 2646c34..0c77431 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ ifeq ($(origin .RECIPEPREFIX), undefined) endif PDF = true +PDF_AGGREGATOR = true DEBUG = false .RECIPEPREFIX = > @@ -17,10 +18,10 @@ browser: > poetry run playwright install $(if $(FORCE),--force,) --with-deps build: ->@ MKDOCS_EXPORTER_PDF=$(PDF) poetry run mkdocs build +>@ MKDOCS_EXPORTER_PDF=$(PDF) MKDOCS_EXPORTER_PDF_AGGREGATOR=$(PDF_AGGREGATOR) poetry run mkdocs build serve: ->@ MKDOCS_EXPORTER_PDF=$(PDF) poetry run mkdocs serve +>@ MKDOCS_EXPORTER_PDF=$(PDF) MKDOCS_EXPORTER_PDF_AGGREGATOR=$(PDF_AGGREGATOR) poetry run mkdocs serve clean: > $(RM) -rf dist/ diff --git a/docs/configuration/generating-pdf-documents.md b/docs/configuration/generating-pdf-documents.md index 57136e4..4e09c03 100644 --- a/docs/configuration/generating-pdf-documents.md +++ b/docs/configuration/generating-pdf-documents.md @@ -251,4 +251,18 @@ plugins: aggregator: enabled: true output: documentation.pdf + covers: all ``` + +#### Configuring cover pages behavior + +When aggregating PDF documents, you have the flexibility to configure the behavior of cover pages to suit your needs. +There are five available options for managing cover pages: + +- `all` (*default*): retains every cover page from all documents. +- `none`: removes every cover page from the aggregated document, resulting in a compilation that includes only the main content of each PDF. +- `limits`: retains the front cover of the first document and the back cover of the last document, while removing all other cover pages in between. +- `front`: preserves every front cover page from all documents but removes all back cover pages. +- `back`: preserves every back cover page from all documents but removes all front cover pages. + +Choose the behavior that best aligns with your document aggregation needs to ensure the final PDF meets your requirements. diff --git a/docs/getting-started.md b/docs/getting-started.md index c5dee31..d9db5f6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -3,8 +3,6 @@ hide: - navigation --- - - # Getting started ## Introduction @@ -39,4 +37,4 @@ plugins: - exporter ``` -Check out the [configuration guides](configuration/generating-pdf-documents) for more details about how to use and configure the plugin. +Check out the [configuration guides](../configuration/generating-pdf-documents) for more details about how to use and configure the plugin. diff --git a/mkdocs.yml b/mkdocs.yml index 4eb19fa..9d9f0c9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,7 +47,6 @@ extra: plugins: - privacy: - log_level: warn - mkdocstrings: handlers: python: @@ -76,6 +75,9 @@ plugins: - social: cards_layout_options: background_color: '#EA2027' + - git-committers: + repository: adrienbrignon/mkdocs-exporter + branch: master - exporter: logging: level: debug @@ -87,13 +89,13 @@ plugins: - resources/stylesheets/pdf.scss covers: front: resources/templates/covers/front.html.j2 + back: resources/templates/covers/back.html.j2 browser: debug: false - headless: true aggregator: - enabled: true + enabled: !ENV [MKDOCS_EXPORTER_PDF_AGGREGATOR, true] output: documentation.pdf - covers: limits + covers: front buttons: - title: View as PDF icon: material-file-move-outline @@ -134,13 +136,13 @@ markdown_extensions: extra_css: - assets/stylesheets/custom.css - - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.css + - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css extra_javascript: - assets/scripts/katex.js - assets/scripts/mathjax.js - assets/scripts/mkdocs-exporter.js - - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.js - - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/contrib/auto-render.min.js + - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.js + - https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/contrib/auto-render.min.js - https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.min.js - - https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.6.1/mermaid.min.js + - https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.1/mermaid.min.js diff --git a/mkdocs_exporter/formats/pdf/aggregator.py b/mkdocs_exporter/formats/pdf/aggregator.py index 8d9da94..b9b897a 100644 --- a/mkdocs_exporter/formats/pdf/aggregator.py +++ b/mkdocs_exporter/formats/pdf/aggregator.py @@ -4,6 +4,7 @@ from pypdf import PdfWriter +from mkdocs_exporter.page import Page from mkdocs_exporter.formats.pdf.renderer import Renderer from mkdocs_exporter.formats.pdf.preprocessor import Preprocessor @@ -12,10 +13,11 @@ class Aggregator: """Aggregates PDF documents together.""" - def __init__(self, renderer: Renderer): + def __init__(self, renderer: Renderer, config: dict = {}) -> None: """The constructor.""" - self.total_pages = 0 + self.pages = [] + self.config = config self.renderer = renderer @@ -25,20 +27,50 @@ def open(self, path: str) -> Aggregator: self.path = path self.writer = PdfWriter() + return self + + + def set_pages(self, pages: list[Page]) -> Aggregator: + """Sets the pages.""" - def increment_total_pages(self, total_pages: int) -> Aggregator: - """Increments the total pages count.""" + self.pages = pages + covers = self.config.get('covers', []) - self.total_pages = self.total_pages + total_pages + for index, page in enumerate(self.pages): + if covers == 'none': + self._skip(page, ['front', 'back']) + elif covers == 'front': + self._skip(page, ['back']) + elif covers == 'back': + self._skip(page, ['front']) + elif covers == 'limits': + if index == 0: + self._skip(page, ['back']) + elif index == (len(self.pages) - 1): + self._skip(page, ['front']) + else: + self._skip(page, ['front', 'back']) + + return self - def preprocess(self, html: str, page_number: int = 1) -> str: + def preprocess(self, page: Page) -> str: """Preprocesses the page.""" preprocessor = Preprocessor() - preprocessor.preprocess(html) - preprocessor.metadata({'page': page_number, 'pages': self.total_pages}) + preprocessor.preprocess(self.renderer.preprocess(page, disable=['teleport'])) + + if 'front' not in page.formats['pdf']['covers']: + preprocessor.remove('div.mkdocs-exporter-front-cover') + if 'back' not in page.formats['pdf']['covers']: + preprocessor.remove('div.mkdocs-exporter-back-cover') + + preprocessor.teleport() + preprocessor.metadata({ + 'page': sum(page.formats['pdf']['pages'] - page.formats['pdf']['skipped_pages'] for page in self.pages[:page.index]), + 'pages': sum(page.formats['pdf']['pages'] - page.formats['pdf']['skipped_pages'] for page in self.pages), + }) return preprocessor.done() @@ -63,3 +95,14 @@ def save(self, metadata={}) -> Aggregator: self.writer = None return self + + + def _skip(self, page: Page, covers: list[str]) -> Aggregator: + """Skip cover pages.""" + + for cover in covers: + if cover in page.formats['pdf']['covers']: + page.formats['pdf']['covers'].remove(cover) + page.formats['pdf']['skipped_pages'] = page.formats['pdf']['skipped_pages'] + 1 + + return self diff --git a/mkdocs_exporter/formats/pdf/browser.py b/mkdocs_exporter/formats/pdf/browser.py index b5cd103..96b9c93 100644 --- a/mkdocs_exporter/formats/pdf/browser.py +++ b/mkdocs_exporter/formats/pdf/browser.py @@ -15,7 +15,8 @@ class Browser: args = [ '--disable-background-timer-throttling', '--disable-renderer-backgrounding', - '--allow-file-access-from-files' + '--allow-file-access-from-files', + '--font-render-hinting=none' ] """The browser's arguments...""" @@ -38,6 +39,7 @@ def __init__(self, options: dict = {}): self.debug = options.get('debug', False) self.headless = options.get('headless', True) self.timeout = options.get('timeout', 60_000) + self.args = self.args + options.get('args', []) self.levels = { 'warn': 'warning', 'error': 'error', @@ -100,13 +102,10 @@ async def print(self, html: str) -> tuple[bytes, int]: pages = int(await context.locator('body').get_attribute('mkdocs-exporter-pages') or 0) pdf = await context.pdf(prefer_css_page_size=True, print_background=True, display_header_footer=False) - try: - os.unlink(file) - except Exception: - pass - await context.close() + os.unlink(file.name) + return (pdf, pages) diff --git a/mkdocs_exporter/formats/pdf/config.py b/mkdocs_exporter/formats/pdf/config.py index 5c0c400..5d9b05d 100644 --- a/mkdocs_exporter/formats/pdf/config.py +++ b/mkdocs_exporter/formats/pdf/config.py @@ -14,6 +14,9 @@ class BrowserConfig(BaseConfig): timeout = c.Type(int, default=60_000) """The timeout when waiting for the PDF to render.""" + args = c.ListOfItems(c.Type(str), default=[]) + """Extra arguments to pass to the browser.""" + class CoversConfig(BaseConfig): """The cover's configuration.""" @@ -26,6 +29,7 @@ class CoversConfig(BaseConfig): class AggregatorConfig(BaseConfig): + """The aggregator's configuration.""" enabled = c.Type(bool, default=False) """Is the aggregator enabled?""" @@ -36,6 +40,9 @@ class AggregatorConfig(BaseConfig): metadata = c.Type(dict, default={}) """Some metadata to append to the PDF document.""" + covers = c.Choice(['all', 'none', 'limits', 'front', 'back'], default='all') + """The behavior of cover pages.""" + class Config(BaseConfig): """The plugin's configuration.""" diff --git a/mkdocs_exporter/formats/pdf/plugin.py b/mkdocs_exporter/formats/pdf/plugin.py index 0ec422f..6109ae0 100644 --- a/mkdocs_exporter/formats/pdf/plugin.py +++ b/mkdocs_exporter/formats/pdf/plugin.py @@ -63,15 +63,20 @@ def on_page_markdown(self, markdown: str, page: Page, config: Config, **kwargs) content = markdown covers = {**self.config.covers, **{k: os.path.join(os.path.dirname(config['config_file_path']), v) for k, v in page.meta.get('covers', {}).items()}} + for path in [path for path in covers.values() if path is not None]: + self.watch.append(path) + if covers.get('front'): + page.formats['pdf']['covers'].append('front') + with open(covers['front'], 'r', encoding='utf-8') as file: - content = self.renderer.cover(file.read()) + content + content = self.renderer.cover(file.read(), 'front') + content + if covers.get('back'): - with open(covers['back'], 'r', encoding='utf-8') as file: - content = content + self.renderer.cover(file.read()) + page.formats['pdf']['covers'].append('back') - for path in [path for path in covers.values() if path is not None]: - self.watch.append(path) + with open(covers['back'], 'r', encoding='utf-8') as file: + content = content + self.renderer.cover(file.read(), 'back') return content @@ -85,11 +90,10 @@ def on_pre_build(self, **kwargs) -> None: self.loop = asyncio.new_event_loop() self.renderer = Renderer(options=self.config) - if self.config.aggregator.get('enabled'): - self.aggregator = Aggregator(renderer=self.renderer) - asyncio.set_event_loop(self.loop) + if self.config.aggregator.get('enabled'): + self.aggregator = Aggregator(renderer=self.renderer, config=self.config.get('aggregator')) for stylesheet in self.config.stylesheets: self.renderer.add_stylesheet(stylesheet) for script in self.config.scripts: @@ -108,7 +112,10 @@ def on_pre_page(self, page: Page, config: dict, **kwargs): fullpath = os.path.join(directory, filename) page.formats['pdf'] = { + 'pages': 0, + 'covers': [], 'path': fullpath, + 'skipped_pages': 0, 'url': os.path.relpath(fullpath, config['site_dir']) } @@ -132,9 +139,6 @@ async def render(page: Page) -> None: with open(page.formats['pdf']['path'], 'wb+') as file: file.write(pdf) - if self.aggregator: - self.aggregator.increment_total_pages(pages) - self.tasks.append(render(page)) return page.html @@ -160,17 +164,19 @@ def _on_post_build_2(self, config: dict, **kwargs) -> None: output = self.config['aggregator']['output'] self.pages = [page for page in self.pages if 'pdf' in page.formats] - logger.info("[mkdocs-exporter.pdf] Aggregating %d pages from %d documents together as '%s'...", self.aggregator.total_pages, len(self.pages), output) + self.aggregator.set_pages(self.pages) - async def render(page: Page, page_number: int) -> None: - html = self.aggregator.preprocess(self.renderer.preprocess(page), page_number=page_number) + logger.info("[mkdocs-exporter.pdf] Aggregating pages to '%s'...", output) + + async def render(page: Page) -> None: + html = self.aggregator.preprocess(page) pdf, _ = await self.renderer.render(html) with open(page.formats['pdf']['path'] + '.aggregate', 'wb+') as file: file.write(pdf) - for n, page in enumerate(self.pages): - self.tasks.append(render(page, page_number=sum(page.formats['pdf']['pages'] for page in self.pages[:n]))) + for page in self.pages: + self.tasks.append(render(page)) while self.tasks: self.loop.run_until_complete(asyncio.gather(*concurrently(self.tasks, max(1, self.config.concurrency or 1)))) @@ -217,6 +223,9 @@ def flatten(items): self.pages = flatten(nav) + for index, page in enumerate(self.pages): + page.index = index + def _enabled(self, page: Page = None) -> bool: """Is the plugin enabled for this page?""" diff --git a/mkdocs_exporter/formats/pdf/renderer.py b/mkdocs_exporter/formats/pdf/renderer.py index 51f715c..ee59593 100644 --- a/mkdocs_exporter/formats/pdf/renderer.py +++ b/mkdocs_exporter/formats/pdf/renderer.py @@ -41,15 +41,15 @@ def add_script(self, path: str) -> Renderer: return self - def cover(self, template: str) -> Renderer: + def cover(self, template: str, location: str) -> Renderer: """Renders a cover.""" content = template.strip('\n') - return f'
{content}
' + '\n' + return f'
{content}
' + '\n' - def preprocess(self, page: Page) -> str: + def preprocess(self, page: Page, disable: list = []) -> str: """Preprocesses a page, returning HTML that can be printed.""" preprocessor = Preprocessor(theme=page.theme) @@ -67,9 +67,11 @@ def preprocess(self, page: Page) -> str: with open(script, 'r', encoding='utf-8') as file: preprocessor.script(file.read(), path=stylesheet) + if 'teleport' not in disable: + preprocessor.teleport() + preprocessor.script(importlib.resources.files(js).joinpath('pdf.js').read_text(encoding='utf-8')) preprocessor.script(importlib.resources.files(js).joinpath('pagedjs.min.js').read_text(encoding='utf-8')) - preprocessor.teleport() preprocessor.update_links(base, root) if self.options.get('url'): diff --git a/mkdocs_exporter/formats/pdf/resources/js/pdf.js b/mkdocs_exporter/formats/pdf/resources/js/pdf.js index e66328e..5cddf72 100644 --- a/mkdocs_exporter/formats/pdf/resources/js/pdf.js +++ b/mkdocs_exporter/formats/pdf/resources/js/pdf.js @@ -3,6 +3,7 @@ window.PagedConfig = { /** * The settings. */ + auto: true, settings: { maxChars: 1e32 }, diff --git a/mkdocs_exporter/page.py b/mkdocs_exporter/page.py index abdaeb2..a057c51 100644 --- a/mkdocs_exporter/page.py +++ b/mkdocs_exporter/page.py @@ -7,6 +7,9 @@ class Page(BasePage): """A page.""" + index: int + """The page's index.""" + html: Optional[str] = None """The page's HTML content.""" diff --git a/mkdocs_exporter/resources/css/material.css b/mkdocs_exporter/resources/css/material.css new file mode 100644 index 0000000..4482822 --- /dev/null +++ b/mkdocs_exporter/resources/css/material.css @@ -0,0 +1,6 @@ +/** + * @see https://github.com/adrienbrignon/mkdocs-exporter/issues/26 + */ +.highlight span.filename+pre { + margin-top: 0 !important; +} diff --git a/mkdocs_exporter/themes/material/theme.py b/mkdocs_exporter/themes/material/theme.py index 6f75c49..4e22c9b 100644 --- a/mkdocs_exporter/themes/material/theme.py +++ b/mkdocs_exporter/themes/material/theme.py @@ -1,8 +1,11 @@ from __future__ import annotations +import importlib + from bs4 import BeautifulSoup +from mkdocs_exporter.resources import css from mkdocs_exporter.theme import Theme as BaseTheme from mkdocs_exporter.preprocessor import Preprocessor from mkdocs_exporter.themes.material.icons import get_icon @@ -19,6 +22,7 @@ def preprocess(self, preprocessor: Preprocessor) -> None: """Preprocesses the DOM before rendering a document.""" preprocessor.remove(['.md-sidebar.md-sidebar--primary', '.md-sidebar.md-sidebar--secondary', 'header.md-header', '.md-container > nav', 'nav.md-tags']) + preprocessor.stylesheet(importlib.resources.files(css).joinpath('material.css').read_text(encoding='utf-8')) def button(self, preprocessor: Preprocessor, title: str, icon: str, attributes: dict = {}): diff --git a/poetry.lock b/poetry.lock index 1c86837..950b8e1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,6 +14,30 @@ files = [ [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +[[package]] +name = "bandit" +version = "1.7.9" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"}, + {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +baseline = ["GitPython (>=3.1.30)"] +sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] +toml = ["tomli (>=1.1.0)"] +yaml = ["PyYAML"] + [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -48,13 +72,13 @@ files = [ [[package]] name = "cairocffi" -version = "1.7.0" +version = "1.7.1" description = "cffi-based cairo bindings for Python" optional = false python-versions = ">=3.8" files = [ - {file = "cairocffi-1.7.0-py3-none-any.whl", hash = "sha256:1f29a8d41dbda4090c0aa33bcdea64f3b493e95f74a43ea107c4a8a7b7f632ef"}, - {file = "cairocffi-1.7.0.tar.gz", hash = "sha256:7761863603894305f3160eca68452f373433ca8745ab7dd445bd2c6ce50dcab7"}, + {file = "cairocffi-1.7.1-py3-none-any.whl", hash = "sha256:9803a0e11f6c962f3b0ae2ec8ba6ae45e957a146a004697a1ac1bbf16b073b3f"}, + {file = "cairocffi-1.7.1.tar.gz", hash = "sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b"}, ] [package.dependencies] @@ -328,18 +352,18 @@ files = [ [[package]] name = "flake8" -version = "7.0.0" +version = "7.1.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, - {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, + {file = "flake8-7.1.0-py2.py3-none-any.whl", hash = "sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a"}, + {file = "flake8-7.1.0.tar.gz", hash = "sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" +pycodestyle = ">=2.12.0,<2.13.0" pyflakes = ">=3.2.0,<3.3.0" [[package]] @@ -464,13 +488,13 @@ test = ["objgraph", "psutil"] [[package]] name = "griffe" -version = "0.45.2" +version = "0.47.0" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-0.45.2-py3-none-any.whl", hash = "sha256:297ec8530d0c68e5b98ff86fb588ebc3aa3559bb5dc21f3caea8d9542a350133"}, - {file = "griffe-0.45.2.tar.gz", hash = "sha256:83ce7dcaafd8cb7f43cbf1a455155015a1eb624b1ffd93249e5e1c4a22b2fdb2"}, + {file = "griffe-0.47.0-py3-none-any.whl", hash = "sha256:07a2fd6a8c3d21d0bbb0decf701d62042ccc8a576645c7f8799fe1f10de2b2de"}, + {file = "griffe-0.47.0.tar.gz", hash = "sha256:95119a440a3c932b13293538bdbc405bee4c36428547553dc6b327e7e7d35e5a"}, ] [package.dependencies] @@ -499,22 +523,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.1.0" +version = "7.2.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, - {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, + {file = "importlib_metadata-7.2.0-py3-none-any.whl", hash = "sha256:04e4aad329b8b948a5711d394fa8759cb80f009225441b4f2a02bd4d8e5f426c"}, + {file = "importlib_metadata-7.2.0.tar.gz", hash = "sha256:3ff4519071ed42740522d494d04819b666541b9752c43012f85afb2cc220fcc6"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "jinja2" @@ -734,6 +758,30 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" version = "2.1.5" @@ -814,6 +862,17 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mdx-truly-sane-lists" version = "1.3" @@ -920,18 +979,20 @@ platformdirs = ">=2.2.0" pyyaml = ">=5.1" [[package]] -name = "mkdocs-git-authors-plugin" -version = "0.7.2" -description = "Mkdocs plugin to display git authors of a page" +name = "mkdocs-git-committers-plugin-2" +version = "2.3.0" +description = "An MkDocs plugin to create a list of contributors on the page. The git-committers plugin will seed the template context with a list of GitHub or GitLab committers and other useful GIT info such as last modified date" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8,<4" files = [ - {file = "mkdocs-git-authors-plugin-0.7.2.tar.gz", hash = "sha256:f541730e4cabdafa0ac758c94d28ba5e8ddca4c859e5de4c89f1226cb6ccd0ad"}, - {file = "mkdocs_git_authors_plugin-0.7.2-py3-none-any.whl", hash = "sha256:c8a2784a867db79ad3b477a96ee96875d17b09192b6d3be71f08df25afff76c4"}, + {file = "mkdocs-git-committers-plugin-2-2.3.0.tar.gz", hash = "sha256:d6baca1ae04db8120640038eda8142f2d081c27b53f3b566c83c75717e4ed81a"}, + {file = "mkdocs_git_committers_plugin_2-2.3.0-py3-none-any.whl", hash = "sha256:7b3434af3be525c12858eb3b44b4c6b695b7c7b7760482ea8de1c6e292e84f0f"}, ] [package.dependencies] -mkdocs = ">=1.0" +gitpython = "*" +mkdocs = ">=1.0.3" +requests = "*" [[package]] name = "mkdocs-git-revision-date-localized-plugin" @@ -973,13 +1034,13 @@ test = ["mkdocs-include-markdown-plugin", "mkdocs-macros-test", "mkdocs-material [[package]] name = "mkdocs-material" -version = "9.5.26" +version = "9.5.27" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.26-py3-none-any.whl", hash = "sha256:5d01fb0aa1c7946a1e3ae8689aa2b11a030621ecb54894e35aabb74c21016312"}, - {file = "mkdocs_material-9.5.26.tar.gz", hash = "sha256:56aeb91d94cffa43b6296fa4fbf0eb7c840136e563eecfd12c2d9e92e50ba326"}, + {file = "mkdocs_material-9.5.27-py3-none-any.whl", hash = "sha256:af8cc263fafa98bb79e9e15a8c966204abf15164987569bd1175fd66a7705182"}, + {file = "mkdocs_material-9.5.27.tar.gz", hash = "sha256:a7d4a35f6d4a62b0c43a0cfe7e987da0980c13587b5bc3c26e690ad494427ec0"}, ] [package.dependencies] @@ -1079,17 +1140,17 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] [[package]] name = "mkdocstrings-python" -version = "1.10.3" +version = "1.10.5" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocstrings_python-1.10.3-py3-none-any.whl", hash = "sha256:11ff6d21d3818fb03af82c3ea6225b1534837e17f790aa5f09626524171f949b"}, - {file = "mkdocstrings_python-1.10.3.tar.gz", hash = "sha256:321cf9c732907ab2b1fedaafa28765eaa089d89320f35f7206d00ea266889d03"}, + {file = "mkdocstrings_python-1.10.5-py3-none-any.whl", hash = "sha256:92e3c588ef1b41151f55281d075de7558dd8092e422cb07a65b18ee2b0863ebb"}, + {file = "mkdocstrings_python-1.10.5.tar.gz", hash = "sha256:acdc2a98cd9d46c7ece508193a16ca03ccabcb67520352b7449f84b57c162bdf"}, ] [package.dependencies] -griffe = ">=0.44" +griffe = ">=0.47" mkdocstrings = ">=0.25" [[package]] @@ -1109,13 +1170,13 @@ icu = ["PyICU (>=1.0.0)"] [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -1139,6 +1200,17 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] +[[package]] +name = "pbr" +version = "6.0.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, +] + [[package]] name = "pillow" version = "10.3.0" @@ -1263,13 +1335,13 @@ pyee = "11.1.0" [[package]] name = "pycodestyle" -version = "2.11.1" +version = "2.12.0" description = "Python style guide checker" optional = false python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, + {file = "pycodestyle-2.12.0-py2.py3-none-any.whl", hash = "sha256:949a39f6b86c3e1515ba1787c2022131d165a8ad271b11370a8819aa070269e4"}, + {file = "pycodestyle-2.12.0.tar.gz", hash = "sha256:442f950141b4f43df752dd303511ffded3a04c2b6fb7f65980574f0c31e6e79c"}, ] [[package]] @@ -1571,6 +1643,24 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "six" version = "1.16.0" @@ -1604,6 +1694,20 @@ files = [ {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] +[[package]] +name = "stevedore" +version = "5.2.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, + {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, +] + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + [[package]] name = "termcolor" version = "2.4.0" @@ -1649,13 +1753,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -1750,5 +1854,5 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" -python-versions = ">=3.9" -content-hash = "2d93295f86f376ca2d37dd139753959bd4cab9ac10dbea82d089af8ea5ec9c48" +python-versions = ">=3.9,<4" +content-hash = "5906c8eea00f369c650f25795b5a29336846d1d09c8a6d4db01c0ff870281fa7" diff --git a/pyproject.toml b/pyproject.toml index b4d69ab..a002229 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "mkdocs-exporter" -version = "6.0.0" +version = "6.1.0" repository = "https://github.com/adrienbrignon/mkdocs-exporter" keywords = ["mkdocs", "pdf", "exporter"] description = "A highly-configurable plugin for MkDocs that exports your pages to PDF files." @@ -20,7 +20,7 @@ classifiers = [ ] [tool.poetry.dependencies] -python = ">=3.9" +python = ">=3.9,<4" mkdocs = ">=1.4" playwright = ">=1.33" beautifulsoup4 = ">=4.12.2" @@ -38,10 +38,11 @@ pypdf = ">=4.2.0" flake8 = ">=7.0" mkdocs-material = {version="^9.4.11", extras=["imaging"]} mkdocs-git-revision-date-localized-plugin = "^1.2.0" -mkdocs-git-authors-plugin = "^0.7.0" mkdocs-awesome-pages-plugin = "^2.9.1" mkdocs-macros-plugin = "^1.0.4" mkdocs-minify-plugin = "^0.6.4" mkdocs-redirects = "^1.2.0" mdx-truly-sane-lists = "^1.3" mkdocstrings = {extras = ["python"], version = "^0.25.1"} +mkdocs-git-committers-plugin-2 = "^2.3.0" +bandit = "^1.7.9"