Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
554c9d210b | ||
|
|
da495b9bfd | ||
|
|
b2d793e9fe | ||
|
|
f00944b9e5 | ||
|
|
274102dcc8 | ||
|
|
8237f19ee5 | ||
|
|
5d243f8480 | ||
|
|
d7f2d798cd | ||
|
|
f56fb033e6 | ||
|
|
d6af39a00f | ||
|
|
5c24ec8836 | ||
|
|
9121bf7161 | ||
|
|
e29c384da2 | ||
|
|
b5a4c64446 | ||
|
|
b23c61e7a4 | ||
|
|
edaa01db15 | ||
|
|
bf51fee370 | ||
|
|
3cf14095c4 | ||
|
|
dde21d5dcd | ||
|
|
1651eaab84 | ||
|
|
cb38bdd94b | ||
|
|
96e4ac635e | ||
|
|
937f5e9aaa | ||
|
|
0c4c4632ba | ||
|
|
33cf4babc2 | ||
|
|
72b058a1d9 | ||
|
|
c78942f326 | ||
|
|
0ae76a8017 | ||
|
|
5427ba996a | ||
|
|
60695c9394 | ||
|
|
86d5f8475e | ||
|
|
fe4006da2b | ||
|
|
e28bc9431a | ||
|
|
47ef1e4bb7 | ||
|
|
71a5f0f84c | ||
|
|
16476222ca |
131
.github/CODE_OF_CONDUCT.md
vendored
131
.github/CODE_OF_CONDUCT.md
vendored
@ -1,133 +1,16 @@
|
||||
# Code of Conduct
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
While not being a [Python Software Foundation](https://www.python.org/psf-landing/) project, everyone interacting in this project is expected to follow the [PSF Code of Conduct](https://policies.python.org/python.org/code-of-conduct/).
|
||||
|
||||
## Our Pledge
|
||||
In general, this means that everyone is expected to be **open**, **considerate**, and **respectful** of others no matter what their position is within the project.
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socioeconomic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
<mailto:hs@ox.cx>.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
We take Code of Conduct violations seriously, and will act to ensure our spaces are welcoming, inclusive, and professional environments to communicate in.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
If you need to raise a Code of Conduct report, you may do so privately by email to [Hynek Schlawack](mailto:hs@ox.cx).
|
||||
|
||||
## Enforcement Guidelines
|
||||
Reports will be treated confidentially.
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
Alternately you can make a [report to the Python Software Foundation](https://policies.python.org/python.org/code-of-conduct/Procedures-for-Reporting-Incidents/).
|
||||
|
||||
7
.github/dependabot.yml
vendored
7
.github/dependabot.yml
vendored
@ -5,3 +5,10 @@ updates:
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
cooldown:
|
||||
# https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns
|
||||
default-days: 7
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
112
.github/workflows/ci.yml
vendored
112
.github/workflows/ci.yml
vendored
@ -22,12 +22,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- uses: hynek/build-and-inspect-python-package@v2
|
||||
- uses: hynek/build-and-inspect-python-package@fe0a0fb1925ca263d076ca4f2c13e93a6e92a33e # v2.17.0
|
||||
id: baipp
|
||||
|
||||
outputs:
|
||||
@ -52,18 +52,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download pre-built packages
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
- run: |
|
||||
tar xf dist/*.tar.gz --strip-components=1
|
||||
rm -rf src
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
allow-prereleases: true
|
||||
- uses: hynek/setup-cached-uv@v2
|
||||
- uses: hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
|
||||
|
||||
- name: Run tests
|
||||
run: >
|
||||
@ -72,7 +72,7 @@ jobs:
|
||||
-f py${PYTHON//./}-tests
|
||||
|
||||
- name: Upload coverage data
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: coverage-data-${{ matrix.python-version }}
|
||||
path: .coverage.*
|
||||
@ -85,6 +85,42 @@ jobs:
|
||||
--installpkg dist/*.whl
|
||||
-e py${PYTHON//./}-mypy
|
||||
|
||||
free-threading:
|
||||
name: Test free-threaded builds on ${{ matrix.python-version }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-package
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version:
|
||||
- 3.14t
|
||||
env:
|
||||
PYTHON: ${{ matrix.python-version }}
|
||||
|
||||
steps:
|
||||
- name: Download pre-built packages
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
- run: |
|
||||
tar xf dist/*.tar.gz --strip-components=1
|
||||
rm -rf src
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
allow-prereleases: true
|
||||
- uses: hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
uv venv --python $PYTHON
|
||||
# cffi 2 is required and currently beta.
|
||||
uv pip install --prerelease=allow dist/*.whl --group dev
|
||||
|
||||
.venv/bin/python -Im pytest tests
|
||||
|
||||
|
||||
coverage:
|
||||
name: Ensure 100% test coverage
|
||||
@ -93,16 +129,16 @@ jobs:
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version-file: .python-version-default
|
||||
- uses: hynek/setup-cached-uv@v2
|
||||
- uses: hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
|
||||
|
||||
- name: Download coverage data
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: coverage-data-*
|
||||
merge-multiple: true
|
||||
@ -121,7 +157,7 @@ jobs:
|
||||
coverage report --fail-under=100
|
||||
|
||||
- name: Upload HTML report if check failed.
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: html-report
|
||||
path: htmlcov
|
||||
@ -135,12 +171,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download pre-built packages
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
- run: tar xf dist/*.tar.gz --strip-components=1
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version-file: .python-version-default
|
||||
|
||||
@ -153,51 +189,24 @@ jobs:
|
||||
|
||||
- run: python -Im tox run -e system-argon2
|
||||
|
||||
|
||||
mypy-pkg:
|
||||
name: Mypy Codebase
|
||||
typing:
|
||||
name: Check types using supported type checkers
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-package
|
||||
|
||||
steps:
|
||||
- name: Download pre-built packages
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
- run: tar xf dist/*.tar.gz --strip-components=1
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version-file: .python-version-default
|
||||
- uses: hynek/setup-cached-uv@v2
|
||||
|
||||
- run: >
|
||||
uvx --with tox-uv
|
||||
tox run -e mypy-pkg
|
||||
|
||||
pyright:
|
||||
name: Pyright Codebase
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-package
|
||||
|
||||
steps:
|
||||
- name: Download pre-built packages
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
- run: tar xf dist/*.tar.gz --strip-components=1
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version-file: .python-version-default
|
||||
- uses: hynek/setup-cached-uv@v2
|
||||
|
||||
- run: |
|
||||
uv venv
|
||||
uv pip install . --group typing
|
||||
echo "$PWD/.venv/bin" >> $GITHUB_PATH
|
||||
- uses: jakebailey/pyright-action@b5d50e5cde6547546a5c4ac92e416a8c2c1a1dfe # v2.3.2
|
||||
- uses: hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
|
||||
|
||||
- run: uvx --with tox-uv tox run -f typing
|
||||
|
||||
docs:
|
||||
name: Run doctests
|
||||
@ -205,12 +214,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download pre-built packages
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
- run: tar xf dist/*.tar.gz --strip-components=1
|
||||
- uses: hynek/setup-cached-uv@v2
|
||||
- uses: hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
|
||||
|
||||
- run: >
|
||||
uvx --with tox-uv
|
||||
@ -225,10 +234,10 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version-file: .python-version-default
|
||||
|
||||
@ -244,8 +253,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- coverage
|
||||
- mypy-pkg
|
||||
- pyright
|
||||
- typing
|
||||
- docs
|
||||
- install-dev
|
||||
- system-package
|
||||
|
||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@ -24,17 +24,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
||||
|
||||
12
.github/workflows/pypi-package.yml
vendored
12
.github/workflows/pypi-package.yml
vendored
@ -21,12 +21,12 @@ jobs:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- uses: hynek/build-and-inspect-python-package@v2
|
||||
- uses: hynek/build-and-inspect-python-package@fe0a0fb1925ca263d076ca4f2c13e93a6e92a33e # v2.17.0
|
||||
with:
|
||||
attest-build-provenance-github: 'true'
|
||||
|
||||
@ -44,13 +44,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download packages built by build-and-inspect-python-package
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
|
||||
- name: Upload package to Test PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
|
||||
@ -68,10 +68,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download packages built by build-and-inspect-python-package
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: Packages
|
||||
path: dist
|
||||
|
||||
- name: Upload package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
|
||||
6
.github/workflows/zizmor.yml
vendored
6
.github/workflows/zizmor.yml
vendored
@ -19,10 +19,10 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: hynek/setup-cached-uv@v2
|
||||
- uses: hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
run: uvx zizmor --format sarif . > results.sarif
|
||||
@ -30,7 +30,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
||||
with:
|
||||
# Path to SARIF file relative to the root of the repository
|
||||
sarif_file: results.sarif
|
||||
|
||||
@ -4,7 +4,7 @@ ci:
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.11.12
|
||||
rev: v0.15.12
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
@ -17,12 +17,12 @@ repos:
|
||||
args: [tests]
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.4.1
|
||||
rev: v2.4.2
|
||||
hooks:
|
||||
- id: codespell
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
|
||||
@ -13,6 +13,9 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/
|
||||
<!-- changelog follows -->
|
||||
|
||||
|
||||
## [Unreleased](https://github.com/hynek/argon2-cffi/compare/25.1.0...HEAD)
|
||||
|
||||
|
||||
## [25.1.0](https://github.com/hynek/argon2-cffi/compare/23.1.0...25.1.0) - 2025-06-03
|
||||
|
||||
### Added
|
||||
@ -23,7 +26,7 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/
|
||||
|
||||
### Removed
|
||||
|
||||
- Python 3.7 is not supported anymore.
|
||||
- Python 3.7 and 3.8 are not supported anymore.
|
||||
[#186](https://github.com/hynek/argon2-cffi/pull/186)
|
||||
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ name = "argon2-cffi"
|
||||
description = "Argon2 for Python"
|
||||
authors = [{ name = "Hynek Schlawack", email = "hs@ox.cx" }]
|
||||
dynamic = ["version", "readme"]
|
||||
requires-python = ">=3.8"
|
||||
requires-python = ">=3.9"
|
||||
license = "MIT"
|
||||
license-files = ["LICENSE"]
|
||||
keywords = ["password", "hash", "hashing", "security"]
|
||||
@ -23,13 +23,13 @@ classifiers = [
|
||||
"Operating System :: MacOS :: MacOS X",
|
||||
"Operating System :: Microsoft :: Windows",
|
||||
"Operating System :: POSIX",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: Free Threading",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Topic :: Security :: Cryptography",
|
||||
@ -117,23 +117,6 @@ source = ["src", ".tox/py*/**/site-packages"]
|
||||
[tool.coverage.report]
|
||||
show_missing = true
|
||||
skip_covered = true
|
||||
exclude_lines = [
|
||||
# a more strict default pragma
|
||||
"\\# pragma: no cover\\b",
|
||||
|
||||
# allow defensive code
|
||||
"^\\s*raise AssertionError\\b",
|
||||
"^\\s*raise NotImplementedError\\b",
|
||||
"^\\s*return NotImplemented\\b",
|
||||
"^\\s*raise$",
|
||||
|
||||
# typing-related code
|
||||
"^if (False|TYPE_CHECKING):",
|
||||
": \\.\\.\\.(\\s*#.*)?$",
|
||||
"^ +\\.\\.\\.$",
|
||||
"-> ['\"]?NoReturn['\"]?:",
|
||||
]
|
||||
omit = []
|
||||
|
||||
|
||||
[tool.interrogate]
|
||||
@ -156,7 +139,6 @@ enable_error_code = ["ignore-without-code"]
|
||||
|
||||
ignore_missing_imports = true
|
||||
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "tests.*"
|
||||
ignore_errors = true
|
||||
@ -182,6 +164,7 @@ ignore = [
|
||||
"FIX", # Yes, we want XXX as a marker.
|
||||
"INP001", # sometimes we want Python files outside of packages
|
||||
"ISC001", # conflicts with ruff format
|
||||
"PLC0415", # sometimes, imports must live elsewhere
|
||||
"PLR0913", # yes, many arguments, but most have defaults
|
||||
"PLR2004", # numbers are sometimes fine
|
||||
"PLW2901", # re-assigning within loop bodies is fine
|
||||
@ -203,7 +186,6 @@ ignore = [
|
||||
"EM101", # no need for exception msg hygiene in tests
|
||||
]
|
||||
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
lines-between-types = 1
|
||||
lines-after-imports = 2
|
||||
|
||||
@ -6,7 +6,6 @@ import platform
|
||||
import sys
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from .exceptions import InvalidHashError, UnsupportedParametersError
|
||||
from .low_level import Type
|
||||
@ -15,7 +14,7 @@ from .low_level import Type
|
||||
NoneType = type(None)
|
||||
|
||||
|
||||
def _check_types(**kw: Any) -> str | None:
|
||||
def _check_types(**kw: tuple[object, type | tuple[type, ...]]) -> str | None:
|
||||
"""
|
||||
Check each ``name: (value, types)`` in *kw*.
|
||||
|
||||
@ -25,11 +24,11 @@ def _check_types(**kw: Any) -> str | None:
|
||||
for name, (value, types) in kw.items():
|
||||
if not isinstance(value, types):
|
||||
if isinstance(types, tuple):
|
||||
types = ", or ".join(t.__name__ for t in types)
|
||||
type_names = ", or ".join(t.__name__ for t in types)
|
||||
else:
|
||||
types = types.__name__
|
||||
type_names = types.__name__
|
||||
errors.append(
|
||||
f"'{name}' must be a {types} (got {type(value).__name__})"
|
||||
f"'{name}' must be a {type_names} (got {type(value).__name__})"
|
||||
)
|
||||
|
||||
if errors != []:
|
||||
|
||||
@ -125,18 +125,20 @@ class TestHash:
|
||||
"""
|
||||
Passing an argument of wrong type raises TypeError.
|
||||
"""
|
||||
with pytest.deprecated_call(
|
||||
match="argon2.hash_password is deprecated"
|
||||
), pytest.raises(TypeError):
|
||||
with (
|
||||
pytest.deprecated_call(match="argon2.hash_password is deprecated"),
|
||||
pytest.raises(TypeError),
|
||||
):
|
||||
hash_password("oh no, unicode!")
|
||||
|
||||
def test_illegal_argon2_parameter(self):
|
||||
"""
|
||||
Raises HashingError if hashing fails.
|
||||
"""
|
||||
with pytest.deprecated_call(
|
||||
match="argon2.hash_password is deprecated"
|
||||
), pytest.raises(HashingError):
|
||||
with (
|
||||
pytest.deprecated_call(match="argon2.hash_password is deprecated"),
|
||||
pytest.raises(HashingError),
|
||||
):
|
||||
hash_password(TEST_PASSWORD, memory_cost=1)
|
||||
|
||||
@given(st.binary(max_size=128))
|
||||
@ -174,16 +176,22 @@ class TestVerify:
|
||||
"""
|
||||
Given a valid hash and password and wrong type, we fail.
|
||||
"""
|
||||
with pytest.deprecated_call(
|
||||
match="argon2.verify_password is deprecated"
|
||||
), pytest.raises(VerificationError):
|
||||
with (
|
||||
pytest.deprecated_call(
|
||||
match="argon2.verify_password is deprecated"
|
||||
),
|
||||
pytest.raises(VerificationError),
|
||||
):
|
||||
verify_password(TEST_HASH_I, TEST_PASSWORD, Type.D)
|
||||
|
||||
def test_wrong_arg_type(self):
|
||||
"""
|
||||
Passing an argument of wrong type raises TypeError.
|
||||
"""
|
||||
with pytest.deprecated_call(
|
||||
match="argon2.verify_password is deprecated"
|
||||
), pytest.raises(TypeError):
|
||||
with (
|
||||
pytest.deprecated_call(
|
||||
match="argon2.verify_password is deprecated"
|
||||
),
|
||||
pytest.raises(TypeError),
|
||||
):
|
||||
verify_password(TEST_HASH_I, TEST_PASSWORD.decode("ascii"))
|
||||
|
||||
@ -63,13 +63,13 @@ class TestPasswordHasher:
|
||||
assert isinstance(h, str)
|
||||
assert h[: len(prefix)] == prefix
|
||||
|
||||
def test_custom_salt(self, password=b"password"):
|
||||
def test_custom_salt(self):
|
||||
"""
|
||||
A custom salt can be specified.
|
||||
"""
|
||||
ph = PasswordHasher.from_parameters(profiles.CHEAPEST)
|
||||
|
||||
h = ph.hash(password, salt=b"1234567890123456")
|
||||
h = ph.hash(b"password", salt=b"1234567890123456")
|
||||
|
||||
assert h == (
|
||||
"$argon2id$v=19$m=8,t=1,p=1$MTIzNDU2Nzg5MDEyMzQ1Ng$maTa5w"
|
||||
@ -173,7 +173,7 @@ class TestPasswordHasher:
|
||||
with mock.patch("platform.machine", return_value=machine):
|
||||
with pytest.raises(
|
||||
UnsupportedParametersError,
|
||||
match="In WebAssembly environments `parallelism` must be 1.",
|
||||
match="In WebAssembly environments `parallelism` must be 1",
|
||||
):
|
||||
PasswordHasher(parallelism=2)
|
||||
|
||||
@ -181,7 +181,7 @@ class TestPasswordHasher:
|
||||
params = Parameters(Type.I, 2, 8, 8, 3, 256, 8)
|
||||
with pytest.raises(
|
||||
UnsupportedParametersError,
|
||||
match="In WebAssembly environments `parallelism` must be 1.",
|
||||
match="In WebAssembly environments `parallelism` must be 1",
|
||||
):
|
||||
ph = PasswordHasher.from_parameters(params)
|
||||
|
||||
|
||||
72
tox.ini
72
tox.ini
@ -1,14 +1,14 @@
|
||||
[tox]
|
||||
min_version = 4
|
||||
min_version = 4.25
|
||||
env_list =
|
||||
pre-commit,
|
||||
mypy-pkg,
|
||||
py3{8,9,10,11,12,13,14}-{tests,mypy}
|
||||
py312-bindings-main,
|
||||
py3{9-14}-{tests,mypy},
|
||||
py314t-tests,
|
||||
py314-tests-{bindings-main,system-argon2},
|
||||
pypy3-tests,
|
||||
system-argon2,
|
||||
typing-{pyright,ty,pyrefly,mypy}
|
||||
docs-doctests,
|
||||
coverage-report
|
||||
coverage-{combine,report}
|
||||
|
||||
|
||||
[testenv]
|
||||
@ -27,33 +27,41 @@ commands =
|
||||
mypy: mypy tests/typing
|
||||
|
||||
|
||||
[testenv:py3{8,13}-tests]
|
||||
[testenv:py3{9,14}-tests]
|
||||
# Keep coverage-combine's depends with the versions.
|
||||
description = Run tests and measure coverage.
|
||||
deps =
|
||||
coverage[toml]
|
||||
deps = coverage[toml]
|
||||
commands =
|
||||
coverage run -m pytest {posargs}
|
||||
coverage run -m argon2 -n 1 -t 1 -m 8 -p 1
|
||||
coverage run -m argon2 --profile CHEAPEST
|
||||
|
||||
|
||||
[testenv:coverage-report]
|
||||
description = Report coverage over all test runs.
|
||||
# Split combine/report in 2 to avoid excessive "Combined data file ..." output.
|
||||
[testenv:coverage-combine]
|
||||
# Keep base_python in-sync with .python-version-default
|
||||
base_python = py313
|
||||
# Keep in-sync with test env definition above.
|
||||
depends = py3{9,14}-tests
|
||||
skip_install = true
|
||||
depends = py3{8,13}-tests
|
||||
deps = coverage[toml]
|
||||
parallel_show_output = true
|
||||
commands =
|
||||
coverage combine
|
||||
coverage report
|
||||
deps = coverage
|
||||
commands = coverage combine
|
||||
|
||||
[testenv:coverage-report]
|
||||
description = Report coverage over oldest and latest supported Python
|
||||
# Keep base_python in-sync with .python-version-default
|
||||
base_python = py313
|
||||
skip_install = true
|
||||
depends = coverage-combine
|
||||
deps = coverage
|
||||
parallel_show_output = true
|
||||
commands = coverage report
|
||||
|
||||
[testenv:system-argon2]
|
||||
description = Run tests against bindings that use a system installation of Argon2.
|
||||
set_env = ARGON2_CFFI_USE_SYSTEM=1
|
||||
install_command = pip install {opts} --no-binary=argon2-cffi-bindings {packages}
|
||||
|
||||
|
||||
[testenv:py312-bindings-main]
|
||||
description = Run tests against the current main branch of argon2-cffi-bindings
|
||||
dependency_groups =
|
||||
@ -69,16 +77,31 @@ deps = pre-commit-uv
|
||||
commands = pre-commit run --all-files
|
||||
|
||||
|
||||
[testenv:pyright]
|
||||
[testenv:typing-mypy]
|
||||
description = Check own code with Mypyy.
|
||||
# Keep base_python in-sync with .python-version-default
|
||||
base_python = py313
|
||||
deps = mypy
|
||||
dependency_groups = typing
|
||||
commands = mypy src
|
||||
|
||||
[testenv:typing-pyright]
|
||||
description = Check API and own code with Pyright
|
||||
deps = pyright
|
||||
dependency_groups = typing
|
||||
commands = pyright tests/typing src
|
||||
commands = pyright src tests/typing
|
||||
|
||||
[testenv:typing-ty]
|
||||
description = Check API with ty
|
||||
deps = ty
|
||||
dependency_groups = typing
|
||||
commands = ty check src tests/typing
|
||||
|
||||
[testenv:mypy-pkg]
|
||||
description = Check own code.
|
||||
deps = mypy
|
||||
commands = mypy src
|
||||
[testenv:typing-pyrefly]
|
||||
description = Check API with pyrefly
|
||||
deps = pyrefly
|
||||
dependency_groups = typing
|
||||
commands = pyrefly check src tests/typing
|
||||
|
||||
|
||||
[testenv:docs-{build,doctests,linkcheck}]
|
||||
@ -91,7 +114,6 @@ commands =
|
||||
doctests: sphinx-build -n -T -W -b doctest -d {envtmpdir}/doctrees docs {posargs:docs/_build/}html
|
||||
linkcheck: sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/html
|
||||
|
||||
|
||||
[testenv:docs-watch]
|
||||
package = editable
|
||||
base_python = {[testenv:docs-build]base_python}
|
||||
|
||||
10
zizmor.yml
10
zizmor.yml
@ -1,10 +0,0 @@
|
||||
---
|
||||
rules:
|
||||
unpinned-uses:
|
||||
config:
|
||||
policies:
|
||||
# We trust GitHub, the PyPA, and ourselves.
|
||||
"actions/*": ref-pin
|
||||
"github/*": ref-pin
|
||||
"pypa/*": ref-pin
|
||||
"hynek/*": ref-pin
|
||||
Loading…
Reference in New Issue
Block a user