From b16e83a2169044702ebebabd544b12e58e3a5347 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 1 Sep 2025 14:00:14 +1000 Subject: [PATCH] Update Python dep and Cython requirements Update the minimum Python version to 3.9 and add official support for Python 3.14. Pin the Cython build requirement to support deterministic builds. Update the various CI/build dependencies to the latest versions. --- .github/workflows/ci.yml | 50 +++++++++++-------------------------- CHANGELOG.md | 5 +++- README.md | 2 +- build_helpers/lib.sh | 2 +- pyproject.toml | 23 +---------------- requirements-dev.txt | 6 ++--- setup.cfg | 5 ++-- src/krb5/_ccache.pyi | 26 +++++++++++-------- src/krb5/_creds.pyi | 7 ++++++ src/krb5/_creds_opt_mit.pyi | 4 +-- src/krb5/_keyblock.pyi | 1 + src/krb5/_kt.pyi | 6 +++++ src/krb5/_principal.pyi | 24 +++++++++++------- 13 files changed, 73 insertions(+), 88 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ab88c6..ca065ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,11 +25,11 @@ jobs: name: build sdist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.12 - name: build sdist run: | @@ -53,6 +53,12 @@ jobs: fail-fast: false matrix: include: + - os: macOS-13 + version: cp314-macosx_x86_64 + prerelease: true + - os: macOS-15 + version: cp314-macosx_arm64 + prerelease: true - os: macOS-13 version: cp313-macosx_x86_64 - os: macOS-15 @@ -73,13 +79,9 @@ jobs: version: cp39-macosx_x86_64 - os: macOS-15 version: cp39-macosx_arm64 - - os: macOS-13 - version: cp38-macosx_x86_64 - - os: macOS-15 - version: cp38-macosx_arm64 steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: artifact-sdist path: ./ @@ -92,43 +94,21 @@ jobs: rm krb5-*.tar.gz - name: build wheel - uses: pypa/cibuildwheel@v2.23.0 + uses: pypa/cibuildwheel@v3.1.4 env: CIBW_BUILD: ${{ matrix.version }} CIBW_BUILD_VERBOSITY: 1 + CIBW_PRERELEASE_PYTHONS: ${{ matrix.prerelease || 'false' }} - uses: actions/upload-artifact@v4 with: path: ./wheelhouse/*.whl name: artifact-wheel-${{ matrix.version }} - build_cython_0_29: - name: build cython 0.29.x - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: 3.9 - - - name: build cython 0.29 - run: | - sudo /bin/bash -c 'source ./build_helpers/lib.sh; lib::setup::system_requirements' - - python -m pip install build - echo 'Cython<3.0.0' > constraints.txt - PIP_CONSTRAINT=constraints.txt python -m build \ - --sdist \ - --wheel \ - --installer pip \ - --verbose - test: name: test needs: - build_sdist - - build_cython_0_29 - build_wheels runs-on: ${{ matrix.os }} @@ -139,12 +119,12 @@ jobs: - ubuntu-latest - macOS-latest python-version: - - 3.8 - 3.9 - '3.10' - '3.11' - '3.12' - '3.13' + - '3.14-dev' provider: - mit - heimdal @@ -154,13 +134,13 @@ jobs: provider: mit steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: pattern: artifact-* merge-multiple: true @@ -189,7 +169,7 @@ jobs: id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: pattern: artifact-* merge-multiple: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 56cb391..d89b459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog -## 0.8.0 - TBD +## 0.8.0 - 2025-09-01 + +* Require Python 3.9 or newer (dropped 3.8) +* Pin Cython build requirements to `3.1.3` for enabling deterministic builds per release ## 0.7.1 - 2025-03-06 diff --git a/README.md b/README.md index cc74fab..6abafc2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Due to the complex nature of this API it is highly recommended to use something * [MIT Kebreros](https://web.mit.edu/kerberos/) - Minimum 1.17 * [Heimdal](https://github.com/heimdal/heimdal) * A C compiler, such as GCC -* Python 3.6+ +* Python 3.9+ _Note: macOS includes their own implementation of Heimdal and a compiler isn't needed on that platform if installing from the wheel._ diff --git a/build_helpers/lib.sh b/build_helpers/lib.sh index 10d6bdf..676a9ed 100755 --- a/build_helpers/lib.sh +++ b/build_helpers/lib.sh @@ -54,7 +54,7 @@ lib::setup::python_requirements() { KRB5_VERSION="$( python -c "import build.util; print(build.util.project_wheel_metadata('.').get('Version'))" )" python -m pip install krb5=="${KRB5_VERSION}" \ - --find-links "file://${PWD}/dist" \ + --find-links dist \ --verbose echo "Installing dev dependencies" diff --git a/pyproject.toml b/pyproject.toml index 5378cf2..55a264e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "Cython >= 0.29.32, < 4.0.0", # 0.29.31 includes noexcept keyword support + "Cython == 3.1.3", "setuptools >= 42.0.0", # Supports license_files ] build-backend = "setuptools.build_meta" @@ -49,24 +49,3 @@ warn_unused_ignores = true [tool.pytest.ini_options] testpaths = "tests" addopts = "--import-mode=importlib" - -[tool.tox] -legacy_tox_ini = """ -[tox] -envlist = sanity,py36,py37,py38,py39,py310 -skip_missing_interpreters = true -isolated_build = True - -[testenv] -deps = - -r{toxinidir}/requirements-dev.txt - -commands = - python -m pytest -v - -[testenv:sanity] -commands = - python -m black . --check - python -m isort . --check-only - python -m mypy . -""" diff --git a/requirements-dev.txt b/requirements-dev.txt index 882211e..3b5cdb2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ -black==22.10.0 -isort==5.10.1 +black==25.1.0 +isort==6.0.1 k5test>=0.10.4 # Needed for MITRealm.start_kadmind env default -mypy==0.982 +mypy==1.17.1 pre-commit pytest tox diff --git a/setup.cfg b/setup.cfg index 939da79..94ce3fc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,21 +14,20 @@ keywords = kerberos classifiers = Development Status :: 4 - Beta - License :: OSI Approved :: MIT License Programming Language :: Python :: 3 - 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 [options] package_dir = =src packages = find: include_package_data = True -python_requires = >= 3.8 +python_requires = >= 3.9 [options.packages.find] where = src diff --git a/src/krb5/_ccache.pyi b/src/krb5/_ccache.pyi index dc08b07..5cbe2cb 100644 --- a/src/krb5/_ccache.pyi +++ b/src/krb5/_ccache.pyi @@ -11,17 +11,17 @@ from krb5._principal import Principal class CredentialsRetrieveFlags(enum.IntEnum): """Flags used to control :meth:`cc_retrieve_cred` and :meth:`cc_remove_cred`.""" - none: CredentialsRetrieveFlags = ... #: No matching flags set - match_times: CredentialsRetrieveFlags = ... #: The requested lifetime must be at least as great as specified - match_is_skey: CredentialsRetrieveFlags = ... #: The is_skey field must match exactly - match_flags: CredentialsRetrieveFlags = ... #: All the flags set in the match credentials must be set - match_times_exact: CredentialsRetrieveFlags = ... #: All the time fields must match exactly - match_flags_exact: CredentialsRetrieveFlags = ... #: All the flags must match exactly - match_authdata: CredentialsRetrieveFlags = ... #: The authorization data must match - match_srv_nameonly: CredentialsRetrieveFlags = ... #: Only the name portion of the principal name must match - match_2nd_tkt: CredentialsRetrieveFlags = ... #: The second ticket must match - match_keytype: CredentialsRetrieveFlags = ... #: The encryption key type must match - supported_ktypes: CredentialsRetrieveFlags = ... #: The supported key types must match + none = ... #: No matching flags set + match_times = ... #: The requested lifetime must be at least as great as specified + match_is_skey = ... #: The is_skey field must match exactly + match_flags = ... #: All the flags set in the match credentials must be set + match_times_exact = ... #: All the time fields must match exactly + match_flags_exact = ... #: All the flags must match exactly + match_authdata = ... #: The authorization data must match + match_srv_nameonly = ... #: Only the name portion of the principal name must match + match_2nd_tkt = ... #: The second ticket must match + match_keytype = ... #: The encryption key type must match + supported_ktypes = ... #: The supported key types must match class CCache: """Kerberos CCache @@ -34,15 +34,19 @@ class CCache: def __iter__(self) -> typing.Iterator[Creds]: """Iterate credentials in a ccache.""" + @property def addr(self) -> typing.Optional[int]: """The raw krb5_ccache pointer address of this credential cache.""" + @property def name(self) -> typing.Optional[bytes]: """The name/residual of the credential cache.""" + @property def principal(self) -> typing.Optional[Principal]: """Default client principal of the credential cache.""" + @property def cache_type(self) -> typing.Optional[bytes]: """The type of the credential cache.""" diff --git a/src/krb5/_creds.pyi b/src/krb5/_creds.pyi index d0840c3..31ae8e2 100644 --- a/src/krb5/_creds.pyi +++ b/src/krb5/_creds.pyi @@ -59,18 +59,23 @@ class Creds: @property def client(self) -> Principal: """Client's principal identifier.""" + @property def server(self) -> Principal: """Server's principal identifier.""" + @property def keyblock(self) -> KeyBlock: """Session encryption key info.""" + @property def times(self) -> TicketTimes: """Lifetime info.""" + @property def ticket_flags_raw(self) -> int: """Flags in ticket, as returned by libkrb5.""" + @property def ticket_flags(self) -> TicketFlags: """Flags in ticket, converted to a representation where the first flag is in the lowermost bit.""" @@ -80,6 +85,7 @@ class Creds: @property def ticket(self) -> bytes: """Ticket string itself.""" + @property def second_ticket(self) -> bytes: """second ticket, if related to ticket (via DUPLICATE-SKEY or ENC-TKT-IN-SKEY)""" @@ -127,6 +133,7 @@ class Krb5Prompt: num_prompts: Number of times :meth:`prompt` is to be called for this prompt session. """ + def prompt( self, msg: bytes, diff --git a/src/krb5/_creds_opt_mit.pyi b/src/krb5/_creds_opt_mit.pyi index 1691dca..7f3d97e 100644 --- a/src/krb5/_creds_opt_mit.pyi +++ b/src/krb5/_creds_opt_mit.pyi @@ -11,8 +11,8 @@ from krb5._creds_opt import GetInitCredsOpt class FastFlags(enum.IntEnum): """Flags used to control :meth:`get_init_creds_opt_set_fast_flags`.""" - none: FastFlags = ... #: No flags set. - required: FastFlags = ... #: Require KDC to support FAST. + none = ... #: No flags set. + required = ... #: Require KDC to support FAST. def get_init_creds_opt_set_fast_ccache( context: Context, diff --git a/src/krb5/_keyblock.pyi b/src/krb5/_keyblock.pyi index 4a2fcc3..b9b0d42 100644 --- a/src/krb5/_keyblock.pyi +++ b/src/krb5/_keyblock.pyi @@ -18,6 +18,7 @@ class KeyBlock: @property def data(self) -> bytes: """The keyblock data.""" + @property def enctype(self) -> int: """The keyblock encryption type.""" diff --git a/src/krb5/_kt.pyi b/src/krb5/_kt.pyi index 857fd20..355a77f 100644 --- a/src/krb5/_kt.pyi +++ b/src/krb5/_kt.pyi @@ -23,12 +23,15 @@ class KeyTab: being iterated does not exist. It may not be possible to add/remove entries on a keytab while it is being enumerated. """ + @property def addr(self) -> typing.Optional[int]: """The raw krb5_keytab pointer address of this credential cache.""" + @property def name(self) -> typing.Optional[bytes]: """The name/residual of the keytab.""" + @property def kt_type(self) -> typing.Optional[bytes]: """The type of the keytab.""" @@ -49,12 +52,15 @@ class KeyTabEntry: @property def key(self) -> KeyBlock: """The keytab key data block.""" + @property def kvno(self) -> int: """The key version number associated with the keytab entry.""" + @property def principal(self) -> Principal: """The principal associated with the keytab entry.""" + @property def timestamp(self) -> int: """The time creation entry of the keytab entry.""" diff --git a/src/krb5/_principal.pyi b/src/krb5/_principal.pyi index 1f81fed..d2e2bff 100644 --- a/src/krb5/_principal.pyi +++ b/src/krb5/_principal.pyi @@ -9,19 +9,19 @@ from krb5._context import Context class PrincipalParseFlags(enum.IntEnum): """Flags used to control :meth:`parse_name_flags`.""" - none: PrincipalParseFlags = ... #: No parse flags set - no_realm: PrincipalParseFlags = ... #: Error if realm is present - require_realm: PrincipalParseFlags = ... #: Error if realm is not present - enterprise: PrincipalParseFlags = ... #: Create single-component enterprise principal - ignore_realm: PrincipalParseFlags = ... #: Ignore realm if present + none = ... #: No parse flags set + no_realm = ... #: Error if realm is present + require_realm = ... #: Error if realm is not present + enterprise = ... #: Create single-component enterprise principal + ignore_realm = ... #: Ignore realm if present class PrincipalUnparseFlags(enum.IntEnum): """Flags used to control :meth:`unparse_name_flags`.""" - none: PrincipalUnparseFlags = ... #: No unparse flags set - short: PrincipalUnparseFlags = ... #: Omit realm if it is the local realm - no_realm: PrincipalUnparseFlags = ... #: Omit realm always - display: PrincipalUnparseFlags = ... #: Don't escape special characters + none = ... #: No unparse flags set + short = ... #: Omit realm if it is the local realm + no_realm = ... #: Omit realm always + display = ... #: Don't escape special characters class NameType(enum.IntEnum): """A kerberos principal name type""" @@ -51,21 +51,27 @@ class Principal: def __copy__(self) -> "Principal": """Create a copy of the principal object.""" + @property def addr(self) -> typing.Optional[int]: """The raw krb5_principal pointer address of this credential cache.""" + @property def name(self) -> typing.Optional[bytes]: """The name of the principal.""" + @property def realm(self) -> bytes: """The realm of the principal.""" + @property def components(self) -> typing.List[bytes]: """The list of components of the principal.""" + @property def type(self) -> NameType: """The name type of the principal.""" + @type.setter def type(self, value: NameType) -> None: pass