diff --git a/.copier-answers.yml b/.copier-answers.yml index fa6c4f9c..6c5d650e 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.8.4 +_commit: 1.11.1 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli @@ -8,12 +8,8 @@ copyright_date: '2019' copyright_holder: Timothée Mazzucotelli copyright_holder_email: dev@pawamoy.fr copyright_license: ISC -insiders: true -insiders_email: insiders@pawamoy.fr -insiders_repository_name: mkdocstrings project_description: Automatic documentation from sources, for MkDocs. project_name: mkdocstrings -public_release: true python_package_command_line_name: '' python_package_distribution_name: mkdocstrings python_package_import_name: mkdocstrings diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..6f0f2faf --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +### For reviewers + + +- [ ] I did not use AI +- [ ] I used AI and thoroughly reviewed every code/docs change + +### Description of the change + + +### Relevant resources + + +- +- +- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dd6da41..54dd741c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,10 +2,17 @@ name: ci on: push: + branches: + - main + - test-me-* pull_request: branches: - main +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + defaults: run: shell: bash @@ -15,13 +22,30 @@ env: LC_ALL: en_US.utf-8 PYTHONIOENCODING: UTF-8 PYTHONPATH: docs + PYTHONWARNDEFAULTENCODING: "1" PYTHON_VERSIONS: "" jobs: quality: + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + python-version: + - "3.10" + - "3.14" + include: + - os: ubuntu-latest + python-version: "3.11" + - os: ubuntu-latest + python-version: "3.12" + - os: ubuntu-latest + python-version: "3.13" - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - name: Checkout @@ -31,9 +55,9 @@ jobs: fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: ${{ matrix.python-version }} - name: Setup uv uses: astral-sh/setup-uv@v5 @@ -58,39 +82,15 @@ jobs: - name: Store objects inventory for tests uses: actions/upload-artifact@v4 + if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' }} with: name: objects.inv path: site/objects.inv - exclude-test-jobs: - runs-on: ubuntu-latest - outputs: - jobs: ${{ steps.exclude-jobs.outputs.jobs }} - steps: - - id: exclude-jobs - run: | - if ${{ github.repository_owner == 'pawamoy-insiders' }}; then - echo 'jobs=[ - {"os": "macos-latest"}, - {"os": "windows-latest"}, - {"python-version": "3.10"}, - {"python-version": "3.11"}, - {"python-version": "3.12"}, - {"python-version": "3.13"}, - {"python-version": "3.14"} - ]' | tr -d '[:space:]' >> $GITHUB_OUTPUT - else - echo 'jobs=[ - {"os": "macos-latest", "resolution": "lowest-direct"}, - {"os": "windows-latest", "resolution": "lowest-direct"} - ]' | tr -d '[:space:]' >> $GITHUB_OUTPUT - fi - tests: needs: - quality - - exclude-test-jobs strategy: matrix: os: @@ -98,18 +98,22 @@ jobs: - macos-latest - windows-latest python-version: - - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" + - "3.15" resolution: - highest - lowest-direct - exclude: ${{ fromJSON(needs.exclude-test-jobs.outputs.jobs) }} + exclude: + - os: macos-latest + resolution: lowest-direct + - os: windows-latest + resolution: lowest-direct runs-on: ${{ matrix.os }} - continue-on-error: ${{ matrix.python-version == '3.14' }} + continue-on-error: true steps: - name: Checkout @@ -119,7 +123,7 @@ jobs: fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} allow-prereleases: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73347dad..1c7cda36 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,30 +15,15 @@ jobs: fetch-depth: 0 fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: "3.13" - name: Setup uv uses: astral-sh/setup-uv@v5 - - name: Build dists - if: github.repository_owner == 'pawamoy-insiders' - run: uv tool run --from build pyproject-build - - name: Upload dists artifact - uses: actions/upload-artifact@v4 - if: github.repository_owner == 'pawamoy-insiders' - with: - name: mkdocstrings-insiders - path: ./dist/* - name: Prepare release notes - if: github.repository_owner != 'pawamoy-insiders' run: uv tool run git-changelog --release-notes > release-notes.md - - name: Create release with assets - uses: softprops/action-gh-release@v2 - if: github.repository_owner == 'pawamoy-insiders' - with: - files: ./dist/* - name: Create release uses: softprops/action-gh-release@v2 - if: github.repository_owner != 'pawamoy-insiders' with: body_path: release-notes.md + diff --git a/.github/workflows/sponsors.yml b/.github/workflows/sponsors.yml new file mode 100644 index 00000000..8dd9150f --- /dev/null +++ b/.github/workflows/sponsors.yml @@ -0,0 +1,26 @@ +name: Update sponsors + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-readme: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Update README and create PR + uses: pawamoy/readme-insert@main + with: + markup-url: https://pawamoy.github.io/sponsors.txt + start-marker: '' + end-marker: '' + commit-message: 'chore: Update sponsors section in README' + pr-title: 'chore: Update sponsors section in README' + pr-body: 'This PR updates the sponsors section in the README file.' diff --git a/CHANGELOG.md b/CHANGELOG.md index 6346ccd3..9def4ba8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,37 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.0) - 2025-11-27 + +[Compare with 0.30.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.1...1.0.0) + +### Breaking Changes + +- `BaseHandler.name`: *Attribute value was changed*: `''` -> unset +- `BaseHandler.domain`: *Attribute value was changed*: `''` -> unset +- `BaseHandler.fallback_config`: *Public object was removed* +- `BaseHandler.__init__(args)`: *Parameter was removed* +- `BaseHandler.__init__(kwargs)`: *Parameter was removed* +- `BaseHandler.__init__(theme)`: *Parameter was added as required* +- `BaseHandler.__init__(custom_templates)`: *Parameter was added as required* +- `BaseHandler.__init__(mdx)`: *Parameter was added as required* +- `BaseHandler.__init__(mdx_config)`: *Parameter was added as required* +- `BaseHandler.update_env(args)`: *Parameter was removed* +- `BaseHandler.update_env(kwargs)`: *Parameter was removed* +- `BaseHandler.update_env(config)`: *Parameter was added as required* +- `Handlers.get_anchors`: *Public object was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.plugin`: *Public module was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.loggers`: *Public module was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.inventory`: *Public module was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.extension`: *Public module was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.handlers`: *Public module was removed* (import from `mkdocstrings` directly) + +### Code Refactoring + +- Remove deprecated code before v1 ([de34044](https://github.com/mkdocstrings/mkdocstrings/commit/de34044a02b45250e215af0f969dca581dfb82c5) by Timothée Mazzucotelli). +- Expect Zensical to pass extension configuration instead of loading it again from YAML ([6b73d5a](https://github.com/mkdocstrings/mkdocstrings/commit/6b73d5a2f455062ab6c68376c85adce6adc037a3) by Timothée Mazzucotelli). +- Expose the Markdown extension, to make mkdocstrings compatible with Zensical ([6de2667](https://github.com/mkdocstrings/mkdocstrings/commit/6de266759b79eb72cddd300e6a0a8576085fae40) by Timothée Mazzucotelli). + ## [0.30.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.30.1) - 2025-09-19 [Compare with 0.30.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.0...0.30.1) diff --git a/Makefile b/Makefile index 5e88121d..1b3391da 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,13 @@ # the `make` command will point at the `scripts/make` shell script. # This Makefile is just here to allow auto-completion in the terminal. +default: help + @echo + @echo 'Enable direnv in your shell to use the `make` command: `direnv allow`' + @echo 'Or use `python scripts/make ARGS` to run the commands/tasks directly.' + +.DEFAULT_GOAL: default + actions = \ allrun \ changelog \ diff --git a/README.md b/README.md index cb6887e6..a486ed40 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![documentation](https://img.shields.io/badge/docs-mkdocs-708FCC.svg?style=flat)](https://mkdocstrings.github.io/) [![pypi version](https://img.shields.io/pypi/v/mkdocstrings.svg)](https://pypi.org/project/mkdocstrings/) [![conda version](https://img.shields.io/conda/vn/conda-forge/mkdocstrings)](https://anaconda.org/conda-forge/mkdocstrings) -[![gitter](https://badges.gitter.im/join%20chat.svg)](https://app.gitter.im/#/room/#mkdocstrings:gitter.im) +[![gitter](https://img.shields.io/badge/matrix-chat-4DB798.svg?style=flat)](https://app.gitter.im/#/room/#mkdocstrings:gitter.im) Automatic documentation from sources, for [MkDocs](https://www.mkdocs.org/). Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdocstrings/community). @@ -24,6 +24,7 @@ Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdo We currently have [handlers](https://mkdocstrings.github.io/handlers/overview/) for the [C](https://mkdocstrings.github.io/c/), [Crystal](https://mkdocstrings.github.io/crystal/), + [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/), [Python](https://mkdocstrings.github.io/python/), [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/), [TypeScript](https://mkdocstrings.github.io/typescript/), and @@ -132,3 +133,66 @@ In one of your markdown files: ``` See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for more examples! + +## Sponsors + + + +
+ +
Silver sponsors

+Material for MkDocs
+FastAPI
+Pydantic
+

+ +
Bronze sponsors

+Nixtla
+

+
+ +--- + +

+ofek +samuelcolvin +tlambert03 +ssbarnea +femtomc +cmarqu +kolenaIO +ramnes +machow +BenHammersley +trevorWieland +laenan8466 +MarcoGorelli +analog-cbarber +OdinManiac +rstudio-sponsorship +schlich +SuperCowPowers +butterlyn +livingbio +NemetschekAllplan +EricJayHartman +15r10nk +cdwilson +activeloopai +roboflow +wrath-codes +leodevian +cmclaughlin +blaisep +RapidataAI +rodolphebarbanneau +theSymbolSyndicate +blakeNaccarato +ChargeStorm +Alphadelta14 +

+ + +*And 8 more private sponsor(s).* + + diff --git a/config/pytest.ini b/config/pytest.ini index 288b6cff..f65bd620 100644 --- a/config/pytest.ini +++ b/config/pytest.ini @@ -10,5 +10,9 @@ testpaths = # action:message_regex:warning_class:module_regex:line filterwarnings = error + default::EncodingWarning + error::EncodingWarning:mkdocstrings ignore:.*`get_anchors` method:DeprecationWarning:mkdocstrings ignore:.*Importing from:DeprecationWarning:mkdocstrings_handlers + # TODO: Remove once pytest-xdist 4 is released. + ignore:.*rsyncdir:DeprecationWarning:xdist diff --git a/docs/.overrides/main.html b/docs/.overrides/main.html index 1e956857..3bfd4775 100644 --- a/docs/.overrides/main.html +++ b/docs/.overrides/main.html @@ -1,20 +1,7 @@ {% extends "base.html" %} {% block announce %} - - Fund this project through - sponsorship - - {% include ".icons/octicons/heart-fill-16.svg" %} - — - - Follow - @pawamoy on - - - {% include ".icons/fontawesome/brands/mastodon.svg" %} - - Fosstodon - - for updates + ⚠️ mkdocstrings is in maintenance mode! + blog post + {% endblock %} diff --git a/docs/css/insiders.css b/docs/css/insiders.css deleted file mode 100644 index e7b9c74f..00000000 --- a/docs/css/insiders.css +++ /dev/null @@ -1,124 +0,0 @@ -@keyframes heart { - - 0%, - 40%, - 80%, - 100% { - transform: scale(1); - } - - 20%, - 60% { - transform: scale(1.15); - } -} - -@keyframes vibrate { - 0%, 2%, 4%, 6%, 8%, 10%, 12%, 14%, 16%, 18% { - -webkit-transform: translate3d(-2px, 0, 0); - transform: translate3d(-2px, 0, 0); - } - 1%, 3%, 5%, 7%, 9%, 11%, 13%, 15%, 17%, 19% { - -webkit-transform: translate3d(2px, 0, 0); - transform: translate3d(2px, 0, 0); - } - 20%, 100% { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.heart { - color: #e91e63; -} - -.pulse { - animation: heart 1000ms infinite; -} - -.vibrate { - animation: vibrate 2000ms infinite; -} - -.new-feature svg { - fill: var(--md-accent-fg-color) !important; -} - -a.insiders { - color: #e91e63; -} - -.sponsorship-list { - width: 100%; -} - -.sponsorship-item { - border-radius: 100%; - display: inline-block; - height: 1.6rem; - margin: 0.1rem; - overflow: hidden; - width: 1.6rem; -} - -.sponsorship-item:focus, .sponsorship-item:hover { - transform: scale(1.1); -} - -.sponsorship-item img { - filter: grayscale(100%) opacity(75%); - height: auto; - width: 100%; -} - -.sponsorship-item:focus img, .sponsorship-item:hover img { - filter: grayscale(0); -} - -.sponsorship-item.private { - background: var(--md-default-fg-color--lightest); - color: var(--md-default-fg-color); - font-size: .6rem; - font-weight: 700; - line-height: 1.6rem; - text-align: center; -} - -.mastodon { - color: #897ff8; - border-radius: 100%; - box-shadow: inset 0 0 0 .05rem currentcolor; - display: inline-block; - height: 1.2rem !important; - padding: .25rem; - transition: all .25s; - vertical-align: bottom !important; - width: 1.2rem; -} - -.premium-sponsors { - text-align: center; -} - -#silver-sponsors img { - height: 140px; -} - -#bronze-sponsors img { - height: 140px; -} - -#bronze-sponsors p { - display: flex; - flex-wrap: wrap; - justify-content: center; -} - -#bronze-sponsors a { - display: block; - flex-shrink: 0; -} - -.sponsors-total { - font-weight: bold; -} \ No newline at end of file diff --git a/docs/insiders/changelog.md b/docs/insiders/changelog.md deleted file mode 100644 index 0f438566..00000000 --- a/docs/insiders/changelog.md +++ /dev/null @@ -1,3 +0,0 @@ -# Changelog - -## mkdocstrings Insiders diff --git a/docs/insiders/goals.yml b/docs/insiders/goals.yml deleted file mode 100644 index 0e27b997..00000000 --- a/docs/insiders/goals.yml +++ /dev/null @@ -1,13 +0,0 @@ -goals: - 500: - name: PlasmaVac User Guide - features: [] - 1000: - name: GraviFridge Fluid Renewal - features: [] - 1500: - name: HyperLamp Navigation Tips - features: [] - 2000: - name: FusionDrive Ejection Configuration - features: [] diff --git a/docs/insiders/index.md b/docs/insiders/index.md deleted file mode 100644 index ce59f6bb..00000000 --- a/docs/insiders/index.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Insiders ---- - -# Insiders - -*mkdocstrings* follows the **sponsorware** release strategy, which means that new features are first exclusively released to sponsors as part of [Insiders][]. Read on to learn [what sponsorships achieve][sponsorship], [how to become a sponsor][sponsors] to get access to Insiders, and [what's in it for you][features]! - -## What is Insiders? - -*mkdocstrings Insiders* is a private fork of *mkdocstrings*, hosted as a private GitHub repository. Almost[^1] [all new features][features] are developed as part of this fork, which means that they are immediately available to all eligible sponsors, as they are granted access to this private repository. - -[^1]: In general, every new feature is first exclusively released to sponsors, but sometimes upstream dependencies enhance existing features that must be supported by *mkdocstrings*. - -Every feature is tied to a [funding goal][funding] in monthly subscriptions. When a funding goal is hit, the features that are tied to it are merged back into *mkdocstrings* and released for general availability, making them available to all users. Bugfixes are always released in tandem. - -Sponsorships start as low as [**$10 a month**][sponsors].[^2] - -[^2]: Note that $10 a month is the minimum amount to become eligible for Insiders. While GitHub Sponsors also allows to sponsor lower amounts or one-time amounts, those can't be granted access to Insiders due to technical reasons. Such contributions are still very much welcome as they help ensuring the project's sustainability. - -## What sponsorships achieve - -Sponsorships make this project sustainable, as they buy the maintainers of this project time – a very scarce resource – which is spent on the development of new features, bug fixing, stability improvement, issue triage and general support. The biggest bottleneck in Open Source is time.[^3] - -[^3]: Making an Open Source project sustainable is exceptionally hard: maintainers burn out, projects are abandoned. That's not great and very unpredictable. The sponsorware model ensures that if you decide to use *mkdocstrings*, you can be sure that bugs are fixed quickly and new features are added regularly. - -If you're unsure if you should sponsor this project, check out the list of [completed funding goals][goals completed] to learn whether you're already using features that were developed with the help of sponsorships. You're most likely using at least a handful of them, [thanks to our awesome sponsors][sponsors]! - -## What's in it for me? - -```python exec="1" session="insiders" -data_source = [ - "docs/insiders/goals.yml", - ("griffe-pydantic", "https://mkdocstrings.github.io/griffe-pydantic/", "insiders/goals.yml"), - ("griffe-typedoc", "https://mkdocstrings.github.io/griffe-typedoc/", "insiders/goals.yml"), - ("griffe-warnings-deprecated", "https://mkdocstrings.github.io/griffe-warnings-deprecated/", "insiders/goals.yml"), - ("mkdocstrings-c", "https://mkdocstrings.github.io/c/", "insiders/goals.yml"), - ("mkdocstrings-python", "https://mkdocstrings.github.io/python/", "insiders/goals.yml"), - ("mkdocstrings-shell", "https://mkdocstrings.github.io/shell/", "insiders/goals.yml"), - ("mkdocstrings-typescript", "https://mkdocstrings.github.io/typescript/", "insiders/goals.yml"), -] -``` - - -```python exec="1" session="insiders" idprefix="" ---8<-- "scripts/insiders.py" - -if unreleased_features: - print( - "The moment you [become a sponsor](#how-to-become-a-sponsor), you'll get **immediate " - f"access to {len(unreleased_features)} additional features** that you can start using right away, and " - "which are currently exclusively available to sponsors:\n" - ) - - for feature in unreleased_features: - feature.render(badge=True) - - print( - "\n\nThese are just the features related to this project. " - "[See the complete feature list on the author's main Insiders page](https://pawamoy.github.io/insiders/#whats-in-it-for-me)." - ) -else: - print( - "The moment you [become a sponsor](#how-to-become-a-sponsor), you'll get immediate " - "access to all released features that you can start using right away, and " - "which are exclusively available to sponsors. At this moment, there are no " - "Insiders features for this project, but checkout the [next funding goals](#goals) " - "to see what's coming, as well as **[the feature list for all Insiders projects](https://pawamoy.github.io/insiders/#whats-in-it-for-me).**" - ) -``` - - -Additionally, your sponsorship will give more weight to your upvotes on issues, helping us prioritize work items in our backlog. For more information on how we prioritize work, see this page: [Backlog management][backlog]. - -## How to become a sponsor - -Thanks for your interest in sponsoring! In order to become an eligible sponsor with your GitHub account, visit [pawamoy's sponsor profile][github sponsor profile], and complete a sponsorship of **$10 a month or more**. You can use your individual or organization GitHub account for sponsoring. - -Sponsorships lower than $10 a month are also very much appreciated, and useful. They won't grant you access to Insiders, but they will be counted towards reaching sponsorship goals. Every sponsorship helps us implementing new features and releasing them to the public. - -**Important:** By default, when you're sponsoring **[@pawamoy][github sponsor profile]** through a GitHub organization, all the publicly visible members of the organization will be invited to join our private repositories. If you wish to only grant access to a subset of users, please send a short email to insiders@pawamoy.fr with the name of your organization and the GitHub accounts of the users that should be granted access. - -**Tip:** to ensure that access is not tied to a particular individual GitHub account, you can create a bot account (i.e. a GitHub account that is not tied to a specific individual), and use this account for the sponsoring. After being granted access to our private repositories, the bot account can create private forks of our private repositories into your own organization, which all members of your organization will have access to. - -You can cancel your sponsorship anytime.[^5] - -[^5]: If you cancel your sponsorship, GitHub schedules a cancellation request which will become effective at the end of the billing cycle. This means that even though you cancel your sponsorship, you will keep your access to Insiders as long as your cancellation isn't effective. All charges are processed by GitHub through Stripe. As we don't receive any information regarding your payment, and GitHub doesn't offer refunds, sponsorships are non-refundable. - - -[:octicons-heart-fill-24:{ .pulse }   Join our awesome sponsors][github sponsor profile]{ .md-button .md-button--primary } - -
-
-
-
-
-
-
- -
- - - If you sponsor publicly, you're automatically added here with a link to your profile and avatar to show your support for *mkdocstrings*. Alternatively, if you wish to keep your sponsorship private, you'll be a silent +1. You can select visibility during checkout and change it afterwards. - - -## Funding - -### Goals - -The following section lists all funding goals. Each goal contains a list of features prefixed with a checkmark symbol, denoting whether a feature is :octicons-check-circle-fill-24:{ style="color: #00e676" } already available or :octicons-check-circle-fill-24:{ style="color: var(--md-default-fg-color--lightest)" } planned, but not yet implemented. When the funding goal is hit, the features are released for general availability. - -```python exec="1" session="insiders" idprefix="" -for goal in goals.values(): - if not goal.complete: - goal.render() -``` - -### Goals completed - -This section lists all funding goals that were previously completed, which means that those features were part of Insiders, but are now generally available and can be used by all users. - -```python exec="1" session="insiders" idprefix="" -for goal in goals.values(): - if goal.complete: - goal.render() -``` - -## Frequently asked questions - -### Compatibility - -> We're building an open source project and want to allow outside collaborators to use *mkdocstrings* locally without having access to Insiders. Is this still possible? - -Yes. Insiders is compatible with *mkdocstrings*. Almost all new features and configuration options are either backward-compatible or implemented behind feature flags. Most Insiders features enhance the overall experience, though while these features add value for the users of your project, they shouldn't be necessary for previewing when making changes to content. - -### Payment - -> We don't want to pay for sponsorship every month. Are there any other options? - -Yes. You can sponsor on a yearly basis by [switching your GitHub account to a yearly billing cycle][billing cycle]. If for some reason you cannot do that, you could also create a dedicated GitHub account with a yearly billing cycle, which you only use for sponsoring (some sponsors already do that). - -If you have any problems or further questions, please reach out to insiders@pawamoy.fr. - -### Terms - -> Are we allowed to use Insiders under the same terms and conditions as *mkdocstrings*? - -Yes. Whether you're an individual or a company, you may use *mkdocstrings Insiders* precisely under the same terms as *mkdocstrings*, which are given by the [ISC license][license]. However, we kindly ask you to respect our **fair use policy**: - -- Please **don't distribute the source code** of Insiders. You may freely use it for public, private or commercial projects, privately fork or mirror it, but please don't make the source code public, as it would counteract the sponsorware strategy. -- If you cancel your subscription, your access to the private repository is revoked, and you will miss out on all future updates of Insiders. However, you may **use the latest version** that's available to you **as long as you like**. Just remember that [GitHub deletes private forks][private forks]. - -[backlog]: https://pawamoy.github.io/backlog/ -[insiders]: #what-is-insiders -[sponsorship]: #what-sponsorships-achieve -[sponsors]: #how-to-become-a-sponsor -[features]: #whats-in-it-for-me -[funding]: #funding -[goals completed]: #goals-completed -[github sponsor profile]: https://github.com/sponsors/pawamoy -[billing cycle]: https://docs.github.com/en/github/setting-up-and-managing-billing-and-payments-on-github/changing-the-duration-of-your-billing-cycle -[license]: ../license.md -[private forks]: https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/removing-a-collaborator-from-a-personal-repository - - - diff --git a/docs/insiders/installation.md b/docs/insiders/installation.md deleted file mode 100644 index 1df4608b..00000000 --- a/docs/insiders/installation.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Getting started with Insiders ---- - -# Getting started with Insiders - -*mkdocstrings Insiders* is a compatible drop-in replacement for *mkdocstrings*, and can be installed similarly using `pip` or `git`. Note that in order to access the Insiders repository, you need to [become an eligible sponsor][] of @pawamoy on GitHub. - -## Installation - -### with the `insiders` tool - -[`insiders`][insiders-tool] is a tool that helps you keep up-to-date versions of Insiders projects in the PyPI index of your choice (self-hosted, Google registry, Artifactory, etc.). - -**We kindly ask that you do not upload the distributions to public registries, as it is against our [Terms of use][].** - -### with pip (ssh/https) - -*mkdocstrings Insiders* can be installed with `pip` [using SSH][install-pip-ssh]: - -```bash -pip install git+ssh://git@github.com/pawamoy-insiders/mkdocstrings.git -``` - -Or using HTTPS: - -```bash -pip install git+https://${GH_TOKEN}@github.com/pawamoy-insiders/mkdocstrings.git -``` - ->? NOTE: **How to get a GitHub personal access token?** The `GH_TOKEN` environment variable is a GitHub token. It can be obtained by creating a [personal access token][github-pat] for your GitHub account. It will give you access to the Insiders repository, programmatically, from the command line or GitHub Actions workflows: -> -> 1. Go to https://github.com/settings/tokens -> 2. Click on [Generate a new token][github-pat-new] -> 3. Enter a name and select the [`repo`][scopes] scope -> 4. Generate the token and store it in a safe place -> -> Note that the personal access token must be kept secret at all times, as it allows the owner to access your private repositories. - -### with Git - -Of course, you can use *mkdocstrings Insiders* directly using Git: - -``` -git clone git@github.com:pawamoy-insiders/mkdocstrings -``` - -When cloning with Git, the package must be installed: - -``` -pip install -e mkdocstrings -``` - -## Upgrading - -When upgrading Insiders, you should always check the version of *mkdocstrings* which makes up the first part of the version qualifier. For example, a version like `8.x.x.4.x.x` means that Insiders `4.x.x` is currently based on `8.x.x`. - -If the major version increased, it's a good idea to consult the [changelog][] and go through the steps to ensure your configuration is up to date and all necessary changes have been made. - -[become an eligible sponsor]: ./index.md#how-to-become-a-sponsor -[changelog]: ./changelog.md -[github-pat]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token -[github-pat-new]: https://github.com/settings/tokens/new -[insiders-tool]: https://pawamoy.github.io/insiders-project/ -[install-pip-ssh]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh -[scopes]: https://docs.github.com/en/developers/apps/scopes-for-oauth-apps#available-scopes -[terms of use]: ./index.md#terms diff --git a/docs/js/insiders.js b/docs/js/insiders.js deleted file mode 100644 index 8bb68485..00000000 --- a/docs/js/insiders.js +++ /dev/null @@ -1,74 +0,0 @@ -function humanReadableAmount(amount) { - const strAmount = String(amount); - if (strAmount.length >= 4) { - return `${strAmount.slice(0, strAmount.length - 3)},${strAmount.slice(-3)}`; - } - return strAmount; -} - -function getJSON(url, callback) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'json'; - xhr.onload = function () { - var status = xhr.status; - if (status === 200) { - callback(null, xhr.response); - } else { - callback(status, xhr.response); - } - }; - xhr.send(); -} - -function updatePremiumSponsors(dataURL, rank) { - let capRank = rank.charAt(0).toUpperCase() + rank.slice(1); - getJSON(dataURL + `/sponsors${capRank}.json`, function (err, sponsors) { - const sponsorsDiv = document.getElementById(`${rank}-sponsors`); - if (sponsors.length > 0) { - let html = ''; - html += `${capRank} sponsors

` - sponsors.forEach(function (sponsor) { - html += ` - - ${sponsor.name} - - ` - }); - html += '

' - sponsorsDiv.innerHTML = html; - } - }); -} - -function updateInsidersPage(author_username) { - const sponsorURL = `https://github.com/sponsors/${author_username}` - const dataURL = `https://raw.githubusercontent.com/${author_username}/sponsors/main`; - getJSON(dataURL + '/numbers.json', function (err, numbers) { - document.getElementById('sponsors-count').innerHTML = numbers.count; - Array.from(document.getElementsByClassName('sponsors-total')).forEach(function (element) { - element.innerHTML = '$ ' + humanReadableAmount(numbers.total); - }); - getJSON(dataURL + '/sponsors.json', function (err, sponsors) { - const sponsorsElem = document.getElementById('sponsors'); - const privateSponsors = numbers.count - sponsors.length; - sponsors.forEach(function (sponsor) { - sponsorsElem.innerHTML += ` - - - - `; - }); - if (privateSponsors > 0) { - sponsorsElem.innerHTML += ` - - +${privateSponsors} - - `; - } - }); - }); - updatePremiumSponsors(dataURL, "gold"); - updatePremiumSponsors(dataURL, "silver"); - updatePremiumSponsors(dataURL, "bronze"); -} diff --git a/docs/usage/handlers.md b/docs/usage/handlers.md index c4bbda57..efdaccd1 100644 --- a/docs/usage/handlers.md +++ b/docs/usage/handlers.md @@ -6,6 +6,7 @@ A handler is what makes it possible to collect and render documentation for a pa - [C](https://mkdocstrings.github.io/c/){ .external } - [Crystal](https://mkdocstrings.github.io/crystal/){ .external } +- [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/){ .external } - [Python](https://mkdocstrings.github.io/python/){ .external } - [Python (Legacy)](https://mkdocstrings.github.io/python-legacy/){ .external } - [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/){ .external } diff --git a/duties.py b/duties.py index b75d8b55..04357dbb 100644 --- a/duties.py +++ b/duties.py @@ -6,10 +6,9 @@ import re import sys from contextlib import contextmanager -from functools import wraps from importlib.metadata import version as pkgversion from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING from duty import duty, tools @@ -26,6 +25,8 @@ WINDOWS = os.name == "nt" PTY = not WINDOWS and not CI MULTIRUN = os.environ.get("MULTIRUN", "0") == "1" +PY_VERSION = f"{sys.version_info.major}{sys.version_info.minor}" +PY_DEV = "314" def pyprefix(title: str) -> str: @@ -35,21 +36,6 @@ def pyprefix(title: str) -> str: return title -def not_from_insiders(func: Callable) -> Callable: - @wraps(func) - def wrapper(ctx: Context, *args: Any, **kwargs: Any) -> None: - origin = ctx.run("git config --get remote.origin.url", silent=True) - if "pawamoy-insiders/griffe" in origin: - ctx.run( - lambda: False, - title="Not running this task from insiders repository (do that from public repo instead!)", - ) - return - func(ctx, *args, **kwargs) - - return wrapper - - @contextmanager def material_insiders() -> Iterator[bool]: if "+insiders" in pkgversion("mkdocs-material"): @@ -84,7 +70,7 @@ def check(ctx: Context) -> None: """Check it all!""" -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_quality(ctx: Context) -> None: """Check the code quality.""" ctx.run( @@ -93,7 +79,7 @@ def check_quality(ctx: Context) -> None: ) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_docs(ctx: Context) -> None: """Check if the documentation builds correctly.""" Path("htmlcov").mkdir(parents=True, exist_ok=True) @@ -105,7 +91,7 @@ def check_docs(ctx: Context) -> None: ) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_types(ctx: Context) -> None: """Check that the code is correctly typed.""" os.environ["MYPYPATH"] = "src" @@ -116,7 +102,7 @@ def check_types(ctx: Context) -> None: ) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_api(ctx: Context, *cli_args: str) -> None: """Check for API breaking changes.""" ctx.run( @@ -143,39 +129,13 @@ def docs(ctx: Context, *cli_args: str, host: str = "127.0.0.1", port: int = 8000 @duty -def docs_deploy(ctx: Context, *, force: bool = False) -> None: - """Deploy the documentation to GitHub pages. - - Parameters: - force: Whether to force deployment, even from non-Insiders version. - """ +def docs_deploy(ctx: Context) -> None: + """Deploy the documentation to GitHub pages.""" os.environ["DEPLOY"] = "true" with material_insiders() as insiders: if not insiders: ctx.run(lambda: False, title="Not deploying docs without Material for MkDocs Insiders!") - origin = ctx.run("git config --get remote.origin.url", silent=True, allow_overrides=False) - if "pawamoy-insiders/mkdocstrings" in origin: - ctx.run( - "git remote add org-pages git@github.com:mkdocstrings/mkdocstrings.github.io", - silent=True, - nofail=True, - allow_overrides=False, - ) - ctx.run( - tools.mkdocs.gh_deploy(remote_name="org-pages", force=True), - title="Deploying documentation", - ) - elif force: - ctx.run( - tools.mkdocs.gh_deploy(remote_name="org-pages", force=True), - title="Deploying documentation", - ) - else: - ctx.run( - lambda: False, - title="Not deploying docs from public repository (do that from insiders instead!)", - nofail=True, - ) + ctx.run(tools.mkdocs.gh_deploy(remote_name="org-pages", force=True), title="Deploying documentation") @duty @@ -199,7 +159,6 @@ def build(ctx: Context) -> None: @duty -@not_from_insiders def publish(ctx: Context) -> None: """Publish source and wheel distributions to PyPI.""" if not Path("dist").exists(): @@ -213,7 +172,6 @@ def publish(ctx: Context) -> None: @duty(post=["build", "publish", "docs-deploy"]) -@not_from_insiders def release(ctx: Context, version: str = "") -> None: """Release a new Python package. @@ -224,7 +182,7 @@ def release(ctx: Context, version: str = "") -> None: ctx.run("false", title="A version must be provided") ctx.run("git add pyproject.toml CHANGELOG.md", title="Staging files", pty=PTY) ctx.run(["git", "commit", "-m", f"chore: Prepare release {version}"], title="Committing changes", pty=PTY) - ctx.run(f"git tag {version}", title="Tagging commit", pty=PTY) + ctx.run(f"git tag -m '' -a {version}", title="Tagging commit", pty=PTY) ctx.run("git push", title="Pushing commits", pty=False) ctx.run("git push --tags", title="Pushing tags", pty=False) @@ -237,20 +195,15 @@ def coverage(ctx: Context) -> None: ctx.run(tools.coverage.html(rcfile="config/coverage.ini")) -@duty -def test(ctx: Context, *cli_args: str, match: str = "") -> None: # noqa: PT028 - """Run the test suite. - - Parameters: - match: A pytest expression to filter selected tests. - """ - py_version = f"{sys.version_info.major}{sys.version_info.minor}" - os.environ["COVERAGE_FILE"] = f".coverage.{py_version}" +@duty(nofail=PY_VERSION == PY_DEV) +def test(ctx: Context, *cli_args: str) -> None: + """Run the test suite.""" + os.environ["COVERAGE_FILE"] = f".coverage.{PY_VERSION}" + os.environ["PYTHONWARNDEFAULTENCODING"] = "1" ctx.run( tools.pytest( "tests", config_file="config/pytest.ini", - select=match, color="yes", ).add_args("-n", "auto", *cli_args), title=pyprefix("Running tests"), diff --git a/mkdocs.yml b/mkdocs.yml index 9163e91d..8250973b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,6 +26,7 @@ nav: - All handlers: - C: https://mkdocstrings.github.io/c/ - Crystal: https://mkdocstrings.github.io/crystal/ + - GitHub Actions: https://watermarkhu.nl/mkdocstrings-github/ - Python: https://mkdocstrings.github.io/python/ - Python (Legacy): https://mkdocstrings.github.io/python-legacy/ - MATLAB: https://watermarkhu.nl/mkdocstrings-matlab/ @@ -40,11 +41,6 @@ nav: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md - Coverage report: coverage.md -- Insiders: - - insiders/index.md - - Getting started: - - Installation: insiders/installation.md - - Changelog: insiders/changelog.md - Author's website: https://pawamoy.github.io/ theme: @@ -92,7 +88,6 @@ extra_css: - css/style.css - css/material.css - css/mkdocstrings.css -- css/insiders.css extra_javascript: - js/feedback.js @@ -157,7 +152,7 @@ plugins: show_root_heading: true show_root_full_path: false show_signature_annotations: true - show_source: false + show_source: true show_symbol_type_heading: true show_symbol_type_toc: true signature_crossrefs: true diff --git a/pyproject.toml b/pyproject.toml index 4db21462..2b3d6c17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ authors = [{name = "Timothée Mazzucotelli", email = "dev@pawamoy.fr"}] license = "ISC" license-files = ["LICENSE"] readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" keywords = ["mkdocs", "mkdocs-plugin", "docstrings", "autodoc", "documentation"] dynamic = ["version"] classifiers = [ @@ -18,7 +18,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -31,15 +30,12 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - # YORE: Bump 1: Replace `2.11.1` with `3.1` within line. - "Jinja2>=2.11.1", + "Jinja2>=3.1", "Markdown>=3.6", "MarkupSafe>=1.1", "mkdocs>=1.6", "mkdocs-autorefs>=1.4", "pymdown-extensions>=6.3", - # YORE: EOL 3.9: Remove line. - "importlib-metadata>=4.6; python_version < '3.10'", ] [project.optional-dependencies] diff --git a/scripts/get_version.py b/scripts/get_version.py index 6734e5b6..3c425a73 100644 --- a/scripts/get_version.py +++ b/scripts/get_version.py @@ -4,7 +4,12 @@ from contextlib import suppress from pathlib import Path -from pdm.backend.hooks.version import SCMVersion, Version, default_version_formatter, get_version_from_scm +from pdm.backend.hooks.version import ( # ty: ignore[unresolved-import] + SCMVersion, + Version, + default_version_formatter, + get_version_from_scm, +) _root = Path(__file__).parent.parent _changelog = _root / "CHANGELOG.md" diff --git a/scripts/insiders.py b/scripts/insiders.py deleted file mode 100644 index 4cd438d4..00000000 --- a/scripts/insiders.py +++ /dev/null @@ -1,173 +0,0 @@ -# Functions related to Insiders funding goals. - -from __future__ import annotations - -import json -import logging -import os -import posixpath -from dataclasses import dataclass -from datetime import date, datetime, timedelta -from itertools import chain -from pathlib import Path -from typing import TYPE_CHECKING, cast -from urllib.error import HTTPError -from urllib.parse import urljoin -from urllib.request import urlopen - -import yaml - -if TYPE_CHECKING: - from collections.abc import Iterable - -logger = logging.getLogger(f"mkdocs.logs.{__name__}") - - -def human_readable_amount(amount: int) -> str: - str_amount = str(amount) - if len(str_amount) >= 4: # noqa: PLR2004 - return f"{str_amount[: len(str_amount) - 3]},{str_amount[-3:]}" - return str_amount - - -@dataclass -class Project: - name: str - url: str - - -@dataclass -class Feature: - name: str - ref: str | None - since: date | None - project: Project | None - - def url(self, rel_base: str = "..") -> str | None: # noqa: D102 - if not self.ref: - return None - if self.project: - rel_base = self.project.url - return posixpath.join(rel_base, self.ref.lstrip("/")) - - def render(self, rel_base: str = "..", *, badge: bool = False) -> None: # noqa: D102 - new = "" - if badge: - recent = self.since and date.today() - self.since <= timedelta(days=60) # noqa: DTZ011 - if recent: - ft_date = self.since.strftime("%B %d, %Y") # type: ignore[union-attr] - new = f' :material-alert-decagram:{{ .new-feature .vibrate title="Added on {ft_date}" }}' - project = f"[{self.project.name}]({self.project.url}) — " if self.project else "" - feature = f"[{self.name}]({self.url(rel_base)})" if self.ref else self.name - print(f"- [{'x' if self.since else ' '}] {project}{feature}{new}") - - -@dataclass -class Goal: - name: str - amount: int - features: list[Feature] - complete: bool = False - - @property - def human_readable_amount(self) -> str: # noqa: D102 - return human_readable_amount(self.amount) - - def render(self, rel_base: str = "..") -> None: # noqa: D102 - print(f"#### $ {self.human_readable_amount} — {self.name}\n") - if self.features: - for feature in self.features: - feature.render(rel_base) - print("") - else: - print("There are no features in this goal for this project. ") - print( - "[See the features in this goal **for all Insiders projects.**]" - f"(https://pawamoy.github.io/insiders/#{self.amount}-{self.name.lower().replace(' ', '-')})", - ) - - -def load_goals(data: str, funding: int = 0, project: Project | None = None) -> dict[int, Goal]: - goals_data = yaml.safe_load(data)["goals"] - return { - amount: Goal( - name=goal_data["name"], - amount=amount, - complete=funding >= amount, - features=[ - Feature( - name=feature_data["name"], - ref=feature_data.get("ref"), - since=feature_data.get("since") and datetime.strptime(feature_data["since"], "%Y/%m/%d").date(), # noqa: DTZ007 - project=project, - ) - for feature_data in goal_data["features"] - ], - ) - for amount, goal_data in goals_data.items() - } - - -def _load_goals_from_disk(path: str, funding: int = 0) -> dict[int, Goal]: - project_dir = os.getenv("MKDOCS_CONFIG_DIR", ".") - try: - data = Path(project_dir, path).read_text() - except OSError as error: - raise RuntimeError(f"Could not load data from disk: {path}") from error - return load_goals(data, funding) - - -def _load_goals_from_url(source_data: tuple[str, str, str], funding: int = 0) -> dict[int, Goal]: - project_name, project_url, data_fragment = source_data - data_url = urljoin(project_url, data_fragment) - try: - with urlopen(data_url) as response: # noqa: S310 - data = response.read() - except HTTPError as error: - raise RuntimeError(f"Could not load data from network: {data_url}") from error - return load_goals(data, funding, project=Project(name=project_name, url=project_url)) - - -def _load_goals(source: str | tuple[str, str, str], funding: int = 0) -> dict[int, Goal]: - if isinstance(source, str): - return _load_goals_from_disk(source, funding) - return _load_goals_from_url(source, funding) - - -def funding_goals(source: str | list[str | tuple[str, str, str]], funding: int = 0) -> dict[int, Goal]: - if isinstance(source, str): - return _load_goals_from_disk(source, funding) - goals = {} - for src in source: - source_goals = _load_goals(src, funding) - for amount, goal in source_goals.items(): - if amount not in goals: - goals[amount] = goal - else: - goals[amount].features.extend(goal.features) - return {amount: goals[amount] for amount in sorted(goals)} - - -def feature_list(goals: Iterable[Goal]) -> list[Feature]: - return list(chain.from_iterable(goal.features for goal in goals)) - - -def load_json(url: str) -> str | list | dict: - with urlopen(url) as response: # noqa: S310 - return json.loads(response.read().decode()) - - -data_source = globals()["data_source"] -sponsor_url = "https://github.com/sponsors/pawamoy" -data_url = "https://raw.githubusercontent.com/pawamoy/sponsors/main" -numbers: dict[str, int] = load_json(f"{data_url}/numbers.json") # type: ignore[assignment] -sponsors: list[dict] = load_json(f"{data_url}/sponsors.json") # type: ignore[assignment] -current_funding = numbers["total"] -sponsors_count = numbers["count"] -goals = funding_goals(data_source, funding=current_funding) -ongoing_goals = [goal for goal in goals.values() if not goal.complete] -unreleased_features = sorted( - (ft for ft in feature_list(ongoing_goals) if ft.since), - key=lambda ft: cast("date", ft.since), - reverse=True, -) diff --git a/scripts/make.py b/scripts/make.py index 55679baa..b741a366 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -14,7 +14,8 @@ from collections.abc import Iterator -PYTHON_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.9 3.10 3.11 3.12 3.13 3.14").split() +PYTHON_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.10 3.11 3.12 3.13 3.14 3.15").split() +PYTHON_DEV = "3.15" def shell(cmd: str, *, capture_output: bool = False, **kwargs: Any) -> str | None: @@ -67,16 +68,31 @@ def setup() -> None: uv_install(venv_path) +class _RunError(subprocess.CalledProcessError): + def __init__(self, *args: Any, python_version: str, **kwargs: Any): + super().__init__(*args, **kwargs) + self.python_version = python_version + + def run(version: str, cmd: str, *args: str, **kwargs: Any) -> None: """Run a command in a virtual environment.""" kwargs = {"check": True, **kwargs} uv_run = ["uv", "run", "--no-sync"] - if version == "default": - with environ(UV_PROJECT_ENVIRONMENT=".venv"): - subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 - else: - with environ(UV_PROJECT_ENVIRONMENT=f".venvs/{version}", MULTIRUN="1"): - subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 + try: + if version == "default": + with environ(UV_PROJECT_ENVIRONMENT=".venv"): + subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 + else: + with environ(UV_PROJECT_ENVIRONMENT=f".venvs/{version}", MULTIRUN="1"): + subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 + except subprocess.CalledProcessError as process: + raise _RunError( + returncode=process.returncode, + python_version=version, + cmd=process.cmd, + output=process.output, + stderr=process.stderr, + ) from process def multirun(cmd: str, *args: str, **kwargs: Any) -> None: @@ -144,19 +160,31 @@ def main() -> int: cmd = args.pop(0) if cmd == "run": - run("default", *args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + run("default", *args) # ty: ignore[missing-argument] return 0 if cmd == "multirun": - multirun(*args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + multirun(*args) # ty: ignore[missing-argument] return 0 if cmd == "allrun": - allrun(*args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + allrun(*args) # ty: ignore[missing-argument] return 0 if cmd.startswith("3."): - run(cmd, *args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + run(cmd, *args) # ty: ignore[missing-argument] return 0 opts = [] @@ -183,7 +211,14 @@ def main() -> int: if __name__ == "__main__": try: sys.exit(main()) - except subprocess.CalledProcessError as process: + except _RunError as process: if process.output: print(process.output, file=sys.stderr) - sys.exit(process.returncode) + if (code := process.returncode) == 139: # noqa: PLR2004 + print( + f"✗ (python{process.python_version}) '{' '.join(process.cmd)}' failed with return code {code} (segfault)", + file=sys.stderr, + ) + if process.python_version == PYTHON_DEV: + code = 0 + sys.exit(code) diff --git a/src/mkdocstrings/__init__.py b/src/mkdocstrings/__init__.py index 71720f8a..137811b1 100644 --- a/src/mkdocstrings/__init__.py +++ b/src/mkdocstrings/__init__.py @@ -5,7 +5,7 @@ from __future__ import annotations -from mkdocstrings._internal.extension import AutoDocProcessor, MkdocstringsExtension +from mkdocstrings._internal.extension import AutoDocProcessor, MkdocstringsExtension, makeExtension from mkdocstrings._internal.handlers.base import ( BaseHandler, CollectionError, @@ -62,4 +62,5 @@ "get_template_logger", "get_template_logger_function", "get_template_path", + "makeExtension", ] diff --git a/src/mkdocstrings/_internal/debug.py b/src/mkdocstrings/_internal/debug.py index 7b56409b..f6b11600 100644 --- a/src/mkdocstrings/_internal/debug.py +++ b/src/mkdocstrings/_internal/debug.py @@ -85,7 +85,7 @@ def _get_debug_info() -> _Environment: interpreter_version=py_version, interpreter_path=sys.executable, platform=platform.platform(), - variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], + variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], # ty: ignore[invalid-argument-type] packages=[_Package(pkg, _get_version(pkg)) for pkg in packages], ) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 2277775c..3a6f4530 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -26,7 +26,6 @@ from functools import partial from inspect import signature from typing import TYPE_CHECKING, Any -from warnings import warn from xml.etree.ElementTree import Element import yaml @@ -35,6 +34,7 @@ from markdown.extensions import Extension from markdown.treeprocessors import Treeprocessor from mkdocs.exceptions import PluginError +from mkdocs_autorefs import AutorefsConfig, AutorefsPlugin from mkdocstrings._internal.handlers.base import BaseHandler, CollectionError, CollectorItem, Handlers from mkdocstrings._internal.loggers import get_logger @@ -43,7 +43,6 @@ from collections.abc import MutableSequence from markdown import Markdown - from mkdocs_autorefs import AutorefsPlugin _logger = get_logger("mkdocstrings") @@ -172,19 +171,7 @@ def _process_block( # Heading level obtained from Markdown (`##`) takes precedence. local_options["heading_level"] = heading_level - # YORE: Bump 1: Replace block with line 2. - if handler.get_options.__func__ is not BaseHandler.get_options: # type: ignore[attr-defined] - options = handler.get_options(local_options) - else: - warn( - "mkdocstrings v1 will start using your handler's `get_options` method to build options " - "instead of merging the global and local options (dictionaries). ", - DeprecationWarning, - stacklevel=1, - ) - handler_config = self._handlers.get_handler_config(handler_name) - global_options = handler_config.get("options", {}) - options = {**global_options, **local_options} + options = handler.get_options(local_options) _logger.debug("Collecting data") try: @@ -266,23 +253,7 @@ def _process_headings(self, handler: BaseHandler, element: Element) -> None: # Register all identifiers for this object # both in the autorefs plugin and in the inventory. aliases: tuple[str, ...] - # YORE: Bump 1: Replace block with line 16. - if hasattr(handler, "get_anchors"): - warn( - "The `get_anchors` method is deprecated. " - "Declare a `get_aliases` method instead, accepting a string (identifier) " - "instead of a collected object.", - DeprecationWarning, - stacklevel=1, - ) - try: - data_object = handler.collect(rendered_id, getattr(handler, "fallback_config", {})) - except CollectionError: - aliases = () - else: - aliases = handler.get_anchors(data_object) - else: - aliases = handler.get_aliases(rendered_id) + aliases = handler.get_aliases(rendered_id) for alias in aliases: if alias != rendered_id: @@ -380,3 +351,80 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me "mkdocstrings_post_toc_labels", priority=4, # Right after 'toc'. ) + + +# ----------------------------------------------------------------------------- +# The following is only used by Zensical. The goal is to provide temporary +# compatibility for users migrating from MkDocs (and Material for MkDocs) +# to Zensical. When detecting the use of the mkdocstrings plugin in mkdocs.yml, +# Zensical will add the mkdocstrings extension to its Markdown extensions. + +_default_config: dict[str, Any] = { + "default_handler": "python", + "handlers": {}, + "custom_templates": None, + "locale": "en", + "enable_inventory": True, + "enabled": True, +} + + +def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str], dict[str, Any]]: + # Split markdown extensions and their configs from mkdocs.yml + mdx: list[str] = [] + mdx_config: dict[str, Any] = {} + for item in markdown_extensions: + if isinstance(item, str): + mdx.append(item) + elif isinstance(item, dict): + for key, value in item.items(): + mdx.append(key) + mdx_config[key] = value + break # Only one item per dict + return mdx, mdx_config + + +class _ToolConfig: + def __init__(self, config_file_path: str | None = None) -> None: + self.config_file_path = config_file_path + + +def makeExtension( # noqa: N802 + *, + default_handler: str | None = None, + inventory_project: str | None = None, + inventory_version: str | None = None, + handlers: dict[str, dict] | None = None, + custom_templates: str | None = None, + markdown_extensions: list[str | dict] | None = None, + locale: str | None = None, + config_file_path: str | None = None, +) -> MkdocstringsExtension: + """Create the extension instance. + + We only support this function being used by Zensical. + Consider this function private API. + """ + mdx, mdx_config = _split_configs(markdown_extensions or []) + tool_config = _ToolConfig(config_file_path=config_file_path) + + handlers_instance = Handlers( + theme="material", + default=default_handler or _default_config["default_handler"], + inventory_project=inventory_project or "Project", + inventory_version=inventory_version or "0.0.0", + handlers_config=handlers or _default_config["handlers"], + custom_templates=custom_templates or _default_config["custom_templates"], + mdx=mdx, + mdx_config=mdx_config, + locale=locale or _default_config["locale"], + tool_config=tool_config, + ) + + handlers_instance._download_inventories() + + autorefs = AutorefsPlugin() + autorefs.config = AutorefsConfig() + autorefs.scan_toc = False + + return MkdocstringsExtension(handlers=handlers_instance, autorefs=autorefs) diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index c4e9950d..2eb9b3e6 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -6,10 +6,9 @@ import datetime import importlib -import inspect import ssl -import sys from concurrent import futures +from importlib.metadata import entry_points from io import BytesIO from pathlib import Path from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, cast @@ -33,12 +32,6 @@ from mkdocstrings._internal.inventory import Inventory from mkdocstrings._internal.loggers import get_logger, get_template_logger -# YORE: EOL 3.9: Replace block with line 4. -if sys.version_info < (3, 10): - from importlib_metadata import entry_points -else: - from importlib.metadata import entry_points - if TYPE_CHECKING: from collections.abc import Iterable, Iterator, Mapping, Sequence @@ -104,21 +97,15 @@ class BaseHandler: To add custom CSS, add an `extra_css` variable or create an 'style.css' file beside the templates. """ - # YORE: Bump 1: Replace ` = ""` with `` within line. - name: ClassVar[str] = "" + name: ClassVar[str] """The handler's name, for example "python".""" - # YORE: Bump 1: Replace ` = ""` with `` within line. - domain: ClassVar[str] = "" + domain: ClassVar[str] """The handler's domain, used to register objects in the inventory, for example "py".""" enable_inventory: ClassVar[bool] = False """Whether the inventory creation is enabled.""" - # YORE: Bump 1: Remove block. - fallback_config: ClassVar[dict] = {} - """Fallback configuration when searching anchors for identifiers.""" - fallback_theme: ClassVar[str] = "" """Fallback theme to use when a template isn't found in the configured theme.""" @@ -127,16 +114,11 @@ class BaseHandler: def __init__( self, - # YORE: Bump 1: Remove line. - *args: Any, - # YORE: Bump 1: Remove line. - **kwargs: Any, - # YORE: Bump 1: Replace `# ` with `` within block. - # *, - # theme: str, - # custom_templates: str | None, - # mdx: Sequence[str | Extension], - # mdx_config: Mapping[str, Any], + *, + theme: str, + custom_templates: str | None, + mdx: Sequence[str | Extension], + mdx_config: Mapping[str, Any], ) -> None: """Initialize the object. @@ -149,58 +131,6 @@ def __init__( mdx (list[str | Extension]): A list of Markdown extensions to use. mdx_config (Mapping[str, Mapping[str, Any]]): Configuration for the Markdown extensions. """ - # YORE: Bump 1: Remove block. - handler = "" - theme = "" - custom_templates = None - if args: - handler, args = args[0], args[1:] - if args: - theme, args = args[0], args[1:] - warn( - "The `theme` argument must be passed as a keyword argument.", - DeprecationWarning, - stacklevel=2, - ) - if args: - custom_templates, args = args[0], args[1:] - warn( - "The `custom_templates` argument must be passed as a keyword argument.", - DeprecationWarning, - stacklevel=2, - ) - handler = kwargs.pop("handler", handler) - theme = kwargs.pop("theme", theme) - custom_templates = kwargs.pop("custom_templates", custom_templates) - mdx = kwargs.pop("mdx", None) - mdx_config = kwargs.pop("mdx_config", None) - if handler: - if not self.name: - type(self).name = handler - warn( - "The `handler` argument is deprecated. The handler name must be specified as a class attribute.", - DeprecationWarning, - stacklevel=2, - ) - if not self.domain: - warn( - "The `domain` attribute must be specified as a class attribute.", - DeprecationWarning, - stacklevel=2, - ) - if mdx is None: - warn( - "The `mdx` argument must be provided (as a keyword argument).", - DeprecationWarning, - stacklevel=2, - ) - if mdx_config is None: - warn( - "The `mdx_config` argument must be provided (as a keyword argument).", - DeprecationWarning, - stacklevel=2, - ) - self.theme = theme """The selected theme.""" self.custom_templates = custom_templates @@ -436,8 +366,7 @@ def do_convert_markdown( treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph # type: ignore[attr-defined] if BacklinksTreeProcessor.name in treeprocessors: treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id # type: ignore[attr-defined] - - if autoref_hook: + if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns: self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # type: ignore[attr-defined] try: @@ -448,7 +377,8 @@ def do_convert_markdown( treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False # type: ignore[attr-defined] if BacklinksTreeProcessor.name in treeprocessors: treeprocessors[BacklinksTreeProcessor.name].initial_id = None # type: ignore[attr-defined] - self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None # type: ignore[attr-defined] + if AutorefsInlineProcessor.name in self.md.inlinePatterns: + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None # type: ignore[attr-defined] self.md.reset() _markdown_conversion_layer -= 1 @@ -533,20 +463,11 @@ def get_headings(self) -> Sequence[Element]: self._headings.clear() return result - # YORE: Bump 1: Replace `*args: Any, **kwargs: Any` with `config: Any`. - def update_env(self, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 + def update_env(self, config: Any) -> None: """Update the Jinja environment.""" - # YORE: Bump 1: Remove line. - warn("No need to call `super().update_env()` anymore.", DeprecationWarning, stacklevel=2) def _update_env(self, md: Markdown, *, config: Any | None = None) -> None: """Update our handler to point to our configured Markdown instance, grabbing some of the config from `md`.""" - # YORE: Bump 1: Remove block. - if self.mdx is None and config is not None: - self.mdx = config.get("mdx", None) or config.get("markdown_extensions", None) or () - if self.mdx_config is None and config is not None: - self.mdx_config = config.get("mdx_config", None) or config.get("mdx_configs", None) or {} - extensions: list[str | Extension] = [*self.mdx, MkdocstringsInnerExtension(self._headings)] new_md = Markdown(extensions=extensions, extension_configs=self.mdx_config) @@ -561,17 +482,7 @@ def _update_env(self, md: Markdown, *, config: Any | None = None) -> None: self.env.filters["highlight"] = Highlighter(new_md).highlight - # YORE: Bump 1: Replace block with `self.update_env(config)`. - parameters = inspect.signature(self.update_env).parameters - if "md" in parameters: - warn( - "The `update_env(md)` parameter is deprecated. Use `self.md` instead.", - DeprecationWarning, - stacklevel=1, - ) - self.update_env(new_md, config) - elif "config" in parameters: - self.update_env(config) + self.update_env(config) class Handlers: @@ -624,35 +535,6 @@ def __init__( self._inv_futures: dict[futures.Future, tuple[BaseHandler, str, Any]] = {} - # YORE: Bump 1: Remove block. - def get_anchors(self, identifier: str) -> tuple[str, ...]: - """Return the canonical HTML anchor for the identifier, if any of the seen handlers can collect it. - - Arguments: - identifier: The identifier (one that [collect][mkdocstrings.BaseHandler.collect] can accept). - - Returns: - A tuple of strings - anchors without '#', or an empty tuple if there isn't any identifier familiar with it. - """ - for handler in self._handlers.values(): - try: - if hasattr(handler, "get_anchors"): - warn( - "The `get_anchors` method is deprecated. " - "Declare a `get_aliases` method instead, accepting a string (identifier) " - "instead of a collected object.", - DeprecationWarning, - stacklevel=1, - ) - aliases = handler.get_anchors(handler.collect(identifier, getattr(handler, "fallback_config", {}))) - else: - aliases = handler.get_aliases(identifier) - except CollectionError: - continue - if aliases: - return aliases - return () - def get_handler_name(self, config: dict) -> str: """Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. @@ -696,34 +578,14 @@ def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHand handler_config = self._handlers_config.get(name, {}) module = importlib.import_module(f"mkdocstrings_handlers.{name}") - # YORE: Bump 1: Remove block. - kwargs = { - "theme": self._theme, - "custom_templates": self._custom_templates, - "mdx": self._mdx, - "mdx_config": self._mdx_config, - "handler_config": handler_config, - "tool_config": self._tool_config, - } - if "config_file_path" in inspect.signature(module.get_handler).parameters: - kwargs["config_file_path"] = self._tool_config.get("config_file_path") - warn( - "The `config_file_path` argument in `get_handler` functions is deprecated. " - "Use `tool_config.get('config_file_path')` instead.", - DeprecationWarning, - stacklevel=1, - ) - self._handlers[name] = module.get_handler(**kwargs) - - # YORE: Bump 1: Replace `# ` with `` within block. - # self._handlers[name] = module.get_handler( - # theme=self._theme, - # custom_templates=self._custom_templates, - # mdx=self._mdx, - # mdx_config=self._mdx_config, - # handler_config=handler_config, - # tool_config=self._tool_config, - # ) + self._handlers[name] = module.get_handler( + theme=self._theme, + custom_templates=self._custom_templates, + mdx=self._mdx, + mdx_config=self._mdx_config, + handler_config=handler_config, + tool_config=self._tool_config, + ) return self._handlers[name] def _download_inventories(self) -> None: diff --git a/src/mkdocstrings/_internal/loggers.py b/src/mkdocstrings/_internal/loggers.py index 6c6304c3..c67a7f4e 100644 --- a/src/mkdocstrings/_internal/loggers.py +++ b/src/mkdocstrings/_internal/loggers.py @@ -7,11 +7,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Callable -# YORE: Bump 1: Replace block with line 2. -try: - from jinja2 import pass_context -except ImportError: - from jinja2 import contextfunction as pass_context # type: ignore[attr-defined,no-redef] +from jinja2 import pass_context if TYPE_CHECKING: from collections.abc import MutableMapping, Sequence diff --git a/src/mkdocstrings/_internal/plugin.py b/src/mkdocstrings/_internal/plugin.py index 2af14a7a..2c27a60a 100644 --- a/src/mkdocstrings/_internal/plugin.py +++ b/src/mkdocstrings/_internal/plugin.py @@ -19,7 +19,6 @@ from inspect import signature from re import Match from typing import TYPE_CHECKING, Any -from warnings import catch_warnings, simplefilter from mkdocs.config import Config from mkdocs.config import config_options as opt @@ -166,10 +165,6 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: autorefs.scan_toc = False config.plugins["autorefs"] = autorefs _logger.debug("Added a subdued autorefs instance %r", autorefs) - # YORE: Bump 1: Remove block. - with catch_warnings(): - simplefilter("ignore", category=DeprecationWarning) - autorefs.get_fallback_anchor = handlers.get_anchors mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs) config.markdown_extensions.append(mkdocstrings_extension) # type: ignore[arg-type] diff --git a/src/mkdocstrings/extension.py b/src/mkdocstrings/extension.py deleted file mode 100644 index c7943652..00000000 --- a/src/mkdocstrings/extension.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal import extension - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.extension` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(extension, name) diff --git a/src/mkdocstrings/handlers/__init__.py b/src/mkdocstrings/handlers/__init__.py deleted file mode 100644 index b684324a..00000000 --- a/src/mkdocstrings/handlers/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py deleted file mode 100644 index c55a50ba..00000000 --- a/src/mkdocstrings/handlers/base.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal.handlers import base - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.handlers.base` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(base, name) diff --git a/src/mkdocstrings/handlers/rendering.py b/src/mkdocstrings/handlers/rendering.py deleted file mode 100644 index f3f04eea..00000000 --- a/src/mkdocstrings/handlers/rendering.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal.handlers import rendering - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.handlers.rendering` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(rendering, name) diff --git a/src/mkdocstrings/inventory.py b/src/mkdocstrings/inventory.py deleted file mode 100644 index 7192acff..00000000 --- a/src/mkdocstrings/inventory.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal import inventory - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.inventory` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(inventory, name) diff --git a/src/mkdocstrings/loggers.py b/src/mkdocstrings/loggers.py deleted file mode 100644 index 25545ca5..00000000 --- a/src/mkdocstrings/loggers.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal import loggers - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.loggers` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(loggers, name) diff --git a/src/mkdocstrings/plugin.py b/src/mkdocstrings/plugin.py deleted file mode 100644 index dbb6abf9..00000000 --- a/src/mkdocstrings/plugin.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal import plugin - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.plugin` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(plugin, name) diff --git a/tests/test_api.py b/tests/test_api.py index 57f0ce20..9821e65c 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -91,7 +91,7 @@ def _fixture_public_objects(public_api: griffe.Module) -> list[griffe.Object | g def _fixture_inventory() -> Inventory: inventory_file = Path(__file__).parent.parent / "site" / "objects.inv" if not inventory_file.exists(): - raise pytest.skip("The objects inventory is not available.") + pytest.skip("The objects inventory is not available.") # ty: ignore[call-non-callable] with inventory_file.open("rb") as file: return Inventory.parse_sphinx(file) @@ -137,7 +137,9 @@ def test_api_matches_inventory(inventory: Inventory, public_objects: list[griffe """All public objects are added to the inventory.""" ignore_names = {"__getattr__", "__init__", "__repr__", "__str__", "__post_init__"} not_in_inventory = [ - obj.path for obj in public_objects if obj.name not in ignore_names and obj.path not in inventory + f"{obj.relative_filepath}:{obj.lineno}: {obj.path}" + for obj in public_objects + if obj.name not in ignore_names and obj.path not in inventory ] msg = "Objects not in the inventory (try running `make run mkdocs build`):\n{paths}" assert not not_in_inventory, msg.format(paths="\n".join(sorted(not_in_inventory))) @@ -150,8 +152,6 @@ def test_inventory_matches_api( ) -> None: """The inventory doesn't contain any additional Python object.""" not_in_api = [] - # YORE: Bump 1: Remove line. - deprecated_modules = {"extension", "handlers", "inventory", "loggers", "plugin"} public_api_paths = {obj.path for obj in public_objects} public_api_paths.add("mkdocstrings") for item in inventory.values(): @@ -161,9 +161,6 @@ def test_inventory_matches_api( and (item.name == "mkdocstrings" or item.name.startswith("mkdocstrings.")) ): obj = loader.modules_collection[item.name] - # YORE: Bump 1: Remove block. - if any(obj.path.startswith(f"mkdocstrings.{module}") for module in deprecated_modules): - continue if obj.path not in public_api_paths and not any(path in public_api_paths for path in obj.aliases): not_in_api.append(item.name) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index acb9556c..833de692 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -41,7 +41,7 @@ def test_disabling_plugin(tmp_path: Path) -> None: mkdocs_config["plugins"].run_event("shutdown") # make sure the instruction was not processed - assert "::: mkdocstrings" in site_dir.joinpath("index.html").read_text() + assert "::: mkdocstrings" in site_dir.joinpath("index.html").read_text(encoding="utf8") def test_plugin_default_config(tmp_path: Path) -> None: