Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-service_identity for 
openSUSE:Factory checked in at 2024-11-01 21:00:44
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-service_identity (Old)
 and      /work/SRC/openSUSE:Factory/.python-service_identity.new.2020 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-service_identity"

Fri Nov  1 21:00:44 2024 rev:16 rq:1219726 version:24.2.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-service_identity/python-service_identity.changes
  2024-01-21 23:07:26.671511362 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-service_identity.new.2020/python-service_identity.changes
        2024-11-01 21:00:48.745507595 +0100
@@ -1,0 +2,11 @@
+Wed Oct 30 19:54:23 UTC 2024 - Dirk Müller <[email protected]>
+
+- update to 24.2.0:
+  * Python 3.13 is now officially supported.
+  * pyOpenSSL's identity extraction has been reimplemented using
+    *cryptography*'s primitives instead of deprecated pyOpenSSL
+    APIs.
+  * As a result, the oldest supported pyOpenSSL version is now
+    17.1.0.
+
+-------------------------------------------------------------------

Old:
----
  24.1.0.tar.gz

New:
----
  24.2.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-service_identity.spec ++++++
--- /var/tmp/diff_new_pack.89EqKV/_old  2024-11-01 21:00:49.505539320 +0100
+++ /var/tmp/diff_new_pack.89EqKV/_new  2024-11-01 21:00:49.509539487 +0100
@@ -19,7 +19,7 @@
 %define oname   service_identity
 %{?sle15_python_module_pythons}
 Name:           python-service_identity
-Version:        24.1.0
+Version:        24.2.0
 Release:        0
 Summary:        Service identity verification for pyOpenSSL
 License:        MIT
@@ -34,7 +34,7 @@
 BuildRequires:  %{python_module hatchling >= 1.14.0}
 BuildRequires:  %{python_module idna}
 BuildRequires:  %{python_module pip}
-BuildRequires:  %{python_module pyOpenSSL >= 17.0.0}
+BuildRequires:  %{python_module pyOpenSSL >= 17.1.0}
 BuildRequires:  %{python_module pyasn1-modules}
 BuildRequires:  %{python_module pyasn1}
 BuildRequires:  %{python_module pytest}
@@ -46,7 +46,7 @@
 Requires:       python-pyasn1
 Requires:       python-pyasn1-modules
 Recommends:     python-idna
-Recommends:     python-pyOpenSSL
+Recommends:     python-pyOpenSSL >= 17.1.0
 BuildArch:      noarch
 %ifpython2
 Requires:       python-ipaddress

++++++ 24.1.0.tar.gz -> 24.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/.git_archival.txt 
new/service-identity-24.2.0/.git_archival.txt
--- old/service-identity-24.1.0/.git_archival.txt       2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/.git_archival.txt       2024-10-26 
09:10:38.000000000 +0200
@@ -1,4 +1,3 @@
-node: e5ba15ec13b1750cae35004dede2e6e4af74308f
-node-date: 2024-01-14T08:05:19+01:00
-describe-name: 24.1.0
-ref-names: tag: 24.1.0
+node: b51873e5aff777ff2e35dfb2bee1f4acbf203b25
+node-date: 2024-10-26T09:10:38+02:00
+describe-name: 24.2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/.github/CONTRIBUTING.md 
new/service-identity-24.2.0/.github/CONTRIBUTING.md
--- old/service-identity-24.1.0/.github/CONTRIBUTING.md 2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/.github/CONTRIBUTING.md 2024-10-26 
09:10:38.000000000 +0200
@@ -137,8 +137,10 @@
           A very important return value.
       """
   ```
+
 - If you add or change public APIs, tag the docstring using `..  
versionadded:: 16.0.0 WHAT` or `..  versionchanged:: 16.2.0 WHAT`.
-- We use [Ruff](https://ruff.rs/) to sort our imports, and we use 
[Black](https://github.com/psf/black) with line length of 79 characters to 
format our code.
+
+- We follow [PEP 8](https://peps.python.org/pep-0008/) as enforced by 
[Ruff](https://ruff.rs/) with a line length of 79 characters.
   As long as you run our full [*tox*] suite before committing, or install our 
[*pre-commit*] hooks (ideally you'll do both – see [*Local Development 
Environment*](#local-development-environment) above), you won't have to spend 
any time on formatting your code at all.
   If you don't, [CI] will catch it for you – but that seems like a waste of 
your time!
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/.github/SECURITY.md 
new/service-identity-24.2.0/.github/SECURITY.md
--- old/service-identity-24.1.0/.github/SECURITY.md     2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/.github/SECURITY.md     2024-10-26 
09:10:38.000000000 +0200
@@ -2,7 +2,7 @@
 
 ## Supported Versions
 
-We are following [*CalVer*](https://calver.org) with generous 
backwards-compatibility guarantees.
+We are following [Calendar Versioning](https://calver.org) with generous 
backwards-compatibility guarantees.
 *Therefore we only support the latest version*.
 
 That said, you shouldn't be afraid to upgrade if you're only using our 
documented public APIs and pay attention to `DeprecationWarning`s.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/.github/workflows/ci.yml 
new/service-identity-24.2.0/.github/workflows/ci.yml
--- old/service-identity-24.1.0/.github/workflows/ci.yml        2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/.github/workflows/ci.yml        2024-10-26 
09:10:38.000000000 +0200
@@ -4,6 +4,7 @@
 on:
   push:
     branches: [main]
+    tags: ["*"]
   pull_request:
   workflow_dispatch:
 
@@ -14,6 +15,7 @@
 
 permissions: {}
 
+
 jobs:
   lint:
     name: Run linters
@@ -22,61 +24,81 @@
       - uses: actions/checkout@v4
       - uses: actions/setup-python@v5
         with:
-          python-version: "3.11"  # XXX: change once interrogate works on 3.12
-          cache: pip
+          python-version-file: .python-version-default
+      - uses: hynek/setup-cached-uv@v2
+
+      - run: >
+          uvx --with tox-uv
+          tox run -e lint -- --show-diff-on-failure
+
+
+  build-package:
+    name: Build & verify package
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+
+      - uses: hynek/build-and-inspect-python-package@v2
+        id: baipp
+
+    outputs:
+      # Used to define the matrix for tests below. The value is based on
+      # packaging metadata (trove classifiers).
+      python-versions: ${{ 
steps.baipp.outputs.supported_python_classifiers_json_array }}
 
-      - name: Install & run tox
-        run: |
-          python -Im pip install tox
-          python -Im tox run -e lint -- --show-diff-on-failure
 
   tests:
-    name: Tests on ${{ matrix.python-version }}
+    name: Tests & Mypy API on ${{ matrix.python-version }}
     runs-on: ubuntu-latest
+    needs: build-package
     strategy:
       matrix:
-        python-version:
-          - "3.8"
-          - "3.9"
-          - "3.10"
-          - "3.11"
-          - "3.12"
-          - "pypy-3.8"
-          - "pypy-3.9"
+        # Created by the build-and-inspect-python-package action above.
+        python-version: ${{ 
fromJson(needs.build-package.outputs.python-versions) }}
 
     steps:
-      - uses: actions/checkout@v4
+      - name: Download pre-built packages
+        uses: actions/download-artifact@v4
+        with:
+          name: Packages
+          path: dist
+      - run: |
+          tar xf dist/*.tar.gz --strip-components=1
+          rm -rf src
       - uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
           allow-prereleases: true
-          cache: pip
-
-      - name: Prepare tox & run tests
-        run: |
-          V=${{ matrix.python-version }}
+      - uses: hynek/setup-cached-uv@v2
 
-          if [[ "$V" = pypy-* ]]; then
-            V=pypy3
-          else
-            V=py$(echo $V | tr -d .)
-          fi
-
-          python -Im pip install tox
-          python -Im tox run -f "$V"
-
-      - name: Run Mypy on API
-        run: python -Im tox run -e mypy-api
+      - name: Run tests
+        run: >
+          uvx --with tox-uv
+          tox run
+          --installpkg dist/*.whl
+          -f py$(echo ${{ matrix.python-version }} | tr -d .)
 
       - name: Upload coverage data
         uses: actions/upload-artifact@v4
         with:
           name: coverage-data-${{ matrix.python-version }}
           path: .coverage.*
+          include-hidden-files: true
           if-no-files-found: ignore
 
+      - name: Check public API with Mypy
+        run: >
+          uvx --with tox-uv
+          tox run
+          --installpkg dist/*.whl
+          -e mypy-api
+
+
   coverage:
-    name: Combine & check coverage
+    name: Ensure 100% test coverage
     needs: tests
     runs-on: ubuntu-latest
 
@@ -85,7 +107,7 @@
       - uses: actions/setup-python@v5
         with:
           python-version-file: .python-version-default
-          cache: pip
+      - uses: hynek/setup-cached-uv@v2
 
       - uses: actions/download-artifact@v4
         with:
@@ -94,16 +116,16 @@
 
       - name: Combine coverage & fail if it's <100%
         run: |
-          python -Im pip install coverage[toml]
+          uv tool install coverage[toml]
 
-          python -Im coverage combine
-          python -Im coverage html --skip-covered --skip-empty
+          coverage combine
+          coverage html --skip-covered --skip-empty
 
           # Report and write to summary.
-          python -Im coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
+          coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
 
           # Report again and fail if under 100%.
-          python -Im coverage report --fail-under=100
+          coverage report --fail-under=100
 
       - name: Upload HTML report if check failed.
         uses: actions/upload-artifact@v4
@@ -112,21 +134,28 @@
           path: htmlcov
         if: ${{ failure() }}
 
+
   mypy-pkg:
-    name: Type-check package
+    name: Mypy Codebase
     runs-on: ubuntu-latest
+    needs: build-package
 
     steps:
-      - uses: actions/checkout@v4
+      - 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
-          cache: pip
+      - uses: hynek/setup-cached-uv@v2
+
+      - run: >
+          uvx --with tox-uv
+          tox run -e mypy-pkg
 
-      - name: Install & run tox
-        run: |
-          python -Im pip install tox
-          python -Im tox run -e mypy-pkg
 
   install-dev:
     strategy:
@@ -147,25 +176,35 @@
         run: |
           python -Im pip install -e .[dev]
           python -Ic 'import service_identity; 
print(service_identity.__version__)'
+          python -Ic 'import service_identity.pyopenssl'
+          python -Ic 'import service_identity.cryptography'
+
 
   docs:
     name: Build docs & run doctests
+    needs: build-package
     runs-on: ubuntu-latest
+
     steps:
-      - uses: actions/checkout@v4
+      - 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:
           # Keep in sync with tox.ini/docs & .readthedocs.yaml
           python-version: "3.12"
-          cache: pip
+      - uses: hynek/setup-cached-uv@v2
+
+      - run: >
+          uvx --with tox-uv
+          tox run -e docs
 
-      - name: Install & run tox
-        run: |
-          python -Im pip install tox
-          python -Im tox run -e docs
 
-  # Ensure everything required is passing for branch protection.
   required-checks-pass:
+    name: Ensure everything required is passing for branch protection
     if: always()
 
     needs:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-24.1.0/.github/workflows/pypi-package.yml 
new/service-identity-24.2.0/.github/workflows/pypi-package.yml
--- old/service-identity-24.1.0/.github/workflows/pypi-package.yml      
2024-01-14 08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/.github/workflows/pypi-package.yml      
2024-10-26 09:10:38.000000000 +0200
@@ -5,13 +5,13 @@
   push:
     branches: [main]
     tags: ["*"]
-  pull_request:
   release:
     types:
       - published
   workflow_dispatch:
 
 permissions:
+  attestations: write
   contents: read
   id-token: write
 
@@ -27,6 +27,8 @@
           fetch-depth: 0
 
       - uses: hynek/build-and-inspect-python-package@v2
+        with:
+          attest-build-provenance-github: 'true'
 
   # Upload to Test PyPI on every commit on main.
   release-test-pypi:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/.pre-commit-config.yaml 
new/service-identity-24.2.0/.pre-commit-config.yaml
--- old/service-identity-24.1.0/.pre-commit-config.yaml 2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/.pre-commit-config.yaml 2024-10-26 
09:10:38.000000000 +0200
@@ -1,31 +1,26 @@
 ---
 repos:
-  - repo: https://github.com/psf/black
-    rev: 23.12.1
-    hooks:
-      - id: black
-
   - repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.1.13
+    rev: v0.7.1
     hooks:
       - id: ruff
         args: [--fix, --exit-non-zero-on-fix]
+      - id: ruff-format
 
   - repo: https://github.com/codespell-project/codespell
-    rev: v2.2.6
+    rev: v2.3.0
     hooks:
       - id: codespell
         args: [-L, fo]
 
   - repo: https://github.com/econchick/interrogate
-    rev: 1.5.0
+    rev: 1.7.0
     hooks:
       - id: interrogate
-        language_version: python3.11
-        args: [tests, -v]
+        args: [tests]
 
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.5.0
+    rev: v5.0.0
     hooks:
       - id: trailing-whitespace
       - id: end-of-file-fixer
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/.python-version-default 
new/service-identity-24.2.0/.python-version-default
--- old/service-identity-24.1.0/.python-version-default 2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/.python-version-default 2024-10-26 
09:10:38.000000000 +0200
@@ -1 +1 @@
-3.12
+3.13
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/.readthedocs.yaml 
new/service-identity-24.2.0/.readthedocs.yaml
--- old/service-identity-24.1.0/.readthedocs.yaml       2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/.readthedocs.yaml       2024-10-26 
09:10:38.000000000 +0200
@@ -2,10 +2,14 @@
 version: 2
 
 build:
-  os: ubuntu-22.04
+  os: ubuntu-lts-latest
   tools:
     # Keep in-sync with tox.ini/docs and ci.yml/docs
     python: "3.12"
+  jobs:
+    # Need the tags to calculate the version (sometimes).
+    post_checkout:
+      - git fetch --tags
 
 python:
   install:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/CHANGELOG.md 
new/service-identity-24.2.0/CHANGELOG.md
--- old/service-identity-24.1.0/CHANGELOG.md    2024-01-14 08:05:19.000000000 
+0100
+++ new/service-identity-24.2.0/CHANGELOG.md    2024-10-26 09:10:38.000000000 
+0200
@@ -13,6 +13,21 @@
 <!-- changelog follows -->
 
 
+## [24.2.0](https://github.com/pyca/service-identity/compare/24.1.0...24.2.0) 
- 2024-10-26
+
+### Added
+
+- Python 3.13 is now officially supported.
+  [#74](https://github.com/pyca/service-identity/pull/74)
+
+
+### Changed
+
+- pyOpenSSL's identity extraction has been reimplemented using 
*cryptography*'s primitives instead of deprecated pyOpenSSL APIs.
+  As a result, the oldest supported pyOpenSSL version is now 17.1.0.
+  [#70](https://github.com/pyca/service-identity/pull/70)
+
+
 ## [24.1.0](https://github.com/pyca/service-identity/compare/23.1.0...24.1.0) 
- 2024-01-14
 
 ### Changed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/README.md 
new/service-identity-24.2.0/README.md
--- old/service-identity-24.1.0/README.md       2024-01-14 08:05:19.000000000 
+0100
+++ new/service-identity-24.2.0/README.md       2024-10-26 09:10:38.000000000 
+0200
@@ -36,8 +36,7 @@
 
 ### *service-identity* for Enterprise
 
-Available as part of the Tidelift Subscription.
+Available as part of the [Tidelift 
Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek).
 
 The maintainers of *service-identity* and thousands of other packages are 
working with Tidelift to deliver commercial support and maintenance for the 
open-source packages you use to build your applications.
 Save time, reduce risk, and improve code health, while paying the maintainers 
of the exact packages you use.
-[Learn more.](https://tidelift.com/lifter/search/pypi/service-identity)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/docs/conf.py 
new/service-identity-24.2.0/docs/conf.py
--- old/service-identity-24.1.0/docs/conf.py    2024-01-14 08:05:19.000000000 
+0100
+++ new/service-identity-24.2.0/docs/conf.py    2024-10-26 09:10:38.000000000 
+0200
@@ -1,16 +1,6 @@
 from importlib import metadata
 
 
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-# sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-# needs_sphinx = '1.0'
-
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
@@ -45,9 +35,6 @@
 # The suffix of source filenames.
 source_suffix = ".rst"
 
-# The encoding of source files.
-# source_encoding = 'utf-8-sig'
-
 # The master toctree document.
 master_doc = "index"
 
@@ -68,50 +55,16 @@
 if "dev" in release:
     release = version = "UNRELEASED"
 
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-# language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-# today = ''
-# Else, today_fmt is used as the format for a strftime call.
-# today_fmt = '%B %d, %Y'
-
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = ["_build"]
 
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-# default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-# add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-# add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-# show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-# pygments_style = "sphinx"
-
-# A list of ignored prefixes for module index sorting.
-# modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-# keep_warnings = False
-
 
 # -- Options for HTML output ----------------------------------------------
 
 html_theme = "furo"
 html_theme_options = {
-    "top_of_page_button": None,
+    "top_of_page_buttons": [],
     "light_css_variables": {
         "font-stack": "Inter, sans-serif",
         "font-stack--monospace": "BerkeleyMono, MonoLisa, ui-monospace, "
@@ -142,27 +95,6 @@
     )
 ]
 
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-# latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-# latex_use_parts = False
-
-# If true, show page references after internal links.
-# latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-# latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-# latex_appendices = []
-
-# If false, no module index is generated.
-# latex_domain_indices = True
-
-
 # -- Options for manual page output ---------------------------------------
 
 # One entry per manual page. List of tuples
@@ -177,10 +109,6 @@
     )
 ]
 
-# If true, show URL addresses after external links.
-# man_show_urls = False
-
-
 # -- Options for Texinfo output -------------------------------------------
 
 # Grouping the document tree into Texinfo files. List of tuples
@@ -198,18 +126,6 @@
     )
 ]
 
-# Documents to append as an appendix to all manuals.
-# texinfo_appendices = []
-
-# If false, no module index is generated.
-# texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-# texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-# texinfo_no_detailmenu = False
-
 
 intersphinx_mapping = {
     "python": ("https://docs.python.org/3";, None),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/pyproject.toml 
new/service-identity-24.2.0/pyproject.toml
--- old/service-identity-24.1.0/pyproject.toml  2024-01-14 08:05:19.000000000 
+0100
+++ new/service-identity-24.2.0/pyproject.toml  2024-10-26 09:10:38.000000000 
+0200
@@ -18,6 +18,7 @@
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
     "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
     "Programming Language :: Python :: Implementation :: CPython",
     "Programming Language :: Python :: Implementation :: PyPy",
     "Topic :: Security :: Cryptography",
@@ -88,12 +89,8 @@
 addopts = ["-ra", "--strict-markers", "--strict-config"]
 xfail_strict = true
 testpaths = "tests"
-filterwarnings = [
-    "once::Warning",
-    "ignore:::aiohttp[.*]",
-    "ignore:::importlib[.*]",
-    "ignore::DeprecationWarning:twisted.python.threadable",
-]
+filterwarnings = ["once::Warning"]
+norecursedirs = ["tests/typing"]
 
 
 [tool.coverage.run]
@@ -132,55 +129,56 @@
 whitelist-regex = ["test_.*"]
 
 
-[tool.black]
-line-length = 79
-
-
 [tool.ruff]
 src = ["src", "tests"]
-select = [
-    "E",   # pycodestyle
-    "W",   # pycodestyle
-    "F",   # Pyflakes
-    "UP",  # pyupgrade
-    "N",   # pep8-naming
-    "YTT", # flake8-2020
-    "S",   # flake8-bandit
-    "B",   # flake8-bugbear
-    "C4",  # flake8-comprehensions
-    "T10", # flake8-debugger
-    "ISC", # flake8-implicit-str-concat
-    "RET", # flake8-return
-    "SIM", # flake8-simplify
-    "DTZ", # flake8-datetimez
-    "I",   # isort
-    "PGH", # pygrep-hooks
-    "PLC", # Pylint
-    "PIE", # flake8-pie
-    "RUF", # ruff
-]
+line-length = 79
+
+[tool.ruff.lint]
+select = ["ALL"]
 ignore = [
-    "RUF001", # leave my smart characters alone
-    "N801",   # some artistic freedom when naming things after RFCs
-    "N802",   # ditto
+    "A001",    # shadowing is fine
+    "ANN",     # Mypy is better at this
+    "ARG001",  # we don't control all args passed in
+    "ARG005",  # we need stub lambdas
+    "COM",     # ruff format takes care of our commas
+    "D",       # We prefer our own docstring style.
+    "E501",    # leave line-length enforcement to ruff format
+    "FIX",     # Yes, we want XXX as a marker.
+    "INP001",  # sometimes we want Python files outside of packages
+    "ISC001",  # conflicts with ruff format
+    "N801",    # some artistic freedom when naming things after RFCs
+    "N802",    # ditto
+    "PLR2004", # numbers are sometimes fine
+    "RUF001",  # leave my smart characters alone
+    "SLF001",  # private members are accessed by friendly functions
+    "TCH",     # TYPE_CHECKING blocks break autodocs
+    "TD",      # we don't follow other people's todo style
 ]
 
-[tool.ruff.per-file-ignores]
+[tool.ruff.lint.per-file-ignores]
 "tests/*" = [
+    "B018",    # "useless" expressions can be useful in tests
+    "PLC1901", # empty strings are falsey, but are less specific in tests
+    "PT005",   # we always add underscores and explicit name
+    "PT011",   # broad is fine
     "S101",    # assert
     "S301",    # I know pickle is bad, but people use it.
-    "SIM300",  # Yoda rocks in tests
-    "PLC1901", # empty strings are falsey, but are less specific in tests
-    "B018",    # "useless" expressions can be useful in tests
+    "SIM300",  # Yoda rocks in asserts
+    "TRY301",  # tests need to raise exceptions
+]
+"docs/pyopenssl_example.py" = [
+    "T201", # print is fine in the example
+    "T203", # pprint is fine in the example
 ]
 
-[tool.ruff.isort]
+[tool.ruff.lint.isort]
 lines-between-types = 1
 lines-after-imports = 2
 
 
 [tool.mypy]
 strict = true
+pretty = true
 
 show_error_codes = true
 enable_error_code = ["ignore-without-code"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-24.1.0/src/service_identity/__init__.py 
new/service-identity-24.2.0/src/service_identity/__init__.py
--- old/service-identity-24.1.0/src/service_identity/__init__.py        
2024-01-14 08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/src/service_identity/__init__.py        
2024-10-26 09:10:38.000000000 +0200
@@ -2,7 +2,6 @@
 Verify service identities.
 """
 
-
 from . import cryptography, hazmat, pyopenssl
 from .exceptions import (
     CertificateError,
@@ -30,34 +29,10 @@
 
 
 def __getattr__(name: str) -> str:
-    dunder_to_metadata = {
-        "__version__": "version",
-        "__description__": "summary",
-        "__uri__": "",
-        "__url__": "",
-        "__email__": "",
-    }
-    if name not in dunder_to_metadata:
-        raise AttributeError(f"module {__name__} has no attribute {name}")
-
-    import warnings
-
-    from importlib.metadata import metadata
-
-    warnings.warn(
-        f"Accessing service_identity.{name} is deprecated and will be "
-        "removed in a future release. Use importlib.metadata directly "
-        "to query packaging metadata.",
-        DeprecationWarning,
-        stacklevel=2,
-    )
-
-    meta = metadata("service-identity")
-
-    if name in ("__uri__", "__url__"):
-        return meta["Project-URL"].split(" ", 1)[1]
+    if name != "__version__":
+        msg = f"module {__name__} has no attribute {name}"
+        raise AttributeError(msg)
 
-    if name == "__email__":
-        return meta["Author-email"].split("<", 1)[1].rstrip(">")
+    from importlib.metadata import version
 
-    return meta[dunder_to_metadata[name]]
+    return version("service-identity")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-24.1.0/src/service_identity/cryptography.py 
new/service-identity-24.2.0/src/service_identity/cryptography.py
--- old/service-identity-24.1.0/src/service_identity/cryptography.py    
2024-01-14 08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/src/service_identity/cryptography.py    
2024-10-26 09:10:38.000000000 +0200
@@ -163,7 +163,8 @@
                 if isinstance(srv, IA5String):
                     ids.append(SRVPattern.from_bytes(srv.asOctets()))
                 else:  # pragma: no cover
-                    raise CertificateError("Unexpected certificate content.")
+                    msg = "Unexpected certificate content."
+                    raise CertificateError(msg)
 
     return ids
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-24.1.0/src/service_identity/hazmat.py 
new/service-identity-24.2.0/src/service_identity/hazmat.py
--- old/service-identity-24.1.0/src/service_identity/hazmat.py  2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/src/service_identity/hazmat.py  2024-10-26 
09:10:38.000000000 +0200
@@ -51,9 +51,8 @@
     if a pattern of the respective type is present.
     """
     if not cert_patterns:
-        raise CertificateError(
-            "Certificate does not contain any `subjectAltName`s."
-        )
+        msg = "Certificate does not contain any `subjectAltName`s."
+        raise CertificateError(msg)
 
     errors = []
     matches = _find_matches(cert_patterns, obligatory_ids) + _find_matches(
@@ -63,7 +62,9 @@
     matched_ids = [match.service_id for match in matches]
     for i in obligatory_ids:
         if i not in matched_ids:
-            errors.append(i.error_on_mismatch(mismatched_id=i))
+            errors.append(  # noqa: PERF401
+                i.error_on_mismatch(mismatched_id=i)
+            )
 
     for i in optional_ids:
         # If an optional ID is not matched by a certificate pattern *but* there
@@ -73,7 +74,9 @@
         if i not in matched_ids and _contains_instance_of(
             cert_patterns, i.pattern_class
         ):
-            errors.append(i.error_on_mismatch(mismatched_id=i))
+            errors.append(  # noqa: PERF401
+                i.error_on_mismatch(mismatched_id=i)
+            )
 
     if errors:
         raise VerificationError(errors=errors)
@@ -95,7 +98,10 @@
     for sid in service_ids:
         for cid in cert_patterns:
             if sid.verify(cid):
-                matches.append(ServiceMatch(cert_pattern=cid, service_id=sid))
+                matches.append(  # noqa: PERF401
+                    ServiceMatch(cert_pattern=cid, service_id=sid)
+                )
+
     return matches
 
 
@@ -121,9 +127,10 @@
 
     try:
         int(pattern)
-        return True
     except ValueError:
         pass
+    else:
+        return True
 
     try:
         ipaddress.ip_address(pattern.replace("*", "1"))
@@ -147,12 +154,14 @@
     @classmethod
     def from_bytes(cls, pattern: bytes) -> DNSPattern:
         if not isinstance(pattern, bytes):
-            raise TypeError("The DNS pattern must be a bytes string.")
+            msg = "The DNS pattern must be a bytes string."
+            raise TypeError(msg)
 
         pattern = pattern.strip()
 
         if pattern == b"" or _is_ip_address(pattern) or b"\0" in pattern:
-            raise CertificateError(f"Invalid DNS pattern {pattern!r}.")
+            msg = f"Invalid DNS pattern {pattern!r}."
+            raise CertificateError(msg)
 
         pattern = pattern.translate(_TRANS_TO_LOWER)
         if b"*" in pattern:
@@ -175,9 +184,8 @@
         try:
             return cls(pattern=ipaddress.ip_address(bs))
         except ValueError:
-            raise CertificateError(
-                f"Invalid IP address pattern {bs!r}."
-            ) from None
+            msg = f"Invalid IP address pattern {bs!r}."
+            raise CertificateError(msg) from None
 
 
 @attr.s(slots=True)
@@ -194,12 +202,14 @@
     @classmethod
     def from_bytes(cls, pattern: bytes) -> URIPattern:
         if not isinstance(pattern, bytes):
-            raise TypeError("The URI pattern must be a bytes string.")
+            msg = "The URI pattern must be a bytes string."
+            raise TypeError(msg)
 
         pattern = pattern.strip().translate(_TRANS_TO_LOWER)
 
         if b":" not in pattern or b"*" in pattern or _is_ip_address(pattern):
-            raise CertificateError(f"Invalid URI pattern {pattern!r}.")
+            msg = f"Invalid URI pattern {pattern!r}."
+            raise CertificateError(msg)
 
         protocol_pattern, hostname = pattern.split(b":")
 
@@ -223,7 +233,8 @@
     @classmethod
     def from_bytes(cls, pattern: bytes) -> SRVPattern:
         if not isinstance(pattern, bytes):
-            raise TypeError("The SRV pattern must be a bytes string.")
+            msg = "The SRV pattern must be a bytes string."
+            raise TypeError(msg)
 
         pattern = pattern.strip().translate(_TRANS_TO_LOWER)
 
@@ -233,7 +244,8 @@
             or b"*" in pattern
             or _is_ip_address(pattern)
         ):
-            raise CertificateError(f"Invalid SRV pattern {pattern!r}.")
+            msg = f"Invalid SRV pattern {pattern!r}."
+            raise CertificateError(msg)
 
         name, hostname = pattern.split(b".", 1)
         return cls(
@@ -253,15 +265,12 @@
 @runtime_checkable
 class ServiceID(Protocol):
     @property
-    def pattern_class(self) -> type[CertificatePattern]:
-        ...
+    def pattern_class(self) -> type[CertificatePattern]: ...
 
     @property
-    def error_on_mismatch(self) -> type[Mismatch]:
-        ...
+    def error_on_mismatch(self) -> type[Mismatch]: ...
 
-    def verify(self, pattern: CertificatePattern) -> bool:
-        ...
+    def verify(self, pattern: CertificatePattern) -> bool: ...
 
 
 @attr.s(init=False, slots=True)
@@ -279,25 +288,27 @@
 
     def __init__(self, hostname: str):
         if not isinstance(hostname, str):
-            raise TypeError("DNS-ID must be a text string.")
+            msg = "DNS-ID must be a text string."
+            raise TypeError(msg)
 
         hostname = hostname.strip()
         if not hostname or _is_ip_address(hostname):
-            raise ValueError("Invalid DNS-ID.")
+            msg = "Invalid DNS-ID."
+            raise ValueError(msg)
 
         if any(ord(c) > 127 for c in hostname):
             if idna:
                 ascii_id = idna.encode(hostname)
             else:
-                raise ImportError(
-                    "idna library is required for non-ASCII IDs."
-                )
+                msg = "idna library is required for non-ASCII IDs."
+                raise ImportError(msg)
         else:
             ascii_id = hostname.encode("ascii")
 
         self.hostname = ascii_id.translate(_TRANS_TO_LOWER)
         if self._RE_LEGAL_CHARS.match(self.hostname) is None:
-            raise ValueError("Invalid DNS-ID.")
+            msg = "Invalid DNS-ID."
+            raise ValueError(msg)
 
     def verify(self, pattern: CertificatePattern) -> bool:
         """
@@ -346,11 +357,13 @@
 
     def __init__(self, uri: str):
         if not isinstance(uri, str):
-            raise TypeError("URI-ID must be a text string.")
+            msg = "URI-ID must be a text string."
+            raise TypeError(msg)
 
         uri = uri.strip()
         if ":" not in uri or _is_ip_address(uri):
-            raise ValueError("Invalid URI-ID.")
+            msg = "Invalid URI-ID."
+            raise ValueError(msg)
 
         prot, hostname = uri.split(":")
 
@@ -384,11 +397,13 @@
 
     def __init__(self, srv: str):
         if not isinstance(srv, str):
-            raise TypeError("SRV-ID must be a text string.")
+            msg = "SRV-ID must be a text string."
+            raise TypeError(msg)
 
         srv = srv.strip()
         if "." not in srv or _is_ip_address(srv) or srv[0] != "_":
-            raise ValueError("Invalid SRV-ID.")
+            msg = "Invalid SRV-ID."
+            raise ValueError(msg)
 
         name, hostname = srv.split(".", 1)
 
@@ -420,7 +435,7 @@
         if actual_head.startswith(b"xn--"):
             return False
 
-        return cert_head == b"*" or cert_head == actual_head
+        return cert_head in (b"*", actual_head)
 
     return cert_pattern == actual_hostname
 
@@ -432,25 +447,19 @@
     """
     cnt = cert_pattern.count(b"*")
     if cnt > 1:
-        raise CertificateError(
-            f"Certificate's DNS-ID {cert_pattern!r} contains too many 
wildcards."
-        )
+        msg = f"Certificate's DNS-ID {cert_pattern!r} contains too many 
wildcards."
+        raise CertificateError(msg)
     parts = cert_pattern.split(b".")
     if len(parts) < 3:
-        raise CertificateError(
-            f"Certificate's DNS-ID {cert_pattern!r} has too few host 
components for "
-            "wildcard usage."
-        )
+        msg = f"Certificate's DNS-ID {cert_pattern!r} has too few host 
components for wildcard usage."
+        raise CertificateError(msg)
     # We assume there will always be only one wildcard allowed.
     if b"*" not in parts[0]:
-        raise CertificateError(
-            "Certificate's DNS-ID {!r} has a wildcard outside the left-most "
-            "part.".format(cert_pattern)
-        )
+        msg = f"Certificate's DNS-ID {cert_pattern!r} has a wildcard outside 
the left-most part."
+        raise CertificateError(msg)
     if any(not len(p) for p in parts):
-        raise CertificateError(
-            f"Certificate's DNS-ID {cert_pattern!r} contains empty parts."
-        )
+        msg = f"Certificate's DNS-ID {cert_pattern!r} contains empty parts."
+        raise CertificateError(msg)
 
 
 # Ensure no locale magic interferes.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-24.1.0/src/service_identity/pyopenssl.py 
new/service-identity-24.2.0/src/service_identity/pyopenssl.py
--- old/service-identity-24.1.0/src/service_identity/pyopenssl.py       
2024-01-14 08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/src/service_identity/pyopenssl.py       
2024-10-26 09:10:38.000000000 +0200
@@ -9,20 +9,11 @@
 
 from typing import Sequence
 
-from pyasn1.codec.der.decoder import decode
-from pyasn1.type.char import IA5String
-from pyasn1.type.univ import ObjectIdentifier
-from pyasn1_modules.rfc2459 import GeneralNames
-
-from .exceptions import CertificateError
+from .cryptography import extract_patterns as _cryptography_extract_patterns
 from .hazmat import (
     DNS_ID,
     CertificatePattern,
-    DNSPattern,
     IPAddress_ID,
-    IPAddressPattern,
-    SRVPattern,
-    URIPattern,
     verify_service_identity,
 )
 
@@ -105,9 +96,6 @@
     )
 
 
-ID_ON_DNS_SRV = ObjectIdentifier("1.3.6.1.5.5.7.8.7")  # id_on_dnsSRV
-
-
 def extract_patterns(cert: X509) -> Sequence[CertificatePattern]:
     """
     Extract all valid ID patterns from a certificate for service verification.
@@ -121,44 +109,7 @@
     .. versionchanged:: 23.1.0
        ``commonName`` is not used as a fallback anymore.
     """
-    ids: list[CertificatePattern] = []
-    for i in range(cert.get_extension_count()):
-        ext = cert.get_extension(i)
-        if ext.get_short_name() == b"subjectAltName":
-            names, _ = decode(ext.get_data(), asn1Spec=GeneralNames())
-            for n in names:
-                name_string = n.getName()
-                if name_string == "dNSName":
-                    ids.append(
-                        DNSPattern.from_bytes(n.getComponent().asOctets())
-                    )
-                elif name_string == "iPAddress":
-                    ids.append(
-                        IPAddressPattern.from_bytes(
-                            n.getComponent().asOctets()
-                        )
-                    )
-                elif name_string == "uniformResourceIdentifier":
-                    ids.append(
-                        URIPattern.from_bytes(n.getComponent().asOctets())
-                    )
-                elif name_string == "otherName":
-                    comp = n.getComponent()
-                    oid = comp.getComponentByPosition(0)
-                    if oid == ID_ON_DNS_SRV:
-                        srv, _ = decode(comp.getComponentByPosition(1))
-                        if isinstance(srv, IA5String):
-                            ids.append(SRVPattern.from_bytes(srv.asOctets()))
-                        else:  # pragma: no cover
-                            raise CertificateError(
-                                "Unexpected certificate content."
-                            )
-                    else:  # pragma: no cover
-                        pass
-                else:  # pragma: no cover
-                    pass
-
-    return ids
+    return _cryptography_extract_patterns(cert.to_cryptography())
 
 
 def extract_ids(cert: X509) -> Sequence[CertificatePattern]:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-24.1.0/tests/constraints/oldest-pyopenssl.txt 
new/service-identity-24.2.0/tests/constraints/oldest-pyopenssl.txt
--- old/service-identity-24.1.0/tests/constraints/oldest-pyopenssl.txt  
2024-01-14 08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/tests/constraints/oldest-pyopenssl.txt  
2024-10-26 09:10:38.000000000 +0200
@@ -1,3 +1,3 @@
 attrs==19.1.0
 cryptography<35
-pyOpenSSL==17.0.0
+pyOpenSSL==17.1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/tests/test_hazmat.py 
new/service-identity-24.2.0/tests/test_hazmat.py
--- old/service-identity-24.1.0/tests/test_hazmat.py    2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/tests/test_hazmat.py    2024-10-26 
09:10:38.000000000 +0200
@@ -699,11 +699,11 @@
         """
         The __str__ and __repr__ methods return something helpful.
         """
-        try:
+        with pytest.raises(VerificationError) as ei:
             raise VerificationError(errors=["foo"])
-        except VerificationError as e:
-            assert repr(e) == str(e)
-            assert str(e) != ""
+
+        assert repr(ei.value) == str(ei.value)
+        assert str(ei.value) != ""
 
     @pytest.mark.parametrize("proto", range(pickle.HIGHEST_PROTOCOL + 1))
     @pytest.mark.parametrize(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/tests/test_packaging.py 
new/service-identity-24.2.0/tests/test_packaging.py
--- old/service-identity-24.1.0/tests/test_packaging.py 2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/tests/test_packaging.py 2024-10-26 
09:10:38.000000000 +0200
@@ -10,38 +10,10 @@
         """
         service_identity.__version__ returns the correct version.
         """
-        with pytest.deprecated_call():
-            assert (
-                metadata.version("service-identity")
-                == service_identity.__version__
-            )
-
-    def test_description(self):
-        """
-        service_identity.__description__ returns the correct description.
-        """
-        with pytest.deprecated_call():
-            assert (
-                "Service identity verification for pyOpenSSL & cryptography."
-                == service_identity.__description__
-            )
-
-    @pytest.mark.parametrize("name", ["uri", "url"])
-    def test_uri(self, name):
-        """
-        service_identity.__uri__ & __url__ return the correct project URL.
-        """
-        with pytest.deprecated_call():
-            assert "https://service-identity.readthedocs.io/"; == getattr(
-                service_identity, f"__{name}__"
-            )
-
-    def test_email(self):
-        """
-        service_identity.__email__ returns Hynek's email address.
-        """
-        with pytest.deprecated_call():
-            assert "[email protected]" == service_identity.__email__
+        assert (
+            metadata.version("service-identity")
+            == service_identity.__version__
+        )
 
     def test_does_not_exist(self):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/tests/typing/api.py 
new/service-identity-24.2.0/tests/typing/api.py
--- old/service-identity-24.1.0/tests/typing/api.py     2024-01-14 
08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/tests/typing/api.py     2024-10-26 
09:10:38.000000000 +0200
@@ -20,9 +20,9 @@
 backend = default_backend()
 c_cert = load_pem_x509_certificate("foo.pem", backend)
 
-c_ids: Sequence[
-    service_identity.hazmat.CertificatePattern
-] = service_identity.cryptography.extract_patterns(c_cert)
+c_ids: Sequence[service_identity.hazmat.CertificatePattern] = (
+    service_identity.cryptography.extract_patterns(c_cert)
+)
 service_identity.cryptography.verify_certificate_hostname(
     c_cert, "example.com"
 )
@@ -36,8 +36,8 @@
 p_cert = conn.get_peer_certificate()
 assert p_cert
 
-p_ids: Sequence[
-    service_identity.hazmat.CertificatePattern
-] = service_identity.pyopenssl.extract_patterns(p_cert)
+p_ids: Sequence[service_identity.hazmat.CertificatePattern] = (
+    service_identity.pyopenssl.extract_patterns(p_cert)
+)
 service_identity.pyopenssl.verify_hostname(conn, "example.com")
 service_identity.pyopenssl.verify_ip_address(conn, "127.0.0.1")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-24.1.0/tox.ini 
new/service-identity-24.2.0/tox.ini
--- old/service-identity-24.1.0/tox.ini 2024-01-14 08:05:19.000000000 +0100
+++ new/service-identity-24.2.0/tox.ini 2024-10-26 09:10:38.000000000 +0200
@@ -4,8 +4,7 @@
     lint,
     mypy-{api,pkg},
     docs,
-    pypy3{,-pyopenssl-latest-idna},
-    py3{8,9,10,11,12}{,-pyopenssl}{,-oldest}{,-idna},
+    py3{8,9,10,11,12,13}{,-pyopenssl}{,-oldest}{,-idna},
     coverage-report
 
 
@@ -40,7 +39,7 @@
 
 [testenv:lint]
 skip_install = true
-deps = pre-commit
+deps = pre-commit-uv
 commands = pre-commit run --all-files {posargs}
 
 

Reply via email to