├── .copier-answers.yml
├── .envrc
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ └── feature_request.md
└── workflows
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── .gitpod.dockerfile
├── .gitpod.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CREDITS.md
├── LICENSE
├── Makefile
├── README.md
├── config
├── coverage.ini
├── git-changelog.toml
├── mypy.ini
├── pytest.ini
├── ruff.toml
└── vscode
│ ├── launch.json
│ ├── settings.json
│ └── tasks.json
├── devdeps.txt
├── docs
├── .overrides
│ └── main.html
├── changelog.md
├── code_of_conduct.md
├── contributing.md
├── credits.md
├── css
│ ├── material.css
│ └── mkdocstrings.css
├── gen_credits.py
├── index.md
├── license.md
└── schema.json
├── duties.py
├── mkdocs.yml
├── pyproject.toml
├── scripts
├── gen_credits.py
├── gen_ref_nav.py
└── make
├── src
└── mkdocs_coverage
│ ├── __init__.py
│ ├── debug.py
│ ├── loggers.py
│ ├── plugin.py
│ └── py.typed
└── tests
├── __init__.py
├── conftest.py
└── test_plugin.py
/.copier-answers.yml:
--------------------------------------------------------------------------------
1 | # Changes here will be overwritten by Copier
2 | _commit: 1.2.8
3 | _src_path: gh:pawamoy/copier-uv
4 | author_email: dev@pawamoy.fr
5 | author_fullname: Timothée Mazzucotelli
6 | author_username: pawamoy
7 | copyright_date: '2021'
8 | copyright_holder: Timothée Mazzucotelli
9 | copyright_holder_email: dev@pawamoy.fr
10 | copyright_license: ISC License
11 | insiders: false
12 | project_description: MkDocs plugin to integrate your coverage HTML report into your site.
13 | project_name: MkDocs Coverage Plugin
14 | python_package_command_line_name: ''
15 | python_package_distribution_name: mkdocs-coverage
16 | python_package_import_name: mkdocs_coverage
17 | repository_name: mkdocs-coverage
18 | repository_namespace: pawamoy
19 | repository_provider: github.com
20 |
21 |
--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------
1 | PATH_add scripts
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: pawamoy
2 | ko_fi: pawamoy
3 | polar: pawamoy
4 | custom:
5 | - https://www.paypal.me/pawamoy
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a bug report to help us improve.
4 | title: "bug: "
5 | labels: unconfirmed
6 | assignees: [pawamoy]
7 | ---
8 |
9 | ### Description of the bug
10 |
11 |
12 | ### To Reproduce
13 |
28 |
29 | ```
30 | WRITE MRE / INSTRUCTIONS HERE
31 | ```
32 |
33 | ### Full traceback
34 |
36 |
37 | Full traceback
38 |
39 | ```python
40 | PASTE TRACEBACK HERE
41 | ```
42 |
43 |
44 |
45 | ### Expected behavior
46 |
47 |
48 | ### Environment information
49 |
51 |
52 | ```bash
53 | python -m mkdocs_coverage.debug # | xclip -selection clipboard
54 | ```
55 |
56 | PASTE OUTPUT HERE
57 |
58 | ### Additional context
59 |
62 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: I have a question / I need help
4 | url: https://github.com/pawamoy/mkdocs-coverage/discussions/new?category=q-a
5 | about: Ask and answer questions in the Discussions tab.
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project.
4 | title: "feature: "
5 | labels: feature
6 | assignees: pawamoy
7 | ---
8 |
9 | ### Is your feature request related to a problem? Please describe.
10 |
11 |
12 | ### Describe the solution you'd like
13 |
14 |
15 | ### Describe alternatives you've considered
16 |
17 |
18 | ### Additional context
19 |
20 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | push:
5 | pull_request:
6 | branches:
7 | - main
8 |
9 | defaults:
10 | run:
11 | shell: bash
12 |
13 | env:
14 | LANG: en_US.utf-8
15 | LC_ALL: en_US.utf-8
16 | PYTHONIOENCODING: UTF-8
17 | PYTHON_VERSIONS: ""
18 |
19 | jobs:
20 |
21 | quality:
22 |
23 | runs-on: ubuntu-latest
24 |
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v4
28 |
29 | - name: Fetch all tags
30 | run: git fetch --depth=1 --tags
31 |
32 | - name: Set up Python
33 | uses: actions/setup-python@v5
34 | with:
35 | python-version: "3.11"
36 |
37 | - name: Install uv
38 | run: pip install uv
39 |
40 | - name: Install dependencies
41 | run: make setup
42 |
43 | - name: Check if the documentation builds correctly
44 | run: make check-docs
45 |
46 | - name: Check the code quality
47 | run: make check-quality
48 |
49 | - name: Check if the code is correctly typed
50 | run: make check-types
51 |
52 | - name: Check for breaking changes in the API
53 | run: make check-api
54 |
55 | tests:
56 |
57 | strategy:
58 | matrix:
59 | os:
60 | - ubuntu-latest
61 | - macos-latest
62 | - windows-latest
63 | python-version:
64 | - "3.8"
65 | - "3.9"
66 | - "3.10"
67 | - "3.11"
68 | - "3.12"
69 | - "3.13"
70 | resolution:
71 | - highest
72 | - lowest-direct
73 | exclude:
74 | - os: macos-latest
75 | resolution: lowest-direct
76 | - os: windows-latest
77 | resolution: lowest-direct
78 | runs-on: ${{ matrix.os }}
79 | continue-on-error: ${{ matrix.python-version == '3.13' }}
80 |
81 | steps:
82 | - name: Checkout
83 | uses: actions/checkout@v4
84 |
85 | - name: Set up Python
86 | uses: actions/setup-python@v5
87 | with:
88 | python-version: ${{ matrix.python-version }}
89 | allow-prereleases: true
90 |
91 | - name: Install uv
92 | run: pip install uv
93 |
94 | - name: Install dependencies
95 | env:
96 | UV_RESOLUTION: ${{ matrix.resolution }}
97 | run: make setup
98 |
99 | - name: Run the test suite
100 | run: make test
101 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on: push
4 | permissions:
5 | contents: write
6 |
7 | jobs:
8 | release:
9 | runs-on: ubuntu-latest
10 | if: startsWith(github.ref, 'refs/tags/')
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v4
14 | - name: Fetch all tags
15 | run: git fetch --depth=1 --tags
16 | - name: Setup Python
17 | uses: actions/setup-python@v4
18 | - name: Install git-changelog
19 | run: pip install git-changelog
20 | - name: Prepare release notes
21 | run: git-changelog --release-notes > release-notes.md
22 | - name: Create release
23 | uses: softprops/action-gh-release@v1
24 | with:
25 | body_path: release-notes.md
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # editors
2 | .idea/
3 | .vscode/
4 |
5 | # python
6 | *.egg-info/
7 | *.py[cod]
8 | .venv/
9 | .venvs/
10 | /build/
11 | /dist/
12 |
13 | # tools
14 | .coverage*
15 | /.pdm-build/
16 | /htmlcov/
17 | /site/
18 |
19 | # cache
20 | .cache/
21 | .pytest_cache/
22 | .mypy_cache/
23 | .ruff_cache/
24 | __pycache__/
25 |
--------------------------------------------------------------------------------
/.gitpod.dockerfile:
--------------------------------------------------------------------------------
1 | FROM gitpod/workspace-full
2 | USER gitpod
3 | ENV PIP_USER=no
4 | RUN pip3 install pipx; \
5 | pipx install uv; \
6 | pipx ensurepath
7 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | vscode:
2 | extensions:
3 | - ms-python.python
4 |
5 | image:
6 | file: .gitpod.dockerfile
7 |
8 | ports:
9 | - port: 8000
10 | onOpen: notify
11 |
12 | tasks:
13 | - init: make setup
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6 |
7 |
8 | ## [1.1.0](https://github.com/pawamoy/mkdocs-coverage/releases/tag/1.1.0) - 2024-06-11
9 |
10 | [Compare with 1.0.0](https://github.com/pawamoy/mkdocs-coverage/compare/1.0.0...1.1.0)
11 |
12 | ### Build
13 |
14 | - Depend on MkDocs 1.6+ ([d2e93b6](https://github.com/pawamoy/mkdocs-coverage/commit/d2e93b6b23ca714351c09f96cf8dd2c444c77b00) by Timothée Mazzucotelli).
15 |
16 | ### Code Refactoring
17 |
18 | - Use more modern features of MkDocs, rename `page_name` to `page_path` to allow nested pages ([6087394](https://github.com/pawamoy/mkdocs-coverage/commit/60873943a87c88349956efd9fd854f20ff134968) by Timothée Mazzucotelli).
19 |
20 | ## [1.0.0](https://github.com/pawamoy/mkdocs-coverage/releases/tag/1.0.0) - 2023-08-02
21 |
22 | [Compare with 0.2.7](https://github.com/pawamoy/mkdocs-coverage/compare/0.2.7...1.0.0)
23 |
24 | ### Breaking Changes
25 |
26 | - Drop support for Python 3.7
27 |
28 | ### Code Refactoring
29 |
30 | - Stop using deprecated warning filter ([fb4d9e6](https://github.com/pawamoy/mkdocs-coverage/commit/fb4d9e6f7b34ecc66c596b7dc4f475a44ce0404c) by Timothée Mazzucotelli).
31 |
32 | ## [0.2.7](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.2.7) - 2023-04-11
33 |
34 | [Compare with 0.2.6](https://github.com/pawamoy/mkdocs-coverage/compare/0.2.6...0.2.7)
35 |
36 | ### Code Refactoring
37 |
38 | - Stop using deprecated distutils ([47c129c](https://github.com/pawamoy/mkdocs-coverage/commit/47c129ce783cc5d908ec946d19010adb059fed0d) by Timothée Mazzucotelli).
39 |
40 | ## [0.2.6](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.2.6) - 2022-11-13
41 |
42 | [Compare with 0.2.5](https://github.com/pawamoy/mkdocs-coverage/compare/0.2.5...0.2.6)
43 |
44 | ### Bug Fixes
45 | - Fix iframe width for recent Material versions ([67c530b](https://github.com/pawamoy/mkdocs-coverage/commit/67c530be834f2e0af251d3bc1db5138a54e6de72) by Timothée Mazzucotelli).
46 |
47 |
48 | ## [0.2.5](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.2.5) - 2021-12-16
49 |
50 | [Compare with 0.2.4](https://github.com/pawamoy/mkdocs-coverage/compare/0.2.4...0.2.5)
51 |
52 | ### Bug Fixes
53 | - Support no directory URLs ([e427be0](https://github.com/pawamoy/mkdocs-coverage/commit/e427be0d8089629c23fba1879fb06fb4715d00e7) by Timothée Mazzucotelli). [Issue #5](https://github.com/pawamoy/mkdocs-coverage/issues/5)
54 |
55 |
56 | ## [0.2.4](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.2.4) - 2021-05-20
57 |
58 | [Compare with 0.2.3](https://github.com/pawamoy/mkdocs-coverage/compare/0.2.3...0.2.4)
59 |
60 | ### Bug Fixes
61 | - Reset iframe height between page changes ([5519c13](https://github.com/pawamoy/mkdocs-coverage/commit/5519c1352759f36b5ff3e1f800ac41fd12cd4acb) by Timothée Mazzucotelli). [Issue #1](https://github.com/pawamoy/mkdocs-coverage/issues/1)
62 |
63 |
64 | ## [0.2.3](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.2.3) - 2021-05-16
65 |
66 | [Compare with 0.2.2](https://github.com/pawamoy/mkdocs-coverage/compare/0.2.2...0.2.3)
67 |
68 | ### Packaging
69 |
70 | - Don't restrict supported Python versions to less than 3.10.
71 |
72 |
73 | ## [0.2.2](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.2.2) - 2021-05-06
74 |
75 | [Compare with 0.2.1](https://github.com/pawamoy/mkdocs-coverage/compare/0.2.1...0.2.2)
76 |
77 | ### Packaging
78 |
79 | - Switch to PDM as project management tool.
80 | - Stop including README.md and pyproject.toml in wheels. It was causing errors in PDM and Poetry when installed in parallel.
81 |
82 |
83 | ## [0.2.1](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.2.1) - 2021-02-03
84 |
85 | [Compare with 0.2.0](https://github.com/pawamoy/mkdocs-coverage/compare/0.2.0...0.2.1)
86 |
87 | ### Bug Fixes
88 | - Don't replace `index.html` everywhere ([ca1da70](https://github.com/pawamoy/mkdocs-coverage/commit/ca1da7003282b20af4cda72ae0ae62849dab1f63) by Timothée Mazzucotelli). [Issue #2](https://github.com/pawamoy/mkdocs-coverage/issues/2)
89 |
90 |
91 | ## [0.2.0](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.2.0) - 2021-02-03
92 |
93 | [Compare with 0.1.0](https://github.com/pawamoy/mkdocs-coverage/compare/0.1.0...0.2.0)
94 |
95 | ### Features
96 | - Implement coverage integration ([b52ac1d](https://github.com/pawamoy/mkdocs-coverage/commit/b52ac1def13c2dda648f4021b3d81f0e850001e4) by Timothée Mazzucotelli).
97 |
98 |
99 | ## [0.1.0](https://github.com/pawamoy/mkdocs-coverage/releases/tag/0.1.0) - 2021-02-03
100 |
101 | [Compare with first commit](https://github.com/pawamoy/mkdocs-coverage/compare/de2b9feab0e3f1a8ff8809a5ef9e9da55e201838...0.1.0)
102 |
103 | ### Features
104 | - Skeleton ([de2b9fe](https://github.com/pawamoy/mkdocs-coverage/commit/de2b9feab0e3f1a8ff8809a5ef9e9da55e201838) by Timothée Mazzucotelli).
105 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, caste, color, religion, or sexual
10 | identity and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the overall
26 | community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or advances of
31 | any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email address,
35 | without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | dev@pawamoy.fr.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series of
86 | actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or permanent
93 | ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within the
113 | community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.1, available at
119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120 |
121 | Community Impact Guidelines were inspired by
122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123 |
124 | For answers to common questions about this code of conduct, see the FAQ at
125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126 | [https://www.contributor-covenant.org/translations][translations].
127 |
128 | [homepage]: https://www.contributor-covenant.org
129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130 | [Mozilla CoC]: https://github.com/mozilla/diversity
131 | [FAQ]: https://www.contributor-covenant.org/faq
132 | [translations]: https://www.contributor-covenant.org/translations
133 |
134 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are welcome, and they are greatly appreciated!
4 | Every little bit helps, and credit will always be given.
5 |
6 | ## Environment setup
7 |
8 | Nothing easier!
9 |
10 | Fork and clone the repository, then:
11 |
12 | ```bash
13 | cd mkdocs-coverage
14 | make setup
15 | ```
16 |
17 | > NOTE:
18 | > If it fails for some reason,
19 | > you'll need to install
20 | > [uv](https://github.com/astral-sh/uv)
21 | > manually.
22 | >
23 | > You can install it with:
24 | >
25 | > ```bash
26 | > python3 -m pip install --user pipx
27 | > pipx install uv
28 | > ```
29 | >
30 | > Now you can try running `make setup` again,
31 | > or simply `uv install`.
32 |
33 | You now have the dependencies installed.
34 |
35 | Run `make help` to see all the available actions!
36 |
37 | ## Tasks
38 |
39 | This project uses [duty](https://github.com/pawamoy/duty) to run tasks.
40 | A Makefile is also provided. The Makefile will try to run certain tasks
41 | on multiple Python versions. If for some reason you don't want to run the task
42 | on multiple Python versions, you run the task directly with `make run duty TASK`.
43 |
44 | The Makefile detects if a virtual environment is activated,
45 | so `make` will work the same with the virtualenv activated or not.
46 |
47 | If you work in VSCode, we provide
48 | [an action to configure VSCode](https://pawamoy.github.io/copier-uv/work/#vscode-setup)
49 | for the project.
50 |
51 | ## Development
52 |
53 | As usual:
54 |
55 | 1. create a new branch: `git switch -c feature-or-bugfix-name`
56 | 1. edit the code and/or the documentation
57 |
58 | **Before committing:**
59 |
60 | 1. run `make format` to auto-format the code
61 | 1. run `make check` to check everything (fix any warning)
62 | 1. run `make test` to run the tests (fix any issue)
63 | 1. if you updated the documentation or the project dependencies:
64 | 1. run `make docs`
65 | 1. go to http://localhost:8000 and check that everything looks good
66 | 1. follow our [commit message convention](#commit-message-convention)
67 |
68 | If you are unsure about how to fix or ignore a warning,
69 | just let the continuous integration fail,
70 | and we will help you during review.
71 |
72 | Don't bother updating the changelog, we will take care of this.
73 |
74 | ## Commit message convention
75 |
76 | Commit messages must follow our convention based on the
77 | [Angular style](https://gist.github.com/stephenparish/9941e89d80e2bc58a153#format-of-the-commit-message)
78 | or the [Karma convention](https://karma-runner.github.io/4.0/dev/git-commit-msg.html):
79 |
80 | ```
81 | [(scope)]: Subject
82 |
83 | [Body]
84 | ```
85 |
86 | **Subject and body must be valid Markdown.**
87 | Subject must have proper casing (uppercase for first letter
88 | if it makes sense), but no dot at the end, and no punctuation
89 | in general.
90 |
91 | Scope and body are optional. Type can be:
92 |
93 | - `build`: About packaging, building wheels, etc.
94 | - `chore`: About packaging or repo/files management.
95 | - `ci`: About Continuous Integration.
96 | - `deps`: Dependencies update.
97 | - `docs`: About documentation.
98 | - `feat`: New feature.
99 | - `fix`: Bug fix.
100 | - `perf`: About performance.
101 | - `refactor`: Changes that are not features or bug fixes.
102 | - `style`: A change in code style/format.
103 | - `tests`: About tests.
104 |
105 | If you write a body, please add trailers at the end
106 | (for example issues and PR references, or co-authors),
107 | without relying on GitHub's flavored Markdown:
108 |
109 | ```
110 | Body.
111 |
112 | Issue #10: https://github.com/namespace/project/issues/10
113 | Related to PR namespace/other-project#15: https://github.com/namespace/other-project/pull/15
114 | ```
115 |
116 | These "trailers" must appear at the end of the body,
117 | without any blank lines between them. The trailer title
118 | can contain any character except colons `:`.
119 | We expect a full URI for each trailer, not just GitHub autolinks
120 | (for example, full GitHub URLs for commits and issues,
121 | not the hash or the #issue-number).
122 |
123 | We do not enforce a line length on commit messages summary and body,
124 | but please avoid very long summaries, and very long lines in the body,
125 | unless they are part of code blocks that must not be wrapped.
126 |
127 | ## Pull requests guidelines
128 |
129 | Link to any related issue in the Pull Request message.
130 |
131 | During the review, we recommend using fixups:
132 |
133 | ```bash
134 | # SHA is the SHA of the commit you want to fix
135 | git commit --fixup=SHA
136 | ```
137 |
138 | Once all the changes are approved, you can squash your commits:
139 |
140 | ```bash
141 | git rebase -i --autosquash main
142 | ```
143 |
144 | And force-push:
145 |
146 | ```bash
147 | git push -f
148 | ```
149 |
150 | If this seems all too complicated, you can push or force-push each new commit,
151 | and we will squash them ourselves if needed, before merging.
152 |
--------------------------------------------------------------------------------
/CREDITS.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Credits
6 | These projects were used to build `mkdocs-coverage`. **Thank you!**
7 |
8 | [`python`](https://www.python.org/) |
9 | [`poetry`](https://poetry.eustace.io/) |
10 | [`copier-poetry`](https://github.com/pawamoy/copier-poetry)
11 |
12 | ### Direct dependencies
13 | [`autoflake`](https://github.com/myint/autoflake) |
14 | [`black`](https://github.com/psf/black) |
15 | [`duty`](https://github.com/pawamoy/duty) |
16 | [`flake8-black`](https://github.com/peterjc/flake8-black) |
17 | [`flake8-builtins`](https://github.com/gforcada/flake8-builtins) |
18 | [`flake8-pytest-style`](https://pypi.org/project/flake8-pytest-style) |
19 | [`flake8-tidy-imports`](https://github.com/adamchainz/flake8-tidy-imports) |
20 | [`flake8-variables-names`](https://github.com/best-doctor/flake8-variables-names) |
21 | [`flakehell`](None) |
22 | [`git-changelog`](https://github.com/pawamoy/git-changelog) |
23 | [`httpx`](https://github.com/encode/httpx) |
24 | [`ipython`](https://ipython.org) |
25 | [`isort`](https://github.com/timothycrosley/isort) |
26 | [`jinja2-cli`](https://github.com/mattrobenolt/jinja2-cli) |
27 | [`markdown-include`](https://github.com/cmacmackin/markdown-include/) |
28 | [`mkdocs`](https://www.mkdocs.org) |
29 | [`mkdocs-material`](https://squidfunk.github.io/mkdocs-material/) |
30 | [`mkdocstrings`](https://github.com/pawamoy/mkdocstrings) |
31 | [`mypy`](http://www.mypy-lang.org/) |
32 | [`pytest`](https://docs.pytest.org/en/latest/) |
33 | [`pytest-cov`](https://github.com/pytest-dev/pytest-cov) |
34 | [`pytest-randomly`](https://github.com/pytest-dev/pytest-randomly) |
35 | [`pytest-sugar`](http://pivotfinland.com/pytest-sugar/) |
36 | [`pytest-xdist`](https://github.com/pytest-dev/pytest-xdist) |
37 | [`toml`](https://github.com/uiri/toml) |
38 | [`wemake-python-styleguide`](https://wemake-python-stylegui.de)
39 |
40 | ### Indirect dependencies
41 | [`ansimarkup`](https://github.com/gvalkov/python-ansimarkup) |
42 | [`apipkg`](https://github.com/pytest-dev/apipkg) |
43 | [`appdirs`](http://github.com/ActiveState/appdirs) |
44 | [`appnope`](http://github.com/minrk/appnope) |
45 | [`astor`](https://github.com/berkerpeksag/astor) |
46 | [`atomicwrites`](https://github.com/untitaker/python-atomicwrites) |
47 | [`attrs`](https://www.attrs.org/) |
48 | [`backcall`](https://github.com/takluyver/backcall) |
49 | [`bandit`](https://bandit.readthedocs.io/en/latest/) |
50 | [`certifi`](https://certifiio.readthedocs.io/en/latest/) |
51 | [`click`](https://palletsprojects.com/p/click/) |
52 | [`colorama`](https://github.com/tartley/colorama) |
53 | [`contextvars`](http://github.com/MagicStack/contextvars) |
54 | [`coverage`](https://github.com/nedbat/coveragepy) |
55 | [`darglint`](None) |
56 | [`dataclasses`](https://github.com/ericvsmith/dataclasses) |
57 | [`decorator`](https://github.com/micheles/decorator) |
58 | [`docutils`](http://docutils.sourceforge.net/) |
59 | [`entrypoints`](https://github.com/takluyver/entrypoints) |
60 | [`eradicate`](https://github.com/myint/eradicate) |
61 | [`execnet`](https://execnet.readthedocs.io/en/latest/) |
62 | [`failprint`](https://github.com/pawamoy/failprint) |
63 | [`flake8`](https://gitlab.com/pycqa/flake8) |
64 | [`flake8-bandit`](https://github.com/tylerwince/flake8-bandit) |
65 | [`flake8-broken-line`](https://github.com/sobolevn/flake8-broken-line) |
66 | [`flake8-bugbear`](https://github.com/PyCQA/flake8-bugbear) |
67 | [`flake8-commas`](https://github.com/PyCQA/flake8-commas/) |
68 | [`flake8-comprehensions`](https://github.com/adamchainz/flake8-comprehensions) |
69 | [`flake8-debugger`](https://github.com/jbkahn/flake8-debugger) |
70 | [`flake8-docstrings`](https://gitlab.com/pycqa/flake8-docstrings) |
71 | [`flake8-eradicate`](https://github.com/sobolevn/flake8-eradicate) |
72 | [`flake8-isort`](https://github.com/gforcada/flake8-isort) |
73 | [`flake8-plugin-utils`](https://pypi.org/project/flake8-plugin-utils) |
74 | [`flake8-polyfill`](https://gitlab.com/pycqa/flake8-polyfill) |
75 | [`flake8-quotes`](http://github.com/zheller/flake8-quotes/) |
76 | [`flake8-rst-docstrings`](https://github.com/peterjc/flake8-rst-docstrings) |
77 | [`flake8-string-format`](https://github.com/xZise/flake8-string-format) |
78 | [`future`](https://python-future.org) |
79 | [`gitdb`](https://github.com/gitpython-developers/gitdb) |
80 | [`GitPython`](https://github.com/gitpython-developers/GitPython) |
81 | [`h11`](https://github.com/python-hyper/h11) |
82 | [`httpcore`](https://github.com/encode/httpcore) |
83 | [`idna`](https://github.com/kjd/idna) |
84 | [`immutables`](https://github.com/MagicStack/immutables) |
85 | [`importlib-metadata`](https://github.com/python/importlib_metadata) |
86 | [`iniconfig`](http://github.com/RonnyPfannschmidt/iniconfig) |
87 | [`ipython-genutils`](http://ipython.org) |
88 | [`jedi`](https://github.com/davidhalter/jedi) |
89 | [`Jinja2`](https://palletsprojects.com/p/jinja/) |
90 | [`joblib`](https://joblib.readthedocs.io) |
91 | [`livereload`](https://github.com/lepture/python-livereload) |
92 | [`lunr`](https://github.com/yeraydiazdiaz/lunr.py) |
93 | [`Markdown`](https://Python-Markdown.github.io/) |
94 | [`MarkupSafe`](https://palletsprojects.com/p/markupsafe/) |
95 | [`mccabe`](https://github.com/pycqa/mccabe) |
96 | [`mkdocs-material-extensions`](https://github.com/facelessuser/mkdocs-material-extensions) |
97 | [`mypy-extensions`](https://github.com/python/mypy_extensions) |
98 | [`nltk`](http://nltk.org/) |
99 | [`packaging`](https://github.com/pypa/packaging) |
100 | [`parso`](https://github.com/davidhalter/parso) |
101 | [`pathspec`](https://github.com/cpburnz/python-path-specification) |
102 | [`pbr`](https://docs.openstack.org/pbr/latest/) |
103 | [`pep8-naming`](https://github.com/PyCQA/pep8-naming) |
104 | [`pexpect`](https://pexpect.readthedocs.io/) |
105 | [`pickleshare`](https://github.com/pickleshare/pickleshare) |
106 | [`pluggy`](https://github.com/pytest-dev/pluggy) |
107 | [`prompt-toolkit`](https://github.com/prompt-toolkit/python-prompt-toolkit) |
108 | [`ptyprocess`](https://github.com/pexpect/ptyprocess) |
109 | [`py`](https://py.readthedocs.io/) |
110 | [`pycodestyle`](https://pycodestyle.readthedocs.io/) |
111 | [`pydocstyle`](https://github.com/PyCQA/pydocstyle/) |
112 | [`pyflakes`](https://github.com/PyCQA/pyflakes) |
113 | [`Pygments`](https://pygments.org/) |
114 | [`pymdown-extensions`](https://github.com/facelessuser/pymdown-extensions) |
115 | [`pyparsing`](https://github.com/pyparsing/pyparsing/) |
116 | [`pytest-forked`](https://github.com/pytest-dev/pytest-forked) |
117 | [`pytkdocs`](https://github.com/pawamoy/pytkdocs) |
118 | [`PyYAML`](https://pyyaml.org/) |
119 | [`regex`](https://bitbucket.org/mrabarnett/mrab-regex) |
120 | [`restructuredtext-lint`](https://github.com/twolfson/restructuredtext-lint) |
121 | [`rfc3986`](http://rfc3986.readthedocs.io) |
122 | [`six`](https://github.com/benjaminp/six) |
123 | [`smmap`](https://github.com/gitpython-developers/smmap) |
124 | [`sniffio`](https://github.com/python-trio/sniffio) |
125 | [`snowballstemmer`](https://github.com/snowballstem/snowball) |
126 | [`stevedore`](https://docs.openstack.org/stevedore/latest/) |
127 | [`termcolor`](http://pypi.python.org/pypi/termcolor) |
128 | [`testfixtures`](https://github.com/Simplistix/testfixtures) |
129 | [`tornado`](http://www.tornadoweb.org/) |
130 | [`tqdm`](https://github.com/tqdm/tqdm) |
131 | [`traitlets`](http://ipython.org) |
132 | [`typed-ast`](https://github.com/python/typed_ast) |
133 | [`typing-extensions`](https://github.com/python/typing/blob/master/typing_extensions/README.rst) |
134 | [`urllib3`](https://urllib3.readthedocs.io/) |
135 | [`wcwidth`](https://github.com/jquast/wcwidth) |
136 | [`zipp`](https://github.com/jaraco/zipp)
137 |
138 | **[More credits from the author](http://pawamoy.github.io/credits/)**
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2021, Timothée Mazzucotelli
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # If you have `direnv` loaded in your shell, and allow it in the repository,
2 | # the `make` command will point at the `scripts/make` shell script.
3 | # This Makefile is just here to allow auto-completion in the terminal.
4 |
5 | actions = \
6 | allrun \
7 | changelog \
8 | check \
9 | check-api \
10 | check-docs \
11 | check-quality \
12 | check-types \
13 | clean \
14 | coverage \
15 | docs \
16 | docs-deploy \
17 | format \
18 | help \
19 | multirun \
20 | release \
21 | run \
22 | setup \
23 | test \
24 | vscode
25 |
26 | .PHONY: $(actions)
27 | $(actions):
28 | @python scripts/make "$@"
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MkDocs Coverage Plugin
2 |
3 | [](https://github.com/pawamoy/mkdocs-coverage/actions?query=workflow%3Aci)
4 | [](https://pawamoy.github.io/mkdocs-coverage/)
5 | [](https://pypi.org/project/mkdocs-coverage/)
6 | [](https://gitpod.io/#https://github.com/pawamoy/mkdocs-coverage)
7 | [](https://app.gitter.im/#/room/#mkdocs-coverage:gitter.im)
8 |
9 | MkDocs plugin to integrate your coverage HTML report into your site.
10 |
11 | ## Installation
12 |
13 | With `pip`:
14 |
15 | ```bash
16 | pip install mkdocs-coverage
17 | ```
18 |
19 | With [`pipx`](https://github.com/pipxproject/pipx):
20 |
21 | ```bash
22 | python3.8 -m pip install --user pipx
23 | pipx install mkdocs-coverage
24 | ```
25 |
26 | ## Usage
27 |
28 | ```yaml
29 | # mkdocs.yml
30 | nav:
31 | - Coverage report: coverage.md
32 |
33 | plugins:
34 | - coverage:
35 | page_path: coverage # default
36 | html_report_dir: htmlcov # default
37 | ```
38 |
39 | The page path can be nested:
40 |
41 | ```yaml
42 | # mkdocs.yml
43 | nav:
44 | - Coverage report: dev/reports/coverage.md
45 |
46 | plugins:
47 | - coverage:
48 | page_path: dev/reports/coverage
49 | ```
50 |
51 | Now serve your documentation,
52 | and go to http://localhost:8000/coverage/
53 | to see your coverage report!
54 |
55 | 
56 | 
57 |
--------------------------------------------------------------------------------
/config/coverage.ini:
--------------------------------------------------------------------------------
1 | [coverage:run]
2 | branch = true
3 | parallel = true
4 | source =
5 | src/
6 | tests/
7 |
8 | [coverage:paths]
9 | equivalent =
10 | src/
11 | .venv/lib/*/site-packages/
12 | .venvs/*/lib/*/site-packages/
13 |
14 | [coverage:report]
15 | precision = 2
16 | omit =
17 | src/*/__init__.py
18 | tests/__init__.py
19 | exclude_lines =
20 | pragma: no cover
21 | if TYPE_CHECKING
22 |
23 | [coverage:json]
24 | output = htmlcov/coverage.json
25 |
--------------------------------------------------------------------------------
/config/git-changelog.toml:
--------------------------------------------------------------------------------
1 | bump = "auto"
2 | convention = "angular"
3 | in-place = true
4 | output = "CHANGELOG.md"
5 | parse-refs = false
6 | parse-trailers = true
7 | sections = ["build", "deps", "feat", "fix", "refactor"]
8 | template = "keepachangelog"
9 | versioning = "pep440"
10 |
--------------------------------------------------------------------------------
/config/mypy.ini:
--------------------------------------------------------------------------------
1 | [mypy]
2 | ignore_missing_imports = true
3 | exclude = tests/fixtures/
4 | warn_unused_ignores = true
5 | show_error_codes = true
6 |
--------------------------------------------------------------------------------
/config/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | python_files =
3 | test_*.py
4 | addopts =
5 | --cov
6 | --cov-config config/coverage.ini
7 | testpaths =
8 | tests
9 |
10 | # action:message_regex:warning_class:module_regex:line
11 | filterwarnings =
12 | error
13 | # TODO: remove once pytest-xdist 4 is released
14 | ignore:.*rsyncdir:DeprecationWarning:xdist
15 |
--------------------------------------------------------------------------------
/config/ruff.toml:
--------------------------------------------------------------------------------
1 | target-version = "py38"
2 | line-length = 120
3 |
4 | [lint]
5 | exclude = [
6 | "tests/fixtures/*.py",
7 | ]
8 | select = [
9 | "A", "ANN", "ARG",
10 | "B", "BLE",
11 | "C", "C4",
12 | "COM",
13 | "D", "DTZ",
14 | "E", "ERA", "EXE",
15 | "F", "FBT",
16 | "G",
17 | "I", "ICN", "INP", "ISC",
18 | "N",
19 | "PGH", "PIE", "PL", "PLC", "PLE", "PLR", "PLW", "PT", "PYI",
20 | "Q",
21 | "RUF", "RSE", "RET",
22 | "S", "SIM", "SLF",
23 | "T", "T10", "T20", "TCH", "TID", "TRY",
24 | "UP",
25 | "W",
26 | "YTT",
27 | ]
28 | ignore = [
29 | "A001", # Variable is shadowing a Python builtin
30 | "ANN101", # Missing type annotation for self
31 | "ANN102", # Missing type annotation for cls
32 | "ANN204", # Missing return type annotation for special method __str__
33 | "ANN401", # Dynamically typed expressions (typing.Any) are disallowed
34 | "ARG005", # Unused lambda argument
35 | "C901", # Too complex
36 | "D105", # Missing docstring in magic method
37 | "D417", # Missing argument description in the docstring
38 | "E501", # Line too long
39 | "ERA001", # Commented out code
40 | "G004", # Logging statement uses f-string
41 | "PLR0911", # Too many return statements
42 | "PLR0912", # Too many branches
43 | "PLR0913", # Too many arguments to function call
44 | "PLR0915", # Too many statements
45 | "SLF001", # Private member accessed
46 | "TRY003", # Avoid specifying long messages outside the exception class
47 | ]
48 |
49 | [lint.per-file-ignores]
50 | "src/*/cli.py" = [
51 | "T201", # Print statement
52 | ]
53 | "src/*/debug.py" = [
54 | "T201", # Print statement
55 | ]
56 | "scripts/*.py" = [
57 | "INP001", # File is part of an implicit namespace package
58 | "T201", # Print statement
59 | ]
60 | "tests/*.py" = [
61 | "ARG005", # Unused lambda argument
62 | "FBT001", # Boolean positional arg in function definition
63 | "PLR2004", # Magic value used in comparison
64 | "S101", # Use of assert detected
65 | ]
66 |
67 | [lint.flake8-quotes]
68 | docstring-quotes = "double"
69 |
70 | [lint.flake8-tidy-imports]
71 | ban-relative-imports = "all"
72 |
73 | [lint.isort]
74 | known-first-party = ["mkdocs_coverage"]
75 |
76 | [lint.pydocstyle]
77 | convention = "google"
78 |
79 | [format]
80 | exclude = [
81 | "tests/fixtures/*.py",
82 | ]
83 | docstring-code-format = true
84 | docstring-code-line-length = 80
85 |
--------------------------------------------------------------------------------
/config/vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "python (current file)",
6 | "type": "debugpy",
7 | "request": "launch",
8 | "program": "${file}",
9 | "console": "integratedTerminal",
10 | "justMyCode": false
11 | },
12 | {
13 | "name": "docs",
14 | "type": "debugpy",
15 | "request": "launch",
16 | "module": "mkdocs",
17 | "justMyCode": false,
18 | "args": [
19 | "serve",
20 | "-v"
21 | ]
22 | },
23 | {
24 | "name": "test",
25 | "type": "debugpy",
26 | "request": "launch",
27 | "module": "pytest",
28 | "justMyCode": false,
29 | "args": [
30 | "-c=config/pytest.ini",
31 | "-vvv",
32 | "--no-cov",
33 | "--dist=no",
34 | "tests",
35 | "-k=${input:tests_selection}"
36 | ]
37 | }
38 | ],
39 | "inputs": [
40 | {
41 | "id": "tests_selection",
42 | "type": "promptString",
43 | "description": "Tests selection",
44 | "default": ""
45 | }
46 | ]
47 | }
--------------------------------------------------------------------------------
/config/vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.watcherExclude": {
3 | "**/.venv*/**": true,
4 | "**/.venvs*/**": true,
5 | "**/venv*/**": true
6 | },
7 | "mypy-type-checker.args": [
8 | "--config-file=config/mypy.ini"
9 | ],
10 | "python.testing.unittestEnabled": false,
11 | "python.testing.pytestEnabled": true,
12 | "python.testing.pytestArgs": [
13 | "--config-file=config/pytest.ini"
14 | ],
15 | "ruff.enable": true,
16 | "ruff.format.args": [
17 | "--config=config/ruff.toml"
18 | ],
19 | "ruff.lint.args": [
20 | "--config=config/ruff.toml"
21 | ],
22 | "yaml.schemas": {
23 | "https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml"
24 | },
25 | "yaml.customTags": [
26 | "!ENV scalar",
27 | "!ENV sequence",
28 | "!relative scalar",
29 | "tag:yaml.org,2002:python/name:materialx.emoji.to_svg",
30 | "tag:yaml.org,2002:python/name:materialx.emoji.twemoji",
31 | "tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format"
32 | ]
33 | }
--------------------------------------------------------------------------------
/config/vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "changelog",
6 | "type": "process",
7 | "command": "scripts/make",
8 | "args": ["changelog"]
9 | },
10 | {
11 | "label": "check",
12 | "type": "process",
13 | "command": "scripts/make",
14 | "args": ["check"]
15 | },
16 | {
17 | "label": "check-quality",
18 | "type": "process",
19 | "command": "scripts/make",
20 | "args": ["check-quality"]
21 | },
22 | {
23 | "label": "check-types",
24 | "type": "process",
25 | "command": "scripts/make",
26 | "args": ["check-types"]
27 | },
28 | {
29 | "label": "check-docs",
30 | "type": "process",
31 | "command": "scripts/make",
32 | "args": ["check-docs"]
33 | },
34 | {
35 | "label": "check-api",
36 | "type": "process",
37 | "command": "scripts/make",
38 | "args": ["check-api"]
39 | },
40 | {
41 | "label": "clean",
42 | "type": "process",
43 | "command": "scripts/make",
44 | "args": ["clean"]
45 | },
46 | {
47 | "label": "docs",
48 | "type": "process",
49 | "command": "scripts/make",
50 | "args": ["docs"]
51 | },
52 | {
53 | "label": "docs-deploy",
54 | "type": "process",
55 | "command": "scripts/make",
56 | "args": ["docs-deploy"]
57 | },
58 | {
59 | "label": "format",
60 | "type": "process",
61 | "command": "scripts/make",
62 | "args": ["format"]
63 | },
64 | {
65 | "label": "release",
66 | "type": "process",
67 | "command": "scripts/make",
68 | "args": ["release", "${input:version}"]
69 | },
70 | {
71 | "label": "setup",
72 | "type": "process",
73 | "command": "scripts/make",
74 | "args": ["setup"]
75 | },
76 | {
77 | "label": "test",
78 | "type": "process",
79 | "command": "scripts/make",
80 | "args": ["test", "coverage"],
81 | "group": "test"
82 | },
83 | {
84 | "label": "vscode",
85 | "type": "process",
86 | "command": "scripts/make",
87 | "args": ["vscode"]
88 | }
89 | ],
90 | "inputs": [
91 | {
92 | "id": "version",
93 | "type": "promptString",
94 | "description": "Version"
95 | }
96 | ]
97 | }
--------------------------------------------------------------------------------
/devdeps.txt:
--------------------------------------------------------------------------------
1 | # dev
2 | editables>=0.5
3 |
4 | # maintenance
5 | build>=1.2
6 | git-changelog>=2.5
7 | twine>=5.1; python_version < '3.13'
8 |
9 | # ci
10 | duty>=1.4
11 | ruff>=0.4
12 | pytest>=8.2
13 | pytest-cov>=5.0
14 | pytest-randomly>=3.15
15 | pytest-xdist>=3.6
16 | mypy>=1.10
17 | types-markdown>=3.6
18 | types-pyyaml>=6.0
19 |
20 | # docs
21 | black>=24.4
22 | markdown-callouts>=0.4
23 | markdown-exec>=1.8
24 | mkdocs>=1.6
25 | mkdocs-coverage>=1.0
26 | mkdocs-gen-files>=0.5
27 | mkdocs-git-committers-plugin-2>=2.3
28 | mkdocs-literate-nav>=0.6
29 | mkdocs-material>=9.5
30 | mkdocs-minify-plugin>=0.8
31 | mkdocstrings[python]>=0.25
32 | tomli>=2.0; python_version < '3.11'
33 |
--------------------------------------------------------------------------------
/docs/.overrides/main.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block announce %}
4 |
5 | For updates follow @pawamoy on
6 |
7 |
8 | {% include ".icons/fontawesome/brands/mastodon.svg" %}
9 |
10 | Fosstodon
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | --8<-- "CHANGELOG.md"
2 |
--------------------------------------------------------------------------------
/docs/code_of_conduct.md:
--------------------------------------------------------------------------------
1 | --8<-- "CODE_OF_CONDUCT.md"
2 |
--------------------------------------------------------------------------------
/docs/contributing.md:
--------------------------------------------------------------------------------
1 | --8<-- "CONTRIBUTING.md"
2 |
--------------------------------------------------------------------------------
/docs/credits.md:
--------------------------------------------------------------------------------
1 | ---
2 | hide:
3 | - toc
4 | ---
5 |
6 |
7 | ```python exec="yes"
8 | --8<-- "scripts/gen_credits.py"
9 | ```
10 |
11 |
--------------------------------------------------------------------------------
/docs/css/material.css:
--------------------------------------------------------------------------------
1 | /* More space at the bottom of the page. */
2 | .md-main__inner {
3 | margin-bottom: 1.5rem;
4 | }
5 |
--------------------------------------------------------------------------------
/docs/css/mkdocstrings.css:
--------------------------------------------------------------------------------
1 | /* Indentation. */
2 | div.doc-contents:not(.first) {
3 | padding-left: 25px;
4 | border-left: .05rem solid var(--md-typeset-table-color);
5 | }
6 |
7 | /* Mark external links as such. */
8 | a.external::after,
9 | a.autorefs-external::after {
10 | /* https://primer.style/octicons/arrow-up-right-24 */
11 | mask-image: url('data:image/svg+xml,');
12 | -webkit-mask-image: url('data:image/svg+xml,');
13 | content: ' ';
14 |
15 | display: inline-block;
16 | vertical-align: middle;
17 | position: relative;
18 |
19 | height: 1em;
20 | width: 1em;
21 | background-color: currentColor;
22 | }
23 |
24 | a.external:hover::after,
25 | a.autorefs-external:hover::after {
26 | background-color: var(--md-accent-fg-color);
27 | }
--------------------------------------------------------------------------------
/docs/gen_credits.py:
--------------------------------------------------------------------------------
1 | """Generate the credits page."""
2 |
3 | import functools
4 | import re
5 | from itertools import chain
6 | from pathlib import Path
7 | from urllib.request import urlopen
8 |
9 | import mkdocs_gen_files
10 | import toml
11 | from jinja2 import StrictUndefined
12 | from jinja2.sandbox import SandboxedEnvironment
13 |
14 |
15 | def get_credits_data() -> dict:
16 | """Return data used to generate the credits file.
17 |
18 | Returns:
19 | Data required to render the credits template.
20 | """
21 | project_dir = Path(__file__).parent.parent
22 | metadata = toml.load(project_dir / "pyproject.toml")["project"]
23 | metadata_pdm = toml.load(project_dir / "pyproject.toml")["tool"]["pdm"]
24 | lock_data = toml.load(project_dir / "pdm.lock")
25 | project_name = metadata["name"]
26 |
27 | all_dependencies = chain(
28 | metadata.get("dependencies", []),
29 | chain(*metadata.get("optional-dependencies", {}).values()),
30 | chain(*metadata_pdm.get("dev-dependencies", {}).values()),
31 | )
32 | direct_dependencies = {re.sub(r"[^\w-].*$", "", dep) for dep in all_dependencies}
33 | direct_dependencies = {dep.lower() for dep in direct_dependencies}
34 | indirect_dependencies = {pkg["name"].lower() for pkg in lock_data["package"]}
35 | indirect_dependencies -= direct_dependencies
36 |
37 | return {
38 | "project_name": project_name,
39 | "direct_dependencies": sorted(direct_dependencies),
40 | "indirect_dependencies": sorted(indirect_dependencies),
41 | "more_credits": "http://pawamoy.github.io/credits/",
42 | }
43 |
44 |
45 | @functools.lru_cache(maxsize=None)
46 | def get_credits():
47 | """Return credits as Markdown.
48 |
49 | Returns:
50 | The credits page Markdown.
51 | """
52 | jinja_env = SandboxedEnvironment(undefined=StrictUndefined)
53 | commit = "c78c29caa345b6ace19494a98b1544253cbaf8c1"
54 | template_url = f"https://raw.githubusercontent.com/pawamoy/jinja-templates/{commit}/credits.md"
55 | template_data = get_credits_data()
56 | template_text = urlopen(template_url).read().decode("utf8") # noqa: S310
57 | return jinja_env.from_string(template_text).render(**template_data)
58 |
59 |
60 | with mkdocs_gen_files.open("credits.md", "w") as fd:
61 | fd.write(get_credits())
62 | mkdocs_gen_files.set_edit_path("credits.md", "gen_credits.py")
63 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | --8<-- "README.md"
2 |
--------------------------------------------------------------------------------
/docs/license.md:
--------------------------------------------------------------------------------
1 | # License
2 |
3 | ```
4 | --8<-- "LICENSE"
5 | ```
6 |
--------------------------------------------------------------------------------
/docs/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft-07/schema",
3 | "title": "MkDocs plugin to integrate your coverage HTML report into your site.",
4 | "oneOf": [
5 | {
6 | "markdownDescription": "https://pawamoy.github.io/mkdocs-coverage",
7 | "enum": [
8 | "coverage"
9 | ]
10 | },
11 | {
12 | "type": "object",
13 | "properties": {
14 | "coverage": {
15 | "markdownDescription": "https://pawamoy.github.io/mkdocs-coverage",
16 | "type": "object",
17 | "properties": {
18 | "page_name": {
19 | "title": "The name of the page (file) where the coverage report is integrated.",
20 | "type": "string",
21 | "default": "coverage"
22 | },
23 | "html_report_dir": {
24 | "title": "The path to the HTML report directory generated by coverage.",
25 | "type": "string",
26 | "default": "htmlcov",
27 | "format": "path"
28 | }
29 | },
30 | "additionalProperties": false
31 | }
32 | },
33 | "additionalProperties": false
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/duties.py:
--------------------------------------------------------------------------------
1 | """Development tasks."""
2 |
3 | from __future__ import annotations
4 |
5 | import os
6 | import sys
7 | from contextlib import contextmanager
8 | from importlib.metadata import version as pkgversion
9 | from pathlib import Path
10 | from typing import TYPE_CHECKING, Iterator
11 |
12 | from duty import duty, tools
13 |
14 | if TYPE_CHECKING:
15 | from duty.context import Context
16 |
17 |
18 | PY_SRC_PATHS = (Path(_) for _ in ("src", "tests", "duties.py", "scripts"))
19 | PY_SRC_LIST = tuple(str(_) for _ in PY_SRC_PATHS)
20 | PY_SRC = " ".join(PY_SRC_LIST)
21 | CI = os.environ.get("CI", "0") in {"1", "true", "yes", ""}
22 | WINDOWS = os.name == "nt"
23 | PTY = not WINDOWS and not CI
24 | MULTIRUN = os.environ.get("MULTIRUN", "0") == "1"
25 |
26 |
27 | def pyprefix(title: str) -> str: # noqa: D103
28 | if MULTIRUN:
29 | prefix = f"(python{sys.version_info.major}.{sys.version_info.minor})"
30 | return f"{prefix:14}{title}"
31 | return title
32 |
33 |
34 | @contextmanager
35 | def material_insiders() -> Iterator[bool]: # noqa: D103
36 | if "+insiders" in pkgversion("mkdocs-material"):
37 | os.environ["MATERIAL_INSIDERS"] = "true"
38 | try:
39 | yield True
40 | finally:
41 | os.environ.pop("MATERIAL_INSIDERS")
42 | else:
43 | yield False
44 |
45 |
46 | @duty
47 | def changelog(ctx: Context, bump: str = "") -> None:
48 | """Update the changelog in-place with latest commits.
49 |
50 | Parameters:
51 | bump: Bump option passed to git-changelog.
52 | """
53 | ctx.run(tools.git_changelog(bump=bump or None), title="Updating changelog")
54 |
55 |
56 | @duty(pre=["check_quality", "check_types", "check_docs", "check_dependencies", "check-api"])
57 | def check(ctx: Context) -> None: # noqa: ARG001
58 | """Check it all!"""
59 |
60 |
61 | @duty
62 | def check_quality(ctx: Context) -> None:
63 | """Check the code quality."""
64 | ctx.run(
65 | tools.ruff.check(*PY_SRC_LIST, config="config/ruff.toml"),
66 | title=pyprefix("Checking code quality"),
67 | )
68 |
69 |
70 | @duty
71 | def check_docs(ctx: Context) -> None:
72 | """Check if the documentation builds correctly."""
73 | Path("htmlcov").mkdir(parents=True, exist_ok=True)
74 | Path("htmlcov/index.html").touch(exist_ok=True)
75 | with material_insiders():
76 | ctx.run(
77 | tools.mkdocs.build(strict=True, verbose=True),
78 | title=pyprefix("Building documentation"),
79 | )
80 |
81 |
82 | @duty
83 | def check_types(ctx: Context) -> None:
84 | """Check that the code is correctly typed."""
85 | ctx.run(
86 | tools.mypy(*PY_SRC_LIST, config_file="config/mypy.ini"),
87 | title=pyprefix("Type-checking"),
88 | )
89 |
90 |
91 | @duty
92 | def check_api(ctx: Context, *cli_args: str) -> None:
93 | """Check for API breaking changes."""
94 | ctx.run(
95 | tools.griffe.check("mkdocs_coverage", search=["src"], color=True).add_args(*cli_args),
96 | title="Checking for API breaking changes",
97 | nofail=True,
98 | )
99 |
100 |
101 | @duty
102 | def docs(ctx: Context, *cli_args: str, host: str = "127.0.0.1", port: int = 8000) -> None:
103 | """Serve the documentation (localhost:8000).
104 |
105 | Parameters:
106 | host: The host to serve the docs from.
107 | port: The port to serve the docs on.
108 | """
109 | with material_insiders():
110 | ctx.run(
111 | tools.mkdocs.serve(dev_addr=f"{host}:{port}").add_args(*cli_args),
112 | title="Serving documentation",
113 | capture=False,
114 | )
115 |
116 |
117 | @duty
118 | def docs_deploy(ctx: Context) -> None:
119 | """Deploy the documentation to GitHub pages."""
120 | os.environ["DEPLOY"] = "true"
121 | with material_insiders() as insiders:
122 | if not insiders:
123 | ctx.run(lambda: False, title="Not deploying docs without Material for MkDocs Insiders!")
124 | ctx.run(tools.mkdocs.gh_deploy(), title="Deploying documentation")
125 |
126 |
127 | @duty
128 | def format(ctx: Context) -> None:
129 | """Run formatting tools on the code."""
130 | ctx.run(
131 | tools.ruff.check(*PY_SRC_LIST, config="config/ruff.toml", fix_only=True, exit_zero=True),
132 | title="Auto-fixing code",
133 | )
134 | ctx.run(tools.ruff.format(*PY_SRC_LIST, config="config/ruff.toml"), title="Formatting code")
135 |
136 |
137 | @duty
138 | def build(ctx: Context) -> None:
139 | """Build source and wheel distributions."""
140 | ctx.run(
141 | tools.build(),
142 | title="Building source and wheel distributions",
143 | pty=PTY,
144 | )
145 |
146 |
147 | @duty
148 | def publish(ctx: Context) -> None:
149 | """Publish source and wheel distributions to PyPI."""
150 | if not Path("dist").exists():
151 | ctx.run("false", title="No distribution files found")
152 | dists = [str(dist) for dist in Path("dist").iterdir()]
153 | ctx.run(
154 | tools.twine.upload(*dists, skip_existing=True),
155 | title="Publishing source and wheel distributions to PyPI",
156 | pty=PTY,
157 | )
158 |
159 |
160 | @duty(post=["build", "publish", "docs-deploy"])
161 | def release(ctx: Context, version: str = "") -> None:
162 | """Release a new Python package.
163 |
164 | Parameters:
165 | version: The new version number to use.
166 | """
167 | if not (version := (version or input("> Version to release: ")).strip()):
168 | ctx.run("false", title="A version must be provided")
169 | ctx.run("git add pyproject.toml CHANGELOG.md", title="Staging files", pty=PTY)
170 | ctx.run(["git", "commit", "-m", f"chore: Prepare release {version}"], title="Committing changes", pty=PTY)
171 | ctx.run(f"git tag {version}", title="Tagging commit", pty=PTY)
172 | ctx.run("git push", title="Pushing commits", pty=False)
173 | ctx.run("git push --tags", title="Pushing tags", pty=False)
174 |
175 |
176 | @duty(silent=True, aliases=["cov"])
177 | def coverage(ctx: Context) -> None:
178 | """Report coverage as text and HTML."""
179 | ctx.run(tools.coverage.combine(), nofail=True)
180 | ctx.run(tools.coverage.report(rcfile="config/coverage.ini"), capture=False)
181 | ctx.run(tools.coverage.html(rcfile="config/coverage.ini"))
182 |
183 |
184 | @duty
185 | def test(ctx: Context, *cli_args: str, match: str = "") -> None:
186 | """Run the test suite.
187 |
188 | Parameters:
189 | match: A pytest expression to filter selected tests.
190 | """
191 | py_version = f"{sys.version_info.major}{sys.version_info.minor}"
192 | os.environ["COVERAGE_FILE"] = f".coverage.{py_version}"
193 | ctx.run(
194 | tools.pytest(
195 | "tests",
196 | config_file="config/pytest.ini",
197 | select=match,
198 | color="yes",
199 | ).add_args("-n", "auto", *cli_args),
200 | title=pyprefix("Running tests"),
201 | )
202 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: "MkDocs Coverage Plugin"
2 | site_description: "MkDocs plugin to integrate your coverage HTML report into your site."
3 | site_url: "https://pawamoy.github.io/mkdocs-coverage"
4 | repo_url: "https://github.com/pawamoy/mkdocs-coverage"
5 | repo_name: "pawamoy/mkdocs-coverage"
6 | site_dir: "site"
7 | watch: [mkdocs.yml, README.md, CONTRIBUTING.md, CHANGELOG.md, src/mkdocs_coverage]
8 | copyright: Copyright © 2021 Timothée Mazzucotelli
9 | edit_uri: edit/main/docs/
10 |
11 | validation:
12 | omitted_files: warn
13 | absolute_links: warn
14 | unrecognized_links: warn
15 |
16 | nav:
17 | - Home:
18 | - Overview: index.md
19 | - Changelog: changelog.md
20 | - Credits: credits.md
21 | - License: license.md
22 | # defer to gen-files + literate-nav
23 | - API reference:
24 | - MkDocs Coverage Plugin: reference/
25 | - Development:
26 | - Contributing: contributing.md
27 | - Code of Conduct: code_of_conduct.md
28 | - Coverage report: coverage.md
29 | - Author's website: https://pawamoy.github.io/
30 |
31 | theme:
32 | name: material
33 | custom_dir: docs/.overrides
34 | icon:
35 | logo: material/currency-sign
36 | features:
37 | - announce.dismiss
38 | - content.action.edit
39 | - content.action.view
40 | - content.code.annotate
41 | - content.code.copy
42 | - content.tooltips
43 | - navigation.footer
44 | - navigation.indexes
45 | - navigation.sections
46 | - navigation.tabs
47 | - navigation.tabs.sticky
48 | - navigation.top
49 | - search.highlight
50 | - search.suggest
51 | - toc.follow
52 | palette:
53 | - media: "(prefers-color-scheme)"
54 | toggle:
55 | icon: material/brightness-auto
56 | name: Switch to light mode
57 | - media: "(prefers-color-scheme: light)"
58 | scheme: default
59 | primary: teal
60 | accent: purple
61 | toggle:
62 | icon: material/weather-sunny
63 | name: Switch to dark mode
64 | - media: "(prefers-color-scheme: dark)"
65 | scheme: slate
66 | primary: black
67 | accent: lime
68 | toggle:
69 | icon: material/weather-night
70 | name: Switch to system preference
71 |
72 | extra_css:
73 | - css/material.css
74 | - css/mkdocstrings.css
75 |
76 | markdown_extensions:
77 | - attr_list
78 | - admonition
79 | - callouts
80 | - footnotes
81 | - pymdownx.emoji:
82 | emoji_index: !!python/name:material.extensions.emoji.twemoji
83 | emoji_generator: !!python/name:material.extensions.emoji.to_svg
84 | - pymdownx.magiclink
85 | - pymdownx.snippets:
86 | base_path: [!relative $config_dir]
87 | check_paths: true
88 | - pymdownx.superfences
89 | - pymdownx.tabbed:
90 | alternate_style: true
91 | slugify: !!python/object/apply:pymdownx.slugs.slugify
92 | kwds:
93 | case: lower
94 | - pymdownx.tasklist:
95 | custom_checkbox: true
96 | - toc:
97 | permalink: "¤"
98 |
99 | plugins:
100 | - search
101 | - markdown-exec
102 | - gen-files:
103 | scripts:
104 | - scripts/gen_ref_nav.py
105 | - literate-nav:
106 | nav_file: SUMMARY.md
107 | - coverage
108 | - mkdocstrings:
109 | handlers:
110 | python:
111 | import:
112 | - https://docs.python.org/3/objects.inv
113 | paths: [src]
114 | options:
115 | docstring_options:
116 | ignore_init_summary: true
117 | docstring_section_style: list
118 | filters: ["!^_"]
119 | heading_level: 1
120 | inherited_members: true
121 | merge_init_into_class: true
122 | separate_signature: true
123 | show_root_heading: true
124 | show_root_full_path: false
125 | show_signature_annotations: true
126 | show_symbol_type_heading: true
127 | show_symbol_type_toc: true
128 | signature_crossrefs: true
129 | summary: true
130 | - git-committers:
131 | enabled: !ENV [DEPLOY, false]
132 | repository: pawamoy/mkdocs-coverage
133 | - minify:
134 | minify_html: !ENV [DEPLOY, false]
135 | - group:
136 | enabled: !ENV [MATERIAL_INSIDERS, false]
137 | plugins:
138 | - typeset
139 |
140 | extra:
141 | social:
142 | - icon: fontawesome/brands/github
143 | link: https://github.com/pawamoy
144 | - icon: fontawesome/brands/mastodon
145 | link: https://fosstodon.org/@pawamoy
146 | - icon: fontawesome/brands/twitter
147 | link: https://twitter.com/pawamoy
148 | - icon: fontawesome/brands/gitter
149 | link: https://gitter.im/mkdocs-coverage/community
150 | - icon: fontawesome/brands/python
151 | link: https://pypi.org/project/mkdocs-coverage/
152 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["pdm-backend"]
3 | build-backend = "pdm.backend"
4 |
5 | [project]
6 | name = "mkdocs-coverage"
7 | description = "MkDocs plugin to integrate your coverage HTML report into your site."
8 | authors = [{name = "Timothée Mazzucotelli", email = "dev@pawamoy.fr"}]
9 | license = {text = "ISC"}
10 | readme = "README.md"
11 | requires-python = ">=3.8"
12 | keywords = []
13 | dynamic = ["version"]
14 | classifiers = [
15 | "Development Status :: 4 - Beta",
16 | "Intended Audience :: Developers",
17 | "Programming Language :: Python",
18 | "Programming Language :: Python :: 3",
19 | "Programming Language :: Python :: 3 :: Only",
20 | "Programming Language :: Python :: 3.8",
21 | "Programming Language :: Python :: 3.9",
22 | "Programming Language :: Python :: 3.10",
23 | "Programming Language :: Python :: 3.11",
24 | "Programming Language :: Python :: 3.12",
25 | "Programming Language :: Python :: 3.13",
26 | "Topic :: Documentation",
27 | "Topic :: Software Development",
28 | "Topic :: Software Development :: Documentation",
29 | "Topic :: Utilities",
30 | "Typing :: Typed",
31 | ]
32 | dependencies = [
33 | "mkdocs>=1.6",
34 | ]
35 |
36 | [project.urls]
37 | Homepage = "https://pawamoy.github.io/mkdocs-coverage"
38 | Documentation = "https://pawamoy.github.io/mkdocs-coverage"
39 | Changelog = "https://pawamoy.github.io/mkdocs-coverage/changelog"
40 | Repository = "https://github.com/pawamoy/mkdocs-coverage"
41 | Issues = "https://github.com/pawamoy/mkdocs-coverage/issues"
42 | Discussions = "https://github.com/pawamoy/mkdocs-coverage/discussions"
43 | Gitter = "https://gitter.im/mkdocs-coverage/community"
44 | Funding = "https://github.com/sponsors/pawamoy"
45 |
46 | [project.entry-points."mkdocs.plugins"]
47 | coverage = "mkdocs_coverage.plugin:MkDocsCoveragePlugin"
48 |
49 | [tool.pdm]
50 | version = {source = "scm"}
51 |
52 | [tool.pdm.build]
53 | package-dir = "src"
54 | editable-backend = "editables"
55 | source-includes = ["share"]
56 |
57 | [tool.pdm.build.wheel-data]
58 | data = [
59 | {path = "share/**/*", relative-to = "."},
60 | ]
61 |
--------------------------------------------------------------------------------
/scripts/gen_credits.py:
--------------------------------------------------------------------------------
1 | """Script to generate the project's credits."""
2 |
3 | from __future__ import annotations
4 |
5 | import os
6 | import sys
7 | from collections import defaultdict
8 | from importlib.metadata import distributions
9 | from itertools import chain
10 | from pathlib import Path
11 | from textwrap import dedent
12 | from typing import Dict, Iterable, Union
13 |
14 | from jinja2 import StrictUndefined
15 | from jinja2.sandbox import SandboxedEnvironment
16 | from packaging.requirements import Requirement
17 |
18 | # TODO: Remove once support for Python 3.10 is dropped.
19 | if sys.version_info >= (3, 11):
20 | import tomllib
21 | else:
22 | import tomli as tomllib
23 |
24 | project_dir = Path(os.getenv("MKDOCS_CONFIG_DIR", "."))
25 | with project_dir.joinpath("pyproject.toml").open("rb") as pyproject_file:
26 | pyproject = tomllib.load(pyproject_file)
27 | project = pyproject["project"]
28 | project_name = project["name"]
29 | with project_dir.joinpath("devdeps.txt").open() as devdeps_file:
30 | devdeps = [line.strip() for line in devdeps_file if line.strip() and not line.strip().startswith(("-e", "#"))]
31 |
32 | PackageMetadata = Dict[str, Union[str, Iterable[str]]]
33 | Metadata = Dict[str, PackageMetadata]
34 |
35 |
36 | def _merge_fields(metadata: dict) -> PackageMetadata:
37 | fields = defaultdict(list)
38 | for header, value in metadata.items():
39 | fields[header.lower()].append(value.strip())
40 | return {
41 | field: value if len(value) > 1 or field in ("classifier", "requires-dist") else value[0]
42 | for field, value in fields.items()
43 | }
44 |
45 |
46 | def _norm_name(name: str) -> str:
47 | return name.replace("_", "-").replace(".", "-").lower()
48 |
49 |
50 | def _requirements(deps: list[str]) -> dict[str, Requirement]:
51 | return {_norm_name((req := Requirement(dep)).name): req for dep in deps}
52 |
53 |
54 | def _extra_marker(req: Requirement) -> str | None:
55 | if not req.marker:
56 | return None
57 | try:
58 | return next(marker[2].value for marker in req.marker._markers if getattr(marker[0], "value", None) == "extra")
59 | except StopIteration:
60 | return None
61 |
62 |
63 | def _get_metadata() -> Metadata:
64 | metadata = {}
65 | for pkg in distributions():
66 | name = _norm_name(pkg.name) # type: ignore[attr-defined,unused-ignore]
67 | metadata[name] = _merge_fields(pkg.metadata) # type: ignore[arg-type]
68 | metadata[name]["spec"] = set()
69 | metadata[name]["extras"] = set()
70 | metadata[name].setdefault("summary", "")
71 | _set_license(metadata[name])
72 | return metadata
73 |
74 |
75 | def _set_license(metadata: PackageMetadata) -> None:
76 | license_field = metadata.get("license-expression", metadata.get("license", ""))
77 | license_name = license_field if isinstance(license_field, str) else " + ".join(license_field)
78 | check_classifiers = license_name in ("UNKNOWN", "Dual License", "") or license_name.count("\n")
79 | if check_classifiers:
80 | license_names = []
81 | for classifier in metadata["classifier"]:
82 | if classifier.startswith("License ::"):
83 | license_names.append(classifier.rsplit("::", 1)[1].strip())
84 | license_name = " + ".join(license_names)
85 | metadata["license"] = license_name or "?"
86 |
87 |
88 | def _get_deps(base_deps: dict[str, Requirement], metadata: Metadata) -> Metadata:
89 | deps = {}
90 | for dep_name, dep_req in base_deps.items():
91 | if dep_name not in metadata or dep_name == "mkdocs-coverage":
92 | continue
93 | metadata[dep_name]["spec"] |= {str(spec) for spec in dep_req.specifier} # type: ignore[operator]
94 | metadata[dep_name]["extras"] |= dep_req.extras # type: ignore[operator]
95 | deps[dep_name] = metadata[dep_name]
96 |
97 | again = True
98 | while again:
99 | again = False
100 | for pkg_name in metadata:
101 | if pkg_name in deps:
102 | for pkg_dependency in metadata[pkg_name].get("requires-dist", []):
103 | requirement = Requirement(pkg_dependency)
104 | dep_name = _norm_name(requirement.name)
105 | extra_marker = _extra_marker(requirement)
106 | if (
107 | dep_name in metadata
108 | and dep_name not in deps
109 | and dep_name != project["name"]
110 | and (not extra_marker or extra_marker in deps[pkg_name]["extras"])
111 | ):
112 | metadata[dep_name]["spec"] |= {str(spec) for spec in requirement.specifier} # type: ignore[operator]
113 | deps[dep_name] = metadata[dep_name]
114 | again = True
115 |
116 | return deps
117 |
118 |
119 | def _render_credits() -> str:
120 | metadata = _get_metadata()
121 | dev_dependencies = _get_deps(_requirements(devdeps), metadata)
122 | prod_dependencies = _get_deps(
123 | _requirements(
124 | chain( # type: ignore[arg-type]
125 | project.get("dependencies", []),
126 | chain(*project.get("optional-dependencies", {}).values()),
127 | ),
128 | ),
129 | metadata,
130 | )
131 |
132 | template_data = {
133 | "project_name": project_name,
134 | "prod_dependencies": sorted(prod_dependencies.values(), key=lambda dep: str(dep["name"]).lower()),
135 | "dev_dependencies": sorted(dev_dependencies.values(), key=lambda dep: str(dep["name"]).lower()),
136 | "more_credits": "http://pawamoy.github.io/credits/",
137 | }
138 | template_text = dedent(
139 | """
140 | # Credits
141 |
142 | These projects were used to build *{{ project_name }}*. **Thank you!**
143 |
144 | [Python](https://www.python.org/) |
145 | [uv](https://github.com/astral-sh/uv) |
146 | [copier-uv](https://github.com/pawamoy/copier-uv)
147 |
148 | {% macro dep_line(dep) -%}
149 | [{{ dep.name }}](https://pypi.org/project/{{ dep.name }}/) | {{ dep.summary }} | {{ ("`" ~ dep.spec|sort(reverse=True)|join(", ") ~ "`") if dep.spec else "" }} | `{{ dep.version }}` | {{ dep.license }}
150 | {%- endmacro %}
151 |
152 | {% if prod_dependencies -%}
153 | ### Runtime dependencies
154 |
155 | Project | Summary | Version (accepted) | Version (last resolved) | License
156 | ------- | ------- | ------------------ | ----------------------- | -------
157 | {% for dep in prod_dependencies -%}
158 | {{ dep_line(dep) }}
159 | {% endfor %}
160 |
161 | {% endif -%}
162 | {% if dev_dependencies -%}
163 | ### Development dependencies
164 |
165 | Project | Summary | Version (accepted) | Version (last resolved) | License
166 | ------- | ------- | ------------------ | ----------------------- | -------
167 | {% for dep in dev_dependencies -%}
168 | {{ dep_line(dep) }}
169 | {% endfor %}
170 |
171 | {% endif -%}
172 | {% if more_credits %}**[More credits from the author]({{ more_credits }})**{% endif %}
173 | """,
174 | )
175 | jinja_env = SandboxedEnvironment(undefined=StrictUndefined)
176 | return jinja_env.from_string(template_text).render(**template_data)
177 |
178 |
179 | print(_render_credits())
180 |
--------------------------------------------------------------------------------
/scripts/gen_ref_nav.py:
--------------------------------------------------------------------------------
1 | """Generate the code reference pages and navigation."""
2 |
3 | from pathlib import Path
4 |
5 | import mkdocs_gen_files
6 |
7 | nav = mkdocs_gen_files.Nav()
8 | mod_symbol = '
'
9 |
10 | root = Path(__file__).parent.parent
11 | src = root / "src"
12 |
13 | for path in sorted(src.rglob("*.py")):
14 | module_path = path.relative_to(src).with_suffix("")
15 | doc_path = path.relative_to(src).with_suffix(".md")
16 | full_doc_path = Path("reference", doc_path)
17 |
18 | parts = tuple(module_path.parts)
19 |
20 | if parts[-1] == "__init__":
21 | parts = parts[:-1]
22 | doc_path = doc_path.with_name("index.md")
23 | full_doc_path = full_doc_path.with_name("index.md")
24 | elif parts[-1].startswith("_"):
25 | continue
26 |
27 | nav_parts = [f"{mod_symbol} {part}" for part in parts]
28 | nav[tuple(nav_parts)] = doc_path.as_posix()
29 |
30 | with mkdocs_gen_files.open(full_doc_path, "w") as fd:
31 | ident = ".".join(parts)
32 | fd.write(f"---\ntitle: {ident}\n---\n\n::: {ident}")
33 |
34 | mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path.relative_to(root))
35 |
36 | with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
37 | nav_file.writelines(nav.build_literate_nav())
38 |
--------------------------------------------------------------------------------
/scripts/make:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """Management commands."""
3 |
4 | import os
5 | import shutil
6 | import subprocess
7 | import sys
8 | from contextlib import contextmanager
9 | from pathlib import Path
10 | from typing import Any, Iterator
11 |
12 | PYTHON_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.8 3.9 3.10 3.11 3.12 3.13").split()
13 |
14 | exe = ""
15 | prefix = ""
16 |
17 |
18 | def shell(cmd: str) -> None:
19 | """Run a shell command."""
20 | subprocess.run(cmd, shell=True, check=True) # noqa: S602
21 |
22 |
23 | @contextmanager
24 | def environ(**kwargs: str) -> Iterator[None]:
25 | """Temporarily set environment variables."""
26 | original = dict(os.environ)
27 | os.environ.update(kwargs)
28 | try:
29 | yield
30 | finally:
31 | os.environ.clear()
32 | os.environ.update(original)
33 |
34 |
35 | def uv_install() -> None:
36 | """Install dependencies using uv."""
37 | uv_opts = ""
38 | if "UV_RESOLUTION" in os.environ:
39 | uv_opts = f"--resolution={os.getenv('UV_RESOLUTION')}"
40 | cmd = f"uv pip compile {uv_opts} pyproject.toml devdeps.txt | uv pip install -r -"
41 | shell(cmd)
42 | if "CI" not in os.environ:
43 | shell("uv pip install --no-deps -e .")
44 | else:
45 | shell("uv pip install --no-deps .")
46 |
47 |
48 | def setup() -> None:
49 | """Setup the project."""
50 | if not shutil.which("uv"):
51 | raise ValueError("make: setup: uv must be installed, see https://github.com/astral-sh/uv")
52 |
53 | print("Installing dependencies (default environment)") # noqa: T201
54 | default_venv = Path(".venv")
55 | if not default_venv.exists():
56 | shell("uv venv --python python")
57 | uv_install()
58 |
59 | if PYTHON_VERSIONS:
60 | for version in PYTHON_VERSIONS:
61 | print(f"\nInstalling dependencies (python{version})") # noqa: T201
62 | venv_path = Path(f".venvs/{version}")
63 | if not venv_path.exists():
64 | shell(f"uv venv --python {version} {venv_path}")
65 | with environ(VIRTUAL_ENV=str(venv_path.resolve())):
66 | uv_install()
67 |
68 |
69 | def activate(path: str) -> None:
70 | """Activate a virtual environment."""
71 | global exe, prefix # noqa: PLW0603
72 |
73 | if (bin := Path(path, "bin")).exists():
74 | activate_script = bin / "activate_this.py"
75 | elif (scripts := Path(path, "Scripts")).exists():
76 | activate_script = scripts / "activate_this.py"
77 | exe = ".exe"
78 | prefix = f"{path}/Scripts/"
79 | else:
80 | raise ValueError(f"make: activate: Cannot find activation script in {path}")
81 |
82 | if not activate_script.exists():
83 | raise ValueError(f"make: activate: Cannot find activation script in {path}")
84 |
85 | exec(activate_script.read_text(), {"__file__": str(activate_script)}) # noqa: S102
86 |
87 |
88 | def run(version: str, cmd: str, *args: str, **kwargs: Any) -> None:
89 | """Run a command in a virtual environment."""
90 | kwargs = {"check": True, **kwargs}
91 | if version == "default":
92 | activate(".venv")
93 | subprocess.run([f"{prefix}{cmd}{exe}", *args], **kwargs) # noqa: S603, PLW1510
94 | else:
95 | activate(f".venvs/{version}")
96 | os.environ["MULTIRUN"] = "1"
97 | subprocess.run([f"{prefix}{cmd}{exe}", *args], **kwargs) # noqa: S603, PLW1510
98 |
99 |
100 | def multirun(cmd: str, *args: str, **kwargs: Any) -> None:
101 | """Run a command for all configured Python versions."""
102 | if PYTHON_VERSIONS:
103 | for version in PYTHON_VERSIONS:
104 | run(version, cmd, *args, **kwargs)
105 | else:
106 | run("default", cmd, *args, **kwargs)
107 |
108 |
109 | def allrun(cmd: str, *args: str, **kwargs: Any) -> None:
110 | """Run a command in all virtual environments."""
111 | run("default", cmd, *args, **kwargs)
112 | if PYTHON_VERSIONS:
113 | multirun(cmd, *args, **kwargs)
114 |
115 |
116 | def clean() -> None:
117 | """Delete build artifacts and cache files."""
118 | paths_to_clean = ["build", "dist", "htmlcov", "site", ".coverage*", ".pdm-build"]
119 | for path in paths_to_clean:
120 | shell(f"rm -rf {path}")
121 |
122 | cache_dirs = [".cache", ".pytest_cache", ".mypy_cache", ".ruff_cache", "__pycache__"]
123 | for dirpath in Path(".").rglob("*"):
124 | if any(dirpath.match(pattern) for pattern in cache_dirs) and not (dirpath.match(".venv") or dirpath.match(".venvs")):
125 | shutil.rmtree(path, ignore_errors=True)
126 |
127 |
128 | def vscode() -> None:
129 | """Configure VSCode to work on this project."""
130 | Path(".vscode").mkdir(parents=True, exist_ok=True)
131 | shell("cp -v config/vscode/* .vscode")
132 |
133 |
134 | def main() -> int:
135 | """Main entry point."""
136 | args = list(sys.argv[1:])
137 | if not args or args[0] == "help":
138 | if len(args) > 1:
139 | run("default", "duty", "--help", args[1])
140 | else:
141 | print("Available commands") # noqa: T201
142 | print(" help Print this help. Add task name to print help.") # noqa: T201
143 | print(" setup Setup all virtual environments (install dependencies).") # noqa: T201
144 | print(" run Run a command in the default virtual environment.") # noqa: T201
145 | print(" multirun Run a command for all configured Python versions.") # noqa: T201
146 | print(" allrun Run a command in all virtual environments.") # noqa: T201
147 | print(" 3.x Run a command in the virtual environment for Python 3.x.") # noqa: T201
148 | print(" clean Delete build artifacts and cache files.") # noqa: T201
149 | print(" vscode Configure VSCode to work on this project.") # noqa: T201
150 | try:
151 | run("default", "python", "-V", capture_output=True)
152 | except (subprocess.CalledProcessError, ValueError):
153 | pass
154 | else:
155 | print("\nAvailable tasks") # noqa: T201
156 | run("default", "duty", "--list")
157 | return 0
158 |
159 | while args:
160 | cmd = args.pop(0)
161 |
162 | if cmd == "run":
163 | run("default", *args)
164 | return 0
165 |
166 | if cmd == "multirun":
167 | multirun(*args)
168 | return 0
169 |
170 | if cmd == "allrun":
171 | allrun(*args)
172 | return 0
173 |
174 | if cmd.startswith("3."):
175 | run(cmd, *args)
176 | return 0
177 |
178 | opts = []
179 | while args and (args[0].startswith("-") or "=" in args[0]):
180 | opts.append(args.pop(0))
181 |
182 | if cmd == "clean":
183 | clean()
184 | elif cmd == "setup":
185 | setup()
186 | elif cmd == "vscode":
187 | vscode()
188 | elif cmd == "check":
189 | multirun("duty", "check-quality", "check-types", "check-docs")
190 | run("default", "duty", "check-api")
191 | elif cmd in {"check-quality", "check-docs", "check-types", "test"}:
192 | multirun("duty", cmd, *opts)
193 | else:
194 | run("default", "duty", cmd, *opts)
195 |
196 | return 0
197 |
198 |
199 | if __name__ == "__main__":
200 | try:
201 | sys.exit(main())
202 | except Exception: # noqa: BLE001
203 | sys.exit(1)
204 |
--------------------------------------------------------------------------------
/src/mkdocs_coverage/__init__.py:
--------------------------------------------------------------------------------
1 | """MkDocs Coverage Plugin package.
2 |
3 | MkDocs plugin to integrate your coverage HTML report into your site.
4 | """
5 |
6 | from __future__ import annotations
7 |
8 | __all__: list[str] = []
9 |
--------------------------------------------------------------------------------
/src/mkdocs_coverage/debug.py:
--------------------------------------------------------------------------------
1 | """Debugging utilities."""
2 |
3 | from __future__ import annotations
4 |
5 | import os
6 | import platform
7 | import sys
8 | from dataclasses import dataclass
9 | from importlib import metadata
10 |
11 |
12 | @dataclass
13 | class Variable:
14 | """Dataclass describing an environment variable."""
15 |
16 | name: str
17 | """Variable name."""
18 | value: str
19 | """Variable value."""
20 |
21 |
22 | @dataclass
23 | class Package:
24 | """Dataclass describing a Python package."""
25 |
26 | name: str
27 | """Package name."""
28 | version: str
29 | """Package version."""
30 |
31 |
32 | @dataclass
33 | class Environment:
34 | """Dataclass to store environment information."""
35 |
36 | interpreter_name: str
37 | """Python interpreter name."""
38 | interpreter_version: str
39 | """Python interpreter version."""
40 | interpreter_path: str
41 | """Path to Python executable."""
42 | platform: str
43 | """Operating System."""
44 | packages: list[Package]
45 | """Installed packages."""
46 | variables: list[Variable]
47 | """Environment variables."""
48 |
49 |
50 | def _interpreter_name_version() -> tuple[str, str]:
51 | if hasattr(sys, "implementation"):
52 | impl = sys.implementation.version
53 | version = f"{impl.major}.{impl.minor}.{impl.micro}"
54 | kind = impl.releaselevel
55 | if kind != "final":
56 | version += kind[0] + str(impl.serial)
57 | return sys.implementation.name, version
58 | return "", "0.0.0"
59 |
60 |
61 | def get_version(dist: str = "mkdocs-coverage") -> str:
62 | """Get version of the given distribution.
63 |
64 | Parameters:
65 | dist: A distribution name.
66 |
67 | Returns:
68 | A version number.
69 | """
70 | try:
71 | return metadata.version(dist)
72 | except metadata.PackageNotFoundError:
73 | return "0.0.0"
74 |
75 |
76 | def get_debug_info() -> Environment:
77 | """Get debug/environment information.
78 |
79 | Returns:
80 | Environment information.
81 | """
82 | py_name, py_version = _interpreter_name_version()
83 | packages = ["mkdocs-coverage"]
84 | variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("MKDOCS_COVERAGE")]]
85 | return Environment(
86 | interpreter_name=py_name,
87 | interpreter_version=py_version,
88 | interpreter_path=sys.executable,
89 | platform=platform.platform(),
90 | variables=[Variable(var, val) for var in variables if (val := os.getenv(var))],
91 | packages=[Package(pkg, get_version(pkg)) for pkg in packages],
92 | )
93 |
94 |
95 | def print_debug_info() -> None:
96 | """Print debug/environment information."""
97 | info = get_debug_info()
98 | print(f"- __System__: {info.platform}")
99 | print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})")
100 | print("- __Environment variables__:")
101 | for var in info.variables:
102 | print(f" - `{var.name}`: `{var.value}`")
103 | print("- __Installed packages__:")
104 | for pkg in info.packages:
105 | print(f" - `{pkg.name}` v{pkg.version}")
106 |
107 |
108 | if __name__ == "__main__":
109 | print_debug_info()
110 |
--------------------------------------------------------------------------------
/src/mkdocs_coverage/loggers.py:
--------------------------------------------------------------------------------
1 | """Logging functions."""
2 |
3 | from __future__ import annotations
4 |
5 | try:
6 | from mkdocs.plugin import get_plugin_logger
7 | except ImportError:
8 | # TODO: remove once support for MkDocs <1.5 is dropped
9 | import logging
10 | from typing import Any, MutableMapping
11 |
12 | class PrefixedLogger(logging.LoggerAdapter): # noqa: D101
13 | def __init__(self, prefix: str, logger: logging.Logger) -> None: # noqa: D107
14 | super().__init__(logger, {})
15 | self.prefix = prefix
16 |
17 | def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, Any]: # noqa: D102
18 | return f"{self.prefix}: {msg}", kwargs
19 |
20 | def get_plugin_logger(name: str) -> PrefixedLogger: # noqa: D103
21 | logger = logging.getLogger(f"mkdocs.plugins.{name}")
22 | return PrefixedLogger(name.split(".", 1)[0], logger)
23 |
--------------------------------------------------------------------------------
/src/mkdocs_coverage/plugin.py:
--------------------------------------------------------------------------------
1 | """This module contains the `mkdocs_coverage` plugin."""
2 |
3 | from __future__ import annotations
4 |
5 | import re
6 | import shutil
7 | import textwrap
8 | import warnings
9 | from pathlib import Path
10 | from typing import TYPE_CHECKING, Any
11 |
12 | from mkdocs.config.base import Config
13 | from mkdocs.config.config_options import Optional
14 | from mkdocs.config.config_options import Type as MkType
15 | from mkdocs.plugins import BasePlugin
16 | from mkdocs.structure.files import File, Files
17 |
18 | from mkdocs_coverage.loggers import get_plugin_logger
19 |
20 | if TYPE_CHECKING:
21 | from mkdocs.config.defaults import MkDocsConfig
22 |
23 | log = get_plugin_logger(__name__)
24 |
25 |
26 | class MkDocsCoverageConfig(Config):
27 | """Configuration options for the plugin."""
28 |
29 | page_name = Optional(MkType(str, default=None))
30 | page_path = MkType(str, default="coverage")
31 | html_report_dir = MkType(str, default="htmlcov")
32 |
33 |
34 | class MkDocsCoveragePlugin(BasePlugin[MkDocsCoverageConfig]):
35 | """The MkDocs plugin to integrate the coverage HTML report in the site."""
36 |
37 | def __init__(self) -> None:
38 | """Initialize the plugin."""
39 | super().__init__()
40 | self.page_path: str = ""
41 |
42 | def on_files(self, files: Files, config: MkDocsConfig, **kwargs: Any) -> Files: # noqa: ARG002
43 | """Add the coverage page to the navigation.
44 |
45 | Hook for the [`on_files` event](https://www.mkdocs.org/user-guide/plugins/#on_files).
46 | This hook is used to add the coverage page to the navigation, using a temporary file.
47 |
48 | Arguments:
49 | files: The files collection.
50 | config: The MkDocs config object.
51 | **kwargs: Additional arguments passed by MkDocs.
52 |
53 | Returns:
54 | The modified files collection.
55 | """
56 | page_name = self.config.page_name
57 | page_path: str
58 | if page_name is not None:
59 | warnings.warn(
60 | "The 'page_name' configuration option is deprecated and will be removed in a future release. "
61 | "Use the 'page_path' configuration option instead.",
62 | DeprecationWarning,
63 | stacklevel=1,
64 | )
65 | page_path = page_name
66 | else:
67 | page_path = self.config.page_path
68 | self.page_path = page_path
69 | covindex = "covindex.html" if config.use_directory_urls else f"{page_path}/covindex.html"
70 |
71 | style = textwrap.dedent(
72 | """
73 |
78 | """,
79 | )
80 |
81 | iframe = textwrap.dedent(
82 | f"""
83 |
91 | """,
92 | )
93 |
94 | script = textwrap.dedent(
95 | """
96 |
107 |
108 | """,
109 | )
110 | page_contents = style + iframe + script
111 | files.append(
112 | File.generated(
113 | config=config,
114 | src_uri=page_path + ".md",
115 | content=page_contents,
116 | ),
117 | )
118 | return files
119 |
120 | def on_post_build(self, config: MkDocsConfig, **kwargs: Any) -> None: # noqa: ARG002
121 | """Copy the coverage HTML report into the site directory.
122 |
123 | Hook for the [`on_post_build` event](https://www.mkdocs.org/user-guide/plugins/#on_post_build).
124 |
125 | Rename `index.html` into `covindex.html`.
126 | Replace every occurrence of `index.html` by `covindex.html` in the HTML files.
127 |
128 | Arguments:
129 | config: The MkDocs config object.
130 | **kwargs: Additional arguments passed by MkDocs.
131 | """
132 | site_dir = Path(config.site_dir)
133 | coverage_dir = site_dir / self.page_path
134 | tmp_index = site_dir / ".coverage-tmp.html"
135 |
136 | if config.use_directory_urls:
137 | shutil.move(str(coverage_dir / "index.html"), tmp_index)
138 | else:
139 | shutil.move(str(coverage_dir.with_suffix(".html")), tmp_index)
140 |
141 | shutil.rmtree(str(coverage_dir), ignore_errors=True)
142 | try:
143 | shutil.copytree(self.config.html_report_dir, str(coverage_dir))
144 | except FileNotFoundError:
145 | log.warning(f"No such HTML report directory: {self.config.html_report_dir}")
146 | return
147 |
148 | shutil.move(str(coverage_dir / "index.html"), coverage_dir / "covindex.html")
149 |
150 | if config.use_directory_urls:
151 | shutil.move(str(tmp_index), coverage_dir / "index.html")
152 | else:
153 | shutil.move(str(tmp_index), coverage_dir.with_suffix(".html"))
154 |
155 | for html_file in coverage_dir.iterdir():
156 | if html_file.suffix == ".html" and html_file.name != "index.html":
157 | html_file.write_text(re.sub(r'href="index\.html"', 'href="covindex.html"', html_file.read_text()))
158 |
--------------------------------------------------------------------------------
/src/mkdocs_coverage/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pawamoy/mkdocs-coverage/3c6ef1d7614336e83674a50c1f09f62524e28dcd/src/mkdocs_coverage/py.typed
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | """Tests suite for `mkdocs_coverage`."""
2 |
3 | from pathlib import Path
4 |
5 | TESTS_DIR = Path(__file__).parent
6 | TMP_DIR = TESTS_DIR / "tmp"
7 | FIXTURES_DIR = TESTS_DIR / "fixtures"
8 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | """Configuration for the pytest test suite."""
2 |
--------------------------------------------------------------------------------
/tests/test_plugin.py:
--------------------------------------------------------------------------------
1 | """Tests for the plugin module."""
2 |
3 | import re
4 | from pathlib import Path
5 |
6 | from mkdocs.commands.build import build
7 | from mkdocs.config.base import load_config
8 |
9 |
10 | def test_plugin() -> None:
11 | """Build our own documentation."""
12 | config = load_config()
13 | config["plugins"].run_event("startup", command="build", dirty=False)
14 | try:
15 | build(config)
16 | finally:
17 | config["plugins"].run_event("shutdown")
18 | site_coverage_dir = Path(config["site_dir"]) / "coverage"
19 | for html_file in site_coverage_dir.iterdir():
20 | if html_file.suffix == ".html" and html_file.name != "index.html" and "test" not in html_file.name:
21 | text = html_file.read_text()
22 | assert not re.search("covcovindex", text)
23 | assert not re.search('href="index.html"', text)
24 |
--------------------------------------------------------------------------------