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-01-21 23:07:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-service_identity (Old)
 and      /work/SRC/openSUSE:Factory/.python-service_identity.new.16006 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-service_identity"

Sun Jan 21 23:07:21 2024 rev:15 rq:1140210 version:24.1.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-service_identity/python-service_identity.changes
  2023-09-22 21:47:20.496702425 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-service_identity.new.16006/python-service_identity.changes
       2024-01-21 23:07:26.671511362 +0100
@@ -1,0 +2,9 @@
+Sat Jan 20 20:31:18 UTC 2024 - Dirk Müller <dmuel...@suse.com>
+
+- update to 24.1.0:
+  * If a certificate doesn't contain any `subjectAltName`s, we
+    now raise `service_identity.CertificateError` instead of
+    `service_identity.VerificationError` to make the problem
+    easier to debug.
+
+-------------------------------------------------------------------

Old:
----
  23.1.0.tar.gz

New:
----
  24.1.0.tar.gz

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

Other differences:
------------------
++++++ python-service_identity.spec ++++++
--- /var/tmp/diff_new_pack.l7lQlL/_old  2024-01-21 23:07:27.443539504 +0100
+++ /var/tmp/diff_new_pack.l7lQlL/_new  2024-01-21 23:07:27.443539504 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-service_identity
 #
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %define oname   service_identity
 %{?sle15_python_module_pythons}
 Name:           python-service_identity
-Version:        23.1.0
+Version:        24.1.0
 Release:        0
 Summary:        Service identity verification for pyOpenSSL
 License:        MIT

++++++ 23.1.0.tar.gz -> 24.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/.git_archival.txt 
new/service-identity-24.1.0/.git_archival.txt
--- old/service-identity-23.1.0/.git_archival.txt       2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/.git_archival.txt       2024-01-14 
08:05:19.000000000 +0100
@@ -1,4 +1,4 @@
-node: be2f98443fe0ce1d8dcc7f373ea53561bd929837
-node-date: 2023-06-14T09:54:35+02:00
-describe-name: 23.1.0
-ref-names: tag: 23.1.0
+node: e5ba15ec13b1750cae35004dede2e6e4af74308f
+node-date: 2024-01-14T08:05:19+01:00
+describe-name: 24.1.0
+ref-names: tag: 24.1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/.github/CONTRIBUTING.md 
new/service-identity-24.1.0/.github/CONTRIBUTING.md
--- old/service-identity-23.1.0/.github/CONTRIBUTING.md 2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/.github/CONTRIBUTING.md 2024-01-14 
08:05:19.000000000 +0100
@@ -65,7 +65,7 @@
 >   Yes, you can work on `main` in your fork and submit pull requests.
 >   But this will *inevitably* lead to you not being able to synchronize your 
 > fork with upstream and having to start over.
 
-Change into the newly created directory and after activating a virtual 
environment, install an editable version of *service-identity* along with its 
tests and docs requirements:
+Change into the newly created directory and after activating a virtual 
environment, install an editable version of *service-identity* along with its 
tests requirements:
 
 ```console
 $ cd service-identity
@@ -79,13 +79,21 @@
 $ python -Im pytest
 ```
 
-should work and pass, as should:
+When working on the documentation, use:
 
 ```console
-$ cd docs
-$ make html
+$ tox run -e docs-watch
 ```
 
+... to watch your files and automatically rebuild when a file changes.
+And use:
+
+```console
+$ tox run -e docs
+```
+
+... to build it once and run our doctests.
+
 The built documentation can then be found in `docs/_build/html/`.
 
 ---
@@ -102,23 +110,31 @@
 $ pre-commit run --all-files
 ```
 
-and our CI has integration with [pre-commit.ci](https://pre-commit.ci).
 But it's way more comfortable to run it locally and catch avoidable errors 
before pushing them to GitHub.
 
 
 ## Code
 
 - Obey [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 
257](https://www.python.org/dev/peps/pep-0257/).
-  We use the `"""`-on-separate-lines style for docstrings:
+  We use the `"""`-on-separate-lines style for docstrings with 
[Napoleon](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html)-style
 API documentation:
 
   ```python
-  def func(x: str) -> str:
+  def func(x: str, y: int) -> str:
       """
       Do something.
 
-      :param str x: A very important parameter.
+      Args:
+          x: A very important parameter.
+
+          y:
+              Another important parameter but one that doesn't fit a line so
+              it already starts on the next one.
 
-      :rtype: str
+      Raises:
+          Exception: When Something goes wrong.
+
+      Returns:
+          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`.
@@ -140,9 +156,6 @@
 
 - To run the test suite, all you need is a recent [*tox*].
   It will ensure the test suite runs with all dependencies against all Python 
versions just as it will in our [CI].
-  If you lack some Python versions, you can can always limit the environments 
like `tox -e py38,py39`, or make it a non-failure using `tox 
--skip-missing-interpreters`.
-
-  In that case you should look into [*asdf*](https://asdf-vm.com) or 
[*pyenv*](https://github.com/pyenv/pyenv), which make it very easy to install 
many different Python versions in parallel.
 - Write [good test docstrings](https://jml.io/pages/test-docstrings.html).
 - If you've changed or added public APIs, please update our type stubs (files 
ending in `.pyi`).
 
@@ -166,7 +179,7 @@
 
   ## Header of New Top Section
 
-  ###  Header of New Section
+  ### Header of New Section
 
   First line of new section.
   ```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/.github/workflows/ci.yml 
new/service-identity-24.1.0/.github/workflows/ci.yml
--- old/service-identity-23.1.0/.github/workflows/ci.yml        2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/.github/workflows/ci.yml        2024-01-14 
08:05:19.000000000 +0100
@@ -3,28 +3,26 @@
 
 on:
   push:
-    branches: ["main"]
+    branches: [main]
   pull_request:
-    branches: ["main"]
-
-permissions:
-  contents: read
+  workflow_dispatch:
 
 env:
   FORCE_COLOR: "1"  # Make tools pretty.
   PIP_DISABLE_PIP_VERSION_CHECK: "1"
   PIP_NO_PYTHON_VERSION_WARNING: "1"
 
+permissions: {}
 
 jobs:
   lint:
     name: Run linters
-    runs-on: "ubuntu-latest"
+    runs-on: ubuntu-latest
     steps:
-      - uses: "actions/checkout@v3"
-      - uses: "actions/setup-python@v4"
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
         with:
-          python-version-file: .python-version-default
+          python-version: "3.11"  # XXX: change once interrogate works on 3.12
           cache: pip
 
       - name: Install & run tox
@@ -47,8 +45,8 @@
           - "pypy-3.9"
 
     steps:
-      - uses: actions/checkout@v3
-      - uses: actions/setup-python@v4
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
           allow-prereleases: true
@@ -71,29 +69,30 @@
         run: python -Im tox run -e mypy-api
 
       - name: Upload coverage data
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
-          name: coverage-data
+          name: coverage-data-${{ matrix.python-version }}
           path: .coverage.*
           if-no-files-found: ignore
 
   coverage:
-    name: Combine & check coverage.
+    name: Combine & check coverage
     needs: tests
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
-      - uses: actions/setup-python@v4
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
         with:
           python-version-file: .python-version-default
           cache: pip
 
-      - uses: actions/download-artifact@v3
+      - uses: actions/download-artifact@v4
         with:
-          name: coverage-data
+          pattern: coverage-data-*
+          merge-multiple: true
 
-      - name: Combine coverage & fail if it's <100%.
+      - name: Combine coverage & fail if it's <100%
         run: |
           python -Im pip install coverage[toml]
 
@@ -101,13 +100,13 @@
           python -Im coverage html --skip-covered --skip-empty
 
           # Report and write to summary.
-          python -Im coverage report | sed 's/^/    /' >> $GITHUB_STEP_SUMMARY
+          python -Im coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
 
           # Report again and fail if under 100%.
           python -Im coverage report --fail-under=100
 
       - name: Upload HTML report if check failed.
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: html-report
           path: htmlcov
@@ -118,8 +117,8 @@
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
-      - uses: actions/setup-python@v4
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
         with:
           python-version-file: .python-version-default
           cache: pip
@@ -138,8 +137,8 @@
     runs-on: ${{ matrix.os }}
 
     steps:
-      - uses: actions/checkout@v3
-      - uses: actions/setup-python@v4
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
         with:
           python-version-file: .python-version-default
           cache: pip
@@ -153,11 +152,11 @@
     name: Build docs & run doctests
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
-      - uses: actions/setup-python@v4
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
         with:
           # Keep in sync with tox.ini/docs & .readthedocs.yaml
-          python-version: "3.11"
+          python-version: "3.12"
           cache: pip
 
       - name: Install & run tox
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-23.1.0/.github/workflows/codeql-analysis.yml 
new/service-identity-24.1.0/.github/workflows/codeql-analysis.yml
--- old/service-identity-23.1.0/.github/workflows/codeql-analysis.yml   
2023-06-14 09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/.github/workflows/codeql-analysis.yml   
2024-01-14 08:05:19.000000000 +0100
@@ -28,15 +28,15 @@
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@v2
+        uses: github/codeql-action/init@v3
         with:
           languages: ${{ matrix.language }}
 
       - name: Autobuild
-        uses: github/codeql-action/autobuild@v2
+        uses: github/codeql-action/autobuild@v3
 
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@v2
+        uses: github/codeql-action/analyze@v3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-23.1.0/.github/workflows/pypi-package.yml 
new/service-identity-24.1.0/.github/workflows/pypi-package.yml
--- old/service-identity-23.1.0/.github/workflows/pypi-package.yml      
2023-06-14 09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/.github/workflows/pypi-package.yml      
2024-01-14 08:05:19.000000000 +0100
@@ -6,7 +6,6 @@
     branches: [main]
     tags: ["*"]
   pull_request:
-    branches: [main]
   release:
     types:
       - published
@@ -23,11 +22,11 @@
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           fetch-depth: 0
 
-      - uses: hynek/build-and-inspect-python-package@v1
+      - uses: hynek/build-and-inspect-python-package@v2
 
   # Upload to Test PyPI on every commit on main.
   release-test-pypi:
@@ -39,7 +38,7 @@
 
     steps:
       - name: Download packages built by build-and-inspect-python-package
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@v4
         with:
           name: Packages
           path: dist
@@ -59,7 +58,7 @@
 
     steps:
       - name: Download packages built by build-and-inspect-python-package
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@v4
         with:
           name: Packages
           path: dist
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/.pre-commit-config.yaml 
new/service-identity-24.1.0/.pre-commit-config.yaml
--- old/service-identity-23.1.0/.pre-commit-config.yaml 2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/.pre-commit-config.yaml 2024-01-14 
08:05:19.000000000 +0100
@@ -1,28 +1,31 @@
 ---
-default_language_version:
-  # keep in-sync with .python-version-default
-  python: python3.11
-
 repos:
   - repo: https://github.com/psf/black
-    rev: 23.3.0
+    rev: 23.12.1
     hooks:
       - id: black
 
-  - repo: https://github.com/charliermarsh/ruff-pre-commit
-    rev: v0.0.272
+  - repo: https://github.com/astral-sh/ruff-pre-commit
+    rev: v0.1.13
     hooks:
       - id: ruff
         args: [--fix, --exit-non-zero-on-fix]
 
   - repo: https://github.com/codespell-project/codespell
-    rev: v2.2.4
+    rev: v2.2.6
     hooks:
       - id: codespell
         args: [-L, fo]
 
+  - repo: https://github.com/econchick/interrogate
+    rev: 1.5.0
+    hooks:
+      - id: interrogate
+        language_version: python3.11
+        args: [tests, -v]
+
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.4.0
+    rev: v4.5.0
     hooks:
       - id: trailing-whitespace
       - id: end-of-file-fixer
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/.python-version-default 
new/service-identity-24.1.0/.python-version-default
--- old/service-identity-23.1.0/.python-version-default 2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/.python-version-default 2024-01-14 
08:05:19.000000000 +0100
@@ -1 +1 @@
-3.11
+3.12
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/.readthedocs.yaml 
new/service-identity-24.1.0/.readthedocs.yaml
--- old/service-identity-23.1.0/.readthedocs.yaml       2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/.readthedocs.yaml       2024-01-14 
08:05:19.000000000 +0100
@@ -5,7 +5,7 @@
   os: ubuntu-22.04
   tools:
     # Keep in-sync with tox.ini/docs and ci.yml/docs
-    python: "3.11"
+    python: "3.12"
 
 python:
   install:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/CHANGELOG.md 
new/service-identity-24.1.0/CHANGELOG.md
--- old/service-identity-23.1.0/CHANGELOG.md    2023-06-14 09:54:35.000000000 
+0200
+++ new/service-identity-24.1.0/CHANGELOG.md    2024-01-14 08:05:19.000000000 
+0100
@@ -12,6 +12,15 @@
 
 <!-- changelog follows -->
 
+
+## [24.1.0](https://github.com/pyca/service-identity/compare/23.1.0...24.1.0) 
- 2024-01-14
+
+### Changed
+
+- If a certificate doesn't contain any `subjectAltName`s, we now raise 
`service_identity.CertificateError` instead of 
`service_identity.VerificationError` to make the problem easier to debug.
+  [#67](https://github.com/pyca/service-identity/pull/67)
+
+
 ## [23.1.0](https://github.com/pyca/service-identity/compare/21.1.0...23.1.0) 
- 2023-06-14
 
 ### Removed
@@ -21,7 +30,7 @@
   It has been deprecated since 2017 and isn't supported by any major browser.
 - The oldest supported pyOpenSSL version (when using the `pyopenssl` backend) 
is now 17.0.0.
   When using such an old pyOpenSSL version, you have to pin *cryptography* 
yourself to ensure compatibility between them.
-  Please check out 
[`contraints/oldest-pyopenssl.txt`](https://github.com/pyca/service-identity/blob/main/tests/constraints/oldest-pyopenssl.txt)
 to verify what we are testing against.
+  Please check out 
[`constraints/oldest-pyopenssl.txt`](https://github.com/pyca/service-identity/blob/main/tests/constraints/oldest-pyopenssl.txt)
 to verify what we are testing against.
 
 
 ### Deprecated
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/LICENSE 
new/service-identity-24.1.0/LICENSE
--- old/service-identity-23.1.0/LICENSE 2023-06-14 09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/LICENSE 2024-01-14 08:05:19.000000000 +0100
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2014 Hynek Schlawack and the service-identity contibutors
+Copyright (c) 2014 Hynek Schlawack and the service-identity contributors
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/README.md 
new/service-identity-24.1.0/README.md
--- old/service-identity-23.1.0/README.md       2023-06-14 09:54:35.000000000 
+0200
+++ new/service-identity-24.1.0/README.md       2024-01-14 08:05:19.000000000 
+0100
@@ -1,23 +1,11 @@
 # Service Identity Verification
 
-<a href="https://service-identity.readthedocs.io/";>
-    <img src="https://img.shields.io/badge/Docs-Read%20The%20Docs-black"; 
alt="Documentation" />
-</a>
-<a href="https://github.com/pyca/service-identity/blob/main/LICENSE";>
-    <img src="https://img.shields.io/badge/license-MIT-C06524"; alt="License: 
MIT" />
-</a>
-<a href="https://pypi.org/project/service-identity/";>
-    <img src="https://img.shields.io/pypi/v/service-identity"; alt="PyPI 
release" />
-</a>
-<a href="https://pepy.tech/project/service-identity";>
-    <img src="https://static.pepy.tech/badge/service-identity/month"; 
alt="Downloads per month" />
-</a>
-<a href="https://bestpractices.coreinfrastructure.org/projects/7462";>
-    <img 
src="https://bestpractices.coreinfrastructure.org/projects/7462/badge"; />
-</a>
-<a 
href="https://www.irccloud.com/invite?channel=%23pyca&amp;hostname=irc.libera.chat&amp;port=6697&amp;ssl=1";>
-    <img 
src="https://www.irccloud.com/invite-svg?channel=%23pyca&amp;hostname=irc.libera.chat&amp;port=6697&amp;ssl=1";
 alt="PyCA on IRC" />
-</a>
+<a href="https://service-identity.readthedocs.io/";><img 
src="https://img.shields.io/badge/Docs-Read%20The%20Docs-black"; 
alt="Documentation" /></a>
+<a href="https://github.com/pyca/service-identity/blob/main/LICENSE";><img 
src="https://img.shields.io/badge/license-MIT-C06524"; alt="License: MIT" /></a>
+<a href="https://pypi.org/project/service-identity/";><img 
src="https://img.shields.io/pypi/v/service-identity"; alt="PyPI release" /></a>
+<a href="https://pepy.tech/project/service-identity";><img 
src="https://static.pepy.tech/badge/service-identity/month"; alt="Downloads per 
month" /></a>
+<a href="https://bestpractices.coreinfrastructure.org/projects/7462";><img 
src="https://bestpractices.coreinfrastructure.org/projects/7462/badge"; /></a>
+<a 
href="https://www.irccloud.com/invite?channel=%23pyca&amp;hostname=irc.libera.chat&amp;port=6697&amp;ssl=1";><img
 
src="https://www.irccloud.com/invite-svg?channel=%23pyca&amp;hostname=irc.libera.chat&amp;port=6697&amp;ssl=1";
 alt="PyCA on IRC" /></a>
 
 <!-- spiel-begin -->
 
@@ -31,6 +19,8 @@
 In the simplest case, this means *host name verification*.
 However, *service-identity* implements [RFC 
6125](https://datatracker.ietf.org/doc/html/rfc6125.html) fully.
 
+Also check out [*pem*](https://github.com/hynek/pem) that makes loading 
certificates from all kinds of PEM-encoded files a breeze!
+
 
 ## Project Information
 
@@ -41,7 +31,7 @@
 
 *service-identity* is written and maintained by [Hynek 
Schlawack](https://hynek.me/).
 
-The development is kindly supported by my employer [Variomedia 
AG](https://www.variomedia.de/), *service-identity*'s [Tidelift 
subscribers][Tidelift], and all my amazing [GitHub 
Sponsors](https://github.com/sponsors/hynek).
+The development is kindly supported by my employer [Variomedia 
AG](https://www.variomedia.de/), *service-identity*'s [Tidelift 
subscribers](https://tidelift.com/lifter/search/pypi/service-identity), and all 
my amazing [GitHub Sponsors](https://github.com/sponsors/hynek).
 
 
 ### *service-identity* for Enterprise
@@ -50,6 +40,4 @@
 
 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.][Tidelift]
-
-[Tidelift]: 
https://tidelift.com/subscription/pkg/pypi-service-identity?utm_source=pypi-service-identity&utm_medium=readme
+[Learn more.](https://tidelift.com/lifter/search/pypi/service-identity)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/docs/_static/custom.css 
new/service-identity-24.1.0/docs/_static/custom.css
--- old/service-identity-23.1.0/docs/_static/custom.css 1970-01-01 
01:00:00.000000000 +0100
+++ new/service-identity-24.1.0/docs/_static/custom.css 2024-01-14 
08:05:19.000000000 +0100
@@ -0,0 +1,10 @@
+@import url('https://rsms.me/inter/inter.css');
+@import url('https://assets.hynek.me/css/bm.css');
+
+
+:root {
+  font-feature-settings: 'liga' 1, 'calt' 1; /* fix for Chrome */
+}
+@supports (font-variation-settings: normal) {
+  :root { font-family: InterVariable, sans-serif; }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/docs/conf.py 
new/service-identity-24.1.0/docs/conf.py
--- old/service-identity-23.1.0/docs/conf.py    2023-06-14 09:54:35.000000000 
+0200
+++ new/service-identity-24.1.0/docs/conf.py    2024-01-14 08:05:19.000000000 
+0100
@@ -18,6 +18,7 @@
     "sphinx.ext.doctest",
     "sphinx.ext.autodoc",
     "sphinx.ext.intersphinx",
+    "sphinx.ext.napoleon",
     "myst_parser",
     "notfound.extension",
 ]
@@ -63,6 +64,10 @@
 # The short X.Y version.
 version = release.rsplit(".", 1)[0]
 
+# Avoid confusing in-dev versions.
+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
@@ -104,85 +109,17 @@
 
 # -- Options for HTML output ----------------------------------------------
 
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-
 html_theme = "furo"
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-# html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-# html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-# html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-# html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-# html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-# html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-# html_static_path = ['_static']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-# html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-# html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-# html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-# html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-# html_additional_pages = {}
-
-# If false, no module index is generated.
-# html_domain_indices = True
-
-# If false, no index is generated.
-# html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-# html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-# html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-# html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-# html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-# html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-# html_file_suffix = None
+html_theme_options = {
+    "top_of_page_button": None,
+    "light_css_variables": {
+        "font-stack": "Inter, sans-serif",
+        "font-stack--monospace": "BerkeleyMono, MonoLisa, ui-monospace, "
+        "SFMono-Regular, Menlo, Consolas, Liberation Mono, monospace",
+    },
+}
+html_static_path = ["_static"]
+html_css_files = ["custom.css"]
 
 # Output file base name for HTML help builder.
 htmlhelp_basename = "service-identitydoc"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/docs/index.md 
new/service-identity-24.1.0/docs/index.md
--- old/service-identity-23.1.0/docs/index.md   2023-06-14 09:54:35.000000000 
+0200
+++ new/service-identity-24.1.0/docs/index.md   2024-01-14 08:05:19.000000000 
+0100
@@ -17,22 +17,28 @@
 api
 ```
 
-## Project Information
-
-```{include} ../README.md
-:start-after: "## Project Information"
-```
-
-
 ## Indices and tables
 
 - {ref}`genindex`
 - {ref}`search`
 
-% Prevent warning; the changelog is linked in the header.
+
+## *service-identity* for Enterprise
+
+```{include} ../README.md
+:start-after: "*service-identity* for Enterprise"
+```
+
 
 ```{toctree}
 :hidden:
+:caption: Meta
 
+license
 changelog
+PyPI <https://pypi.org/project/service-identity/>
+GitHub <https://github.com/pyca/service-identity/>
+Contributing 
<https://github.com/pyca/service-identity/blob/main/.github/CONTRIBUTING.md>
+Security Policy 
<https://github.com/pyca/service-identity/blob/main/.github/SECURITY.md>
+Funding <https://hynek.me/say-thanks/>
 ```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/docs/license.md 
new/service-identity-24.1.0/docs/license.md
--- old/service-identity-23.1.0/docs/license.md 1970-01-01 01:00:00.000000000 
+0100
+++ new/service-identity-24.1.0/docs/license.md 2024-01-14 08:05:19.000000000 
+0100
@@ -0,0 +1,9 @@
+# License and Credits
+
+*service-identity* is released under the 
[MIT](https://github.com/pyca/service-identity/blob/main/LICENSE) license.
+
+```{include} ../README.md
+:parser: myst_parser.sphinx_
+:start-after: "## Credits"
+:end-before: "### *service-identity* for Enterprise"
+```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/pyproject.toml 
new/service-identity-24.1.0/pyproject.toml
--- old/service-identity-23.1.0/pyproject.toml  2023-06-14 09:54:35.000000000 
+0200
+++ new/service-identity-24.1.0/pyproject.toml  2024-01-14 08:05:19.000000000 
+0100
@@ -38,14 +38,16 @@
 tests = ["coverage[toml]>=5.0.2", "pytest"]
 docs = ["sphinx", "furo", "myst-parser", "sphinx-notfound-page", "pyOpenSSL"]
 mypy = ["mypy", "types-pyOpenSSL", "idna"]
-dev = ["service-identity[tests,mypy,docs,idna]", "pyOpenSSL"]
+dev = ["service-identity[tests,mypy,idna]", "pyOpenSSL"]
 
 [project.urls]
 Documentation = "https://service-identity.readthedocs.io/";
-"Bug Tracker" = "https://github.com/pyca/service-identity/issues";
-"Source Code" = "https://github.com/pyca/service-identity";
+Changelog = "https://service-identity.readthedocs.io/en/stable/changelog.html";
+GitHub = "https://github.com/pyca/service-identity";
 Funding = "https://github.com/sponsors/hynek";
 Tidelift = 
"https://tidelift.com/subscription/pkg/pypi-service-identity?utm_source=pypi-service-identity&utm_medium=pypi";
+Mastodon = "https://mastodon.social/@hynek";
+Twitter = "https://twitter.com/hynek";
 
 
 [tool.hatch.version]
@@ -78,7 +80,7 @@
 text = """
 ----
 
-[→ Complete 
Changelog](https://service-identity.readthedocs.io/en/stable/changelog.html)
+[Complete Changelog 
→](https://service-identity.readthedocs.io/en/stable/changelog.html)
 """
 
 
@@ -123,6 +125,13 @@
 ]
 
 
+[tool.interrogate]
+omit-covered-files = true
+verbose = 2
+fail-under = 100
+whitelist-regex = ["test_.*"]
+
+
 [tool.black]
 line-length = 79
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-23.1.0/src/service_identity/__init__.py 
new/service-identity-24.1.0/src/service_identity/__init__.py
--- old/service-identity-23.1.0/src/service_identity/__init__.py        
2023-06-14 09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/src/service_identity/__init__.py        
2024-01-14 08:05:19.000000000 +0100
@@ -37,7 +37,7 @@
         "__url__": "",
         "__email__": "",
     }
-    if name not in dunder_to_metadata.keys():
+    if name not in dunder_to_metadata:
         raise AttributeError(f"module {__name__} has no attribute {name}")
 
     import warnings
@@ -55,7 +55,7 @@
     meta = metadata("service-identity")
 
     if name in ("__uri__", "__url__"):
-        return meta["Project-URL"].split(" ", 1)[-1]
+        return meta["Project-URL"].split(" ", 1)[1]
 
     if name == "__email__":
         return meta["Author-email"].split("<", 1)[1].rstrip(">")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-23.1.0/src/service_identity/cryptography.py 
new/service-identity-24.1.0/src/service_identity/cryptography.py
--- old/service-identity-23.1.0/src/service_identity/cryptography.py    
2023-06-14 09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/src/service_identity/cryptography.py    
2024-01-14 08:05:19.000000000 +0100
@@ -40,22 +40,31 @@
 def verify_certificate_hostname(
     certificate: Certificate, hostname: str
 ) -> None:
-    """
+    r"""
     Verify whether *certificate* is valid for *hostname*.
 
-    .. note:: Nothing is verified about the *authority* of the certificate;
-       the caller must verify that the certificate chains to an appropriate
-       trust root themselves.
-
-    :param certificate: A *cryptography* X509 certificate object.
-    :param hostname: The hostname that *certificate* should be valid for.
-
-    :raises service_identity.VerificationError: If *certificate* is not valid
-        for *hostname*.
-    :raises service_identity.CertificateError: If *certificate* contains
-        invalid / unexpected data.
-
-    :returns: ``None``
+    .. note::
+        Nothing is verified about the *authority* of the certificate;
+        the caller must verify that the certificate chains to an appropriate
+        trust root themselves.
+
+    Args:
+        certificate: A *cryptography* X509 certificate object.
+
+        hostname: The hostname that *certificate* should be valid for.
+
+    Raises:
+        service_identity.VerificationError:
+            If *certificate* is not valid for *hostname*.
+
+        service_identity.CertificateError:
+            If *certificate* contains invalid / unexpected data. This includes
+            the case where the certificate contains no `subjectAltName`\ s.
+
+    .. versionchanged:: 24.1.0
+        :exc:`~service_identity.CertificateError` is raised if the certificate
+        contains no ``subjectAltName``\ s instead of
+        :exc:`~service_identity.VerificationError`.
     """
     verify_service_identity(
         cert_patterns=extract_patterns(certificate),
@@ -67,25 +76,35 @@
 def verify_certificate_ip_address(
     certificate: Certificate, ip_address: str
 ) -> None:
-    """
+    r"""
     Verify whether *certificate* is valid for *ip_address*.
 
-    .. note:: Nothing is verified about the *authority* of the certificate;
-       the caller must verify that the certificate chains to an appropriate
-       trust root themselves.
-
-    :param certificate: A *cryptography* X509 certificate object.
-    :param ip_address: The IP address that *connection* should be valid
-        for.  Can be an IPv4 or IPv6 address.
-
-    :raises service_identity.VerificationError: If *certificate* is not valid
-        for *ip_address*.
-    :raises service_identity.CertificateError: If *certificate* contains
-        invalid / unexpected data.
-
-    :returns: ``None``
+    .. note::
+        Nothing is verified about the *authority* of the certificate;
+        the caller must verify that the certificate chains to an appropriate
+        trust root themselves.
+
+    Args:
+        certificate: A *cryptography* X509 certificate object.
+
+        ip_address:
+            The IP address that *connection* should be valid for.  Can be an
+            IPv4 or IPv6 address.
+
+    Raises:
+        service_identity.VerificationError:
+            If *certificate* is not valid for *ip_address*.
+
+        service_identity.CertificateError:
+            If *certificate* contains invalid / unexpected data. This includes
+            the case where the certificate contains no ``subjectAltName``\ s.
 
     .. versionadded:: 18.1.0
+
+    .. versionchanged:: 24.1.0
+        :exc:`~service_identity.CertificateError` is raised if the certificate
+        contains no ``subjectAltName``\ s instead of
+        :exc:`~service_identity.VerificationError`.
     """
     verify_service_identity(
         cert_patterns=extract_patterns(certificate),
@@ -101,9 +120,11 @@
     """
     Extract all valid ID patterns from a certificate for service verification.
 
-    :param cert: The certificate to be dissected.
+    Args:
+        cert: The certificate to be dissected.
 
-    :return: List of IDs.
+    Returns:
+        List of IDs.
 
     .. versionchanged:: 23.1.0
        ``commonName`` is not used as a fallback anymore.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-23.1.0/src/service_identity/exceptions.py 
new/service-identity-24.1.0/src/service_identity/exceptions.py
--- old/service-identity-23.1.0/src/service_identity/exceptions.py      
2023-06-14 09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/src/service_identity/exceptions.py      
2024-01-14 08:05:19.000000000 +0100
@@ -72,6 +72,9 @@
 
 
 class CertificateError(Exception):
-    """
+    r"""
     Certificate contains invalid or unexpected data.
+
+    This includes the case where s certificate contains no
+    ``subjectAltName``\ s.
     """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-23.1.0/src/service_identity/hazmat.py 
new/service-identity-24.1.0/src/service_identity/hazmat.py
--- old/service-identity-23.1.0/src/service_identity/hazmat.py  2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/src/service_identity/hazmat.py  2024-01-14 
08:05:19.000000000 +0100
@@ -50,6 +50,11 @@
     *obligatory_ids* must be both present and match.  *optional_ids* must match
     if a pattern of the respective type is present.
     """
+    if not cert_patterns:
+        raise CertificateError(
+            "Certificate does not contain any `subjectAltName`s."
+        )
+
     errors = []
     matches = _find_matches(cert_patterns, obligatory_ids) + _find_matches(
         cert_patterns, optional_ids
@@ -83,8 +88,8 @@
     """
     Search for matching certificate patterns and service_ids.
 
-    :param service_ids: List of service IDs like DNS_ID.
-    :type service_ids: `list`
+    Args:
+        service_ids: List of service IDs like DNS_ID.
     """
     matches = []
     for sid in service_ids:
@@ -102,9 +107,11 @@
     """
     Check whether *pattern* could be/match an IP address.
 
-    :param pattern: A pattern for a host name.
+    Args:
+        pattern: A pattern for a host name.
 
-    :return: `True` if *pattern* could be an IP address, else `False`.
+    Returns:
+        `True` if *pattern* could be an IP address, else `False`.
     """
     if isinstance(pattern, bytes):
         try:
@@ -426,15 +433,13 @@
     cnt = cert_pattern.count(b"*")
     if cnt > 1:
         raise CertificateError(
-            "Certificate's DNS-ID {!r} contains too many wildcards.".format(
-                cert_pattern
-            )
+            f"Certificate's DNS-ID {cert_pattern!r} contains too many 
wildcards."
         )
     parts = cert_pattern.split(b".")
     if len(parts) < 3:
         raise CertificateError(
-            "Certificate's DNS-ID {!r} has too few host components for "
-            "wildcard usage.".format(cert_pattern)
+            f"Certificate's DNS-ID {cert_pattern!r} has too few host 
components for "
+            "wildcard usage."
         )
     # We assume there will always be only one wildcard allowed.
     if b"*" not in parts[0]:
@@ -444,9 +449,7 @@
         )
     if any(not len(p) for p in parts):
         raise CertificateError(
-            "Certificate's DNS-ID {!r} contains empty parts.".format(
-                cert_pattern
-            )
+            f"Certificate's DNS-ID {cert_pattern!r} contains empty parts."
         )
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/service-identity-23.1.0/src/service_identity/pyopenssl.py 
new/service-identity-24.1.0/src/service_identity/pyopenssl.py
--- old/service-identity-23.1.0/src/service_identity/pyopenssl.py       
2023-06-14 09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/src/service_identity/pyopenssl.py       
2024-01-14 08:05:19.000000000 +0100
@@ -37,19 +37,28 @@
 
 
 def verify_hostname(connection: Connection, hostname: str) -> None:
-    """
+    r"""
     Verify whether the certificate of *connection* is valid for *hostname*.
 
-    :param connection: A pyOpenSSL connection object.
-    :param hostname: The hostname that *connection* should be connected to.
+    Args:
+        connection: A pyOpenSSL connection object.
 
-    :raises service_identity.VerificationError: If *connection* does not
-        provide a certificate that is valid for *hostname*.
-    :raises service_identity.CertificateError: If the certificate chain of
-        *connection* contains a certificate that contains invalid/unexpected
-        data.
+        hostname: The hostname that *connection* should be connected to.
 
-    :returns: ``None``
+    Raises:
+        service_identity.VerificationError:
+            If *connection* does not provide a certificate that is valid for
+            *hostname*.
+
+        service_identity.CertificateError:
+            If certificate provided by *connection* contains invalid /
+            unexpected data. This includes the case where the certificate
+            contains no ``subjectAltName``\ s.
+
+    .. versionchanged:: 24.1.0
+        :exc:`~service_identity.CertificateError` is raised if the certificate
+        contains no ``subjectAltName``\ s instead of
+        :exc:`~service_identity.VerificationError`.
     """
     verify_service_identity(
         cert_patterns=extract_patterns(
@@ -61,22 +70,31 @@
 
 
 def verify_ip_address(connection: Connection, ip_address: str) -> None:
-    """
+    r"""
     Verify whether the certificate of *connection* is valid for *ip_address*.
 
-    :param connection: A pyOpenSSL connection object.
-    :param ip_address: The IP address that *connection* should be connected to.
-        Can be an IPv4 or IPv6 address.
-
-    :raises service_identity.VerificationError: If *connection* does not
-        provide a certificate that is valid for *ip_address*.
-    :raises service_identity.CertificateError: If the certificate chain of
-        *connection* contains a certificate that contains invalid/unexpected
-        data.
+    Args:
+        connection: A pyOpenSSL connection object.
 
-    :returns: ``None``
+        ip_address:
+            The IP address that *connection* should be connected to. Can be an
+            IPv4 or IPv6 address.
+
+    Raises:
+        service_identity.VerificationError:
+            If *connection* does not provide a certificate that is valid for
+            *ip_address*.
+
+        service_identity.CertificateError:
+            If the certificate chain of *connection* contains a certificate
+            that contains invalid/unexpected data.
 
     .. versionadded:: 18.1.0
+
+    .. versionchanged:: 24.1.0
+        :exc:`~service_identity.CertificateError` is raised if the certificate
+        contains no ``subjectAltName``\ s instead of
+        :exc:`~service_identity.VerificationError`.
     """
     verify_service_identity(
         cert_patterns=extract_patterns(
@@ -94,9 +112,11 @@
     """
     Extract all valid ID patterns from a certificate for service verification.
 
-    :param cert: The certificate to be dissected.
+    Args:
+        cert: The certificate to be dissected.
 
-    :return: List of IDs.
+    Returns:
+        List of IDs.
 
     .. versionchanged:: 23.1.0
        ``commonName`` is not used as a fallback anymore.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/tests/certificates.py 
new/service-identity-24.1.0/tests/certificates.py
--- old/service-identity-23.1.0/tests/certificates.py   1970-01-01 
01:00:00.000000000 +0100
+++ new/service-identity-24.1.0/tests/certificates.py   2024-01-14 
08:05:19.000000000 +0100
@@ -0,0 +1,142 @@
+from cryptography.hazmat.backends import default_backend
+from cryptography.x509 import load_pem_x509_certificate
+
+from service_identity.cryptography import extract_patterns
+
+
+# Test certificates
+
+PEM_DNS_ONLY = b"""\
+-----BEGIN CERTIFICATE-----
+MIIGbjCCBVagAwIBAgIDCesrMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
+TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
+YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg
+MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTMwNDEwMTk1ODA5
+WhcNMTQwNDExMTkyODAwWjB1MRkwFwYDVQQNExBTN2xiQ3Q3TjJSNHQ5bzhKMQsw
+CQYDVQQGEwJVUzEeMBwGA1UEAxMVd3d3LnR3aXN0ZWRtYXRyaXguY29tMSswKQYJ
+KoZIhvcNAQkBFhxwb3N0bWFzdGVyQHR3aXN0ZWRtYXRyaXguY29tMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxUH8iDxIEiDcMQb8kr/JTYXDGuE8ISQA
+uw/gBqpvHIvCgPBkZpvjQLA23rnUZm1S3VG5MIq6gZVdtl9LFIfokMPGgY9EZng8
+BaI+6Y36cMtubnzW53OZb7yLQQyg+rjuwjvJOY33ZulEthxhdB3km1Leb67iE9v7
+dpyKeJ/8m2IWD37HCtXIEnp9ZqWOZkAPzlzDt6oNxj0s/l3z23+XqZdr+kmlh9U+
+VWBTPppO4AJNwSqbBd0PgIozbYsp6urxSr40YQkIYFOOZQNs7HETJE71Ia7DQcUD
+kUF1jZSYZnhVQwGPisqQLGodt9q9p2BhpSf0cUm02uKKzYi5A2h7UQIDAQABo4IC
+7TCCAukwCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gwEwYDVR0lBAwwCgYIKwYBBQUH
+AwEwHQYDVR0OBBYEFGeuUvDrFHkl7Krl/+rlv1FsnsU6MB8GA1UdIwQYMBaAFOtC
+NNCYsKuf9BtrCPfMZC7vDixFMDMGA1UdEQQsMCqCFXd3dy50d2lzdGVkbWF0cml4
+LmNvbYIRdHdpc3RlZG1hdHJpeC5jb20wggFWBgNVHSAEggFNMIIBSTAIBgZngQwB
+AgEwggE7BgsrBgEEAYG1NwECAzCCASowLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cu
+c3RhcnRzc2wuY29tL3BvbGljeS5wZGYwgfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0
+Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MAMCAQEagb5UaGlzIGNlcnRpZmlj
+YXRlIHdhcyBpc3N1ZWQgYWNjb3JkaW5nIHRvIHRoZSBDbGFzcyAxIFZhbGlkYXRp
+b24gcmVxdWlyZW1lbnRzIG9mIHRoZSBTdGFydENvbSBDQSBwb2xpY3ksIHJlbGlh
+bmNlIG9ubHkgZm9yIHRoZSBpbnRlbmRlZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ug
+b2YgdGhlIHJlbHlpbmcgcGFydHkgb2JsaWdhdGlvbnMuMDUGA1UdHwQuMCwwKqAo
+oCaGJGh0dHA6Ly9jcmwuc3RhcnRzc2wuY29tL2NydDEtY3JsLmNybDCBjgYIKwYB
+BQUHAQEEgYEwfzA5BggrBgEFBQcwAYYtaHR0cDovL29jc3Auc3RhcnRzc2wuY29t
+L3N1Yi9jbGFzczEvc2VydmVyL2NhMEIGCCsGAQUFBzAChjZodHRwOi8vYWlhLnN0
+YXJ0c3NsLmNvbS9jZXJ0cy9zdWIuY2xhc3MxLnNlcnZlci5jYS5jcnQwIwYDVR0S
+BBwwGoYYaHR0cDovL3d3dy5zdGFydHNzbC5jb20vMA0GCSqGSIb3DQEBBQUAA4IB
+AQCN85dUStYjHmWdXthpAqJcS3KD2JP6N9egOz7FTcToXLW8Kl5a2SUVaJv8Fzs+
+wtbPJQSm0LyGtfdrR6iKFPf28Vm/VkYXPiOV08GD9B7yl1SjktXOsGMPlOHU8YQZ
+DEsHOrRvaZBSA1VtBQjYnoO0pDVu9QwDLAPLFvFice2PN803HuMFIwcuQSIrh4nq
+PqwitBZ6nPPHz7aSiAut/+txK3EZll0d+hl0H3Phd+ICeITYhNkLe90k7l1IFpET
+fJiBDvG/iDAJISgkrR1heuX/e+yWfx7RvqGlMLIE35d+0MhWy92Jzejbl8fJdr4C
+Kulh/pV07MWAUZxscUPtWmPo
+-----END CERTIFICATE-----"""
+
+DNS_IDS = extract_patterns(
+    load_pem_x509_certificate(PEM_DNS_ONLY, default_backend())
+)
+
+PEM_CN_ONLY = b"""\
+-----BEGIN CERTIFICATE-----
+MIIDlzCCAn+gAwIBAgIUNS9qfJRgoLrr68mAfmhzBior0JAwDQYJKoZIhvcNAQEL
+BQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20w
+HhcNMjMwNjA4MDkwNDAxWhcNNDkwMTI3MDkwNDAxWjBbMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALltBNRAyeFczK2kdKTkW6E3/+rptXBcaw3SyltY/Ft8DPe3
+lW/GWbbgAU7ceYC0LkYxOEnyQTlXhTyLHSk+PCQLi2ESgwWGnGY5Eusk1DRJ162q
+hLUkmz25KdTILnh402fgoziF2RU6PnA7iIwAVntT5uh4S1yPW7TWkdPsE0tc1yBx
+3SAHTXDkoahjKSot35Q87Jw92fxUd7WqVmDrgcLMvp8rXNjdg+HG4fQGoMcSQq2Z
+nVPIoWSTOGdL6SzSs6Wvllz3n7YWShTqp9CmGctCGubt02fHF+8G/dMTTukntG4q
+EjBtVfeDFx8yXUdOgN4egFxZGYATAUsLcyzNhQcCAwEAAaNTMFEwHQYDVR0OBBYE
+FDkuqwU8KxIvwAwMqVpNFYlgd6WbMB8GA1UdIwQYMBaAFDkuqwU8KxIvwAwMqVpN
+FYlgd6WbMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ9Nb/C0
+2gkxhWm3S2/9onPMhFOxDLsYxBvZtJMeOvRRrJaxN2m0aXkm0Ww8Bq0qEegZE51m
+0T57IOS256rQlxEnZNeAWT2tf8FEqQGjbI1c/prj420e6afn18x8CSQgiOG4PJ+S
+YfufRjSHAYwiiNi+tiKBQ8jOuayFhih9/GsDJvAMW0HjaYZyG6jlA9p4bL1rSqD3
+gE+upYhkpVN1lFRZAhxRZOVIqJV/ppHp9RAuawsUdSNKm+rnlaExJKoVys+tZL9+
+djWWQVqnttYocQQ0umX4ydCTr1RM+7ziwWTR5kpOESA/gZePZQqeH9k+XrPEQtLn
+6QBxk+Ft53ZPVso=
+-----END CERTIFICATE-----
+"""
+
+
+PEM_OTHER_NAME = b"""\
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIJAIS0TSddIw6cMA0GCSqGSIb3DQEBBQUAMGwxFDASBgNV
+BAMTC2V4YW1wbGUuY29tMSAwHgYJKoZIhvcNAQkBFhFib2d1c0BleGFtcGxlLmNv
+bTEUMBIGA1UEChMLRXhhbXBsZSBJbmMxDzANBgNVBAcTBkJlcmxpbjELMAkGA1UE
+BhMCREUwHhcNMTQwMzA2MTYyNTA5WhcNMTUwMzA2MTYyNTA5WjBsMRQwEgYDVQQD
+EwtleGFtcGxlLmNvbTEgMB4GCSqGSIb3DQEJARYRYm9ndXNAZXhhbXBsZS5jb20x
+FDASBgNVBAoTC0V4YW1wbGUgSW5jMQ8wDQYDVQQHEwZCZXJsaW4xCzAJBgNVBAYT
+AkRFMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxGQUcOc8cAdzSJbk
+0eCHA1qBY2XwRG8YQzihgQS8Ey+3j69Xf0mtWOlL6v23v8J1ilA7ERs87Y4nbV/9
+GJVhC/jTMZmrC6ogwtVIl1wL8sTiHaQZ/4pbpx57YW3qCdefLQrZqAMUgAe20z0G
+YVU97u5EGXHYahG4TnB3xN6Qd3BGKP7K69Lb7ZOES2Esq533AZxZShseYR4JNYAc
+2anag2/DpHw6k8ZaxtWHR4SmxlkCoW5IPK0YypeUY91PFY+dxJQEewtisfALKltE
+SYnOTWkc0K9YuLuYVogx0K285wX4/Yha2wyo6KSAm0txJayOhcrEP2/34aWCl62m
+xOtPbQIDAQABo4GgMIGdMIGaBgNVHREEgZIwgY+CDSouZXhhbXBsZS5uZXSCC2V4
+YW1wbGUuY29thwTAqAABhxAAEwAAAAAAAAAAAAAAAAAXhhNodHRwOi8vZXhhbXBs
+ZS5jb20voCYGCCsGAQUFBwgHoBoWGF94bXBwLWNsaWVudC5leGFtcGxlLm5ldKAc
+BggrBgEFBQcIBaAQDA5pbS5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA
+ACVQcgEKzXEw0M9mmVFFXL2SyDk/4oaDFZbnNfyUp+H7bnxdVBG2M3DzQQLw5yH5
+k4GNPvHOKshBbaFcZWiG1sdrfQJy/UjIWnaC5410npfBv7kJWafKKxZzMq3gp4rd
+jPO2LxuWcYVOnUtA3CBe12tRV7ynGU8KmKOsU9bOWhUKo8DJ4a6XHB+YwXeOTPyU
+mG7XBpQebT01I3OijFJ+apKR2ubjwZE8l1+BAlTzHyUmmcTTWTQk8FTFcP3nZuIr
+VyudDBMASs4yVGHzQxmMalYYzd7ZDzM1NrgfG1KyKWqZEA0MzUxiYdUbZN79xL52
+EyKUOXPHw78G6zsVmAE1Aw==
+-----END CERTIFICATE-----"""
+
+
+PEM_EVERYTHING = b"""\
+-----BEGIN CERTIFICATE-----
+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
+3HotzNMgpPpx8C2KKLKKaiIGRY0pg/WC6w==
+-----END CERTIFICATE-----"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/tests/conftest.py 
new/service-identity-24.1.0/tests/conftest.py
--- old/service-identity-23.1.0/tests/conftest.py       2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/tests/conftest.py       2024-01-14 
08:05:19.000000000 +0100
@@ -18,11 +18,7 @@
         openssl_version = "n/a"
         pyopenssl_version = "missing"
 
-    return """\
-OpenSSL: {openssl}
-pyOpenSSL: {pyOpenSSL}
-cryptography: {cryptography}""".format(
-        openssl=openssl_version,
-        pyOpenSSL=pyopenssl_version,
-        cryptography=cryptography.__version__,
-    )
+    return f"""\
+OpenSSL: {openssl_version}
+pyOpenSSL: {pyopenssl_version}
+cryptography: {cryptography.__version__}"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/tests/test_cryptography.py 
new/service-identity-24.1.0/tests/test_cryptography.py
--- old/service-identity-23.1.0/tests/test_cryptography.py      2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/tests/test_cryptography.py      2024-01-14 
08:05:19.000000000 +0100
@@ -12,6 +12,7 @@
     verify_certificate_ip_address,
 )
 from service_identity.exceptions import (
+    CertificateError,
     DNSMismatch,
     IPAddressMismatch,
     VerificationError,
@@ -24,7 +25,12 @@
     URIPattern,
 )
 
-from .util import PEM_CN_ONLY, PEM_DNS_ONLY, PEM_EVERYTHING, PEM_OTHER_NAME
+from .certificates import (
+    PEM_CN_ONLY,
+    PEM_DNS_ONLY,
+    PEM_EVERYTHING,
+    PEM_OTHER_NAME,
+)
 
 
 backend = default_backend()
@@ -35,6 +41,29 @@
 
 
 class TestPublicAPI:
+    def test_no_cert_patterns_hostname(self):
+        """
+        A certificate without subjectAltNames raises a helpful
+        CertificateError.
+        """
+        with pytest.raises(
+            CertificateError,
+            match="Certificate does not contain any `subjectAltName`s.",
+        ):
+            verify_certificate_hostname(X509_CN_ONLY, "example.com")
+
+    @pytest.mark.parametrize("ip", ["203.0.113.0", "2001:db8::"])
+    def test_no_cert_patterns_ip_address(self, ip):
+        """
+        A certificate without subjectAltNames raises a helpful
+        CertificateError.
+        """
+        with pytest.raises(
+            CertificateError,
+            match="Certificate does not contain any `subjectAltName`s.",
+        ):
+            verify_certificate_ip_address(X509_CN_ONLY, ip)
+
     def test_certificate_verify_hostname_ok(self):
         """
         verify_certificate_hostname succeeds if the hostnames match.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/tests/test_hazmat.py 
new/service-identity-24.1.0/tests/test_hazmat.py
--- old/service-identity-23.1.0/tests/test_hazmat.py    2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/tests/test_hazmat.py    2024-01-14 
08:05:19.000000000 +0100
@@ -30,8 +30,8 @@
     verify_service_identity,
 )
 
+from .certificates import DNS_IDS
 from .test_cryptography import CERT_EVERYTHING
-from .util import DNS_IDS
 
 
 try:
@@ -45,6 +45,18 @@
     Simple integration tests for verify_service_identity.
     """
 
+    def test_no_cert_patterns(self):
+        """
+        Empty cert patterns raise a helpful CertificateError.
+        """
+        with pytest.raises(
+            CertificateError,
+            match="Certificate does not contain any `subjectAltName`s.",
+        ):
+            verify_service_identity(
+                cert_patterns=[], obligatory_ids=[], optional_ids=[]
+            )
+
     def test_dns_id_success(self):
         """
         Return pairs of certificate ids and service ids on matches.
@@ -693,7 +705,7 @@
             assert repr(e) == str(e)
             assert str(e) != ""
 
-    @pytest.mark.parametrize("proto", range(0, pickle.HIGHEST_PROTOCOL + 1))
+    @pytest.mark.parametrize("proto", range(pickle.HIGHEST_PROTOCOL + 1))
     @pytest.mark.parametrize(
         "exc",
         [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/tests/test_packaging.py 
new/service-identity-24.1.0/tests/test_packaging.py
--- old/service-identity-23.1.0/tests/test_packaging.py 2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/tests/test_packaging.py 2024-01-14 
08:05:19.000000000 +0100
@@ -1,16 +1,10 @@
-import sys
+from importlib import metadata
 
 import pytest
 
 import service_identity
 
 
-if sys.version_info < (3, 8):
-    import importlib_metadata as metadata
-else:
-    from importlib import metadata
-
-
 class TestLegacyMetadataHack:
     def test_version(self):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/tests/test_pyopenssl.py 
new/service-identity-24.1.0/tests/test_pyopenssl.py
--- old/service-identity-23.1.0/tests/test_pyopenssl.py 2023-06-14 
09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/tests/test_pyopenssl.py 2024-01-14 
08:05:19.000000000 +0100
@@ -21,7 +21,12 @@
     verify_ip_address,
 )
 
-from .util import PEM_CN_ONLY, PEM_DNS_ONLY, PEM_EVERYTHING, PEM_OTHER_NAME
+from .certificates import (
+    PEM_CN_ONLY,
+    PEM_DNS_ONLY,
+    PEM_EVERYTHING,
+    PEM_OTHER_NAME,
+)
 
 
 if pytest.importorskip("OpenSSL"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/tests/util.py 
new/service-identity-24.1.0/tests/util.py
--- old/service-identity-23.1.0/tests/util.py   2023-06-14 09:54:35.000000000 
+0200
+++ new/service-identity-24.1.0/tests/util.py   1970-01-01 01:00:00.000000000 
+0100
@@ -1,142 +0,0 @@
-from cryptography.hazmat.backends import default_backend
-from cryptography.x509 import load_pem_x509_certificate
-
-from service_identity.cryptography import extract_patterns
-
-
-# Test certificates
-
-PEM_DNS_ONLY = b"""\
------BEGIN CERTIFICATE-----
-MIIGbjCCBVagAwIBAgIDCesrMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
-TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
-YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg
-MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTMwNDEwMTk1ODA5
-WhcNMTQwNDExMTkyODAwWjB1MRkwFwYDVQQNExBTN2xiQ3Q3TjJSNHQ5bzhKMQsw
-CQYDVQQGEwJVUzEeMBwGA1UEAxMVd3d3LnR3aXN0ZWRtYXRyaXguY29tMSswKQYJ
-KoZIhvcNAQkBFhxwb3N0bWFzdGVyQHR3aXN0ZWRtYXRyaXguY29tMIIBIjANBgkq
-hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxUH8iDxIEiDcMQb8kr/JTYXDGuE8ISQA
-uw/gBqpvHIvCgPBkZpvjQLA23rnUZm1S3VG5MIq6gZVdtl9LFIfokMPGgY9EZng8
-BaI+6Y36cMtubnzW53OZb7yLQQyg+rjuwjvJOY33ZulEthxhdB3km1Leb67iE9v7
-dpyKeJ/8m2IWD37HCtXIEnp9ZqWOZkAPzlzDt6oNxj0s/l3z23+XqZdr+kmlh9U+
-VWBTPppO4AJNwSqbBd0PgIozbYsp6urxSr40YQkIYFOOZQNs7HETJE71Ia7DQcUD
-kUF1jZSYZnhVQwGPisqQLGodt9q9p2BhpSf0cUm02uKKzYi5A2h7UQIDAQABo4IC
-7TCCAukwCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gwEwYDVR0lBAwwCgYIKwYBBQUH
-AwEwHQYDVR0OBBYEFGeuUvDrFHkl7Krl/+rlv1FsnsU6MB8GA1UdIwQYMBaAFOtC
-NNCYsKuf9BtrCPfMZC7vDixFMDMGA1UdEQQsMCqCFXd3dy50d2lzdGVkbWF0cml4
-LmNvbYIRdHdpc3RlZG1hdHJpeC5jb20wggFWBgNVHSAEggFNMIIBSTAIBgZngQwB
-AgEwggE7BgsrBgEEAYG1NwECAzCCASowLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cu
-c3RhcnRzc2wuY29tL3BvbGljeS5wZGYwgfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0
-Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MAMCAQEagb5UaGlzIGNlcnRpZmlj
-YXRlIHdhcyBpc3N1ZWQgYWNjb3JkaW5nIHRvIHRoZSBDbGFzcyAxIFZhbGlkYXRp
-b24gcmVxdWlyZW1lbnRzIG9mIHRoZSBTdGFydENvbSBDQSBwb2xpY3ksIHJlbGlh
-bmNlIG9ubHkgZm9yIHRoZSBpbnRlbmRlZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ug
-b2YgdGhlIHJlbHlpbmcgcGFydHkgb2JsaWdhdGlvbnMuMDUGA1UdHwQuMCwwKqAo
-oCaGJGh0dHA6Ly9jcmwuc3RhcnRzc2wuY29tL2NydDEtY3JsLmNybDCBjgYIKwYB
-BQUHAQEEgYEwfzA5BggrBgEFBQcwAYYtaHR0cDovL29jc3Auc3RhcnRzc2wuY29t
-L3N1Yi9jbGFzczEvc2VydmVyL2NhMEIGCCsGAQUFBzAChjZodHRwOi8vYWlhLnN0
-YXJ0c3NsLmNvbS9jZXJ0cy9zdWIuY2xhc3MxLnNlcnZlci5jYS5jcnQwIwYDVR0S
-BBwwGoYYaHR0cDovL3d3dy5zdGFydHNzbC5jb20vMA0GCSqGSIb3DQEBBQUAA4IB
-AQCN85dUStYjHmWdXthpAqJcS3KD2JP6N9egOz7FTcToXLW8Kl5a2SUVaJv8Fzs+
-wtbPJQSm0LyGtfdrR6iKFPf28Vm/VkYXPiOV08GD9B7yl1SjktXOsGMPlOHU8YQZ
-DEsHOrRvaZBSA1VtBQjYnoO0pDVu9QwDLAPLFvFice2PN803HuMFIwcuQSIrh4nq
-PqwitBZ6nPPHz7aSiAut/+txK3EZll0d+hl0H3Phd+ICeITYhNkLe90k7l1IFpET
-fJiBDvG/iDAJISgkrR1heuX/e+yWfx7RvqGlMLIE35d+0MhWy92Jzejbl8fJdr4C
-Kulh/pV07MWAUZxscUPtWmPo
------END CERTIFICATE-----"""
-
-DNS_IDS = extract_patterns(
-    load_pem_x509_certificate(PEM_DNS_ONLY, default_backend())
-)
-
-PEM_CN_ONLY = b"""\
------BEGIN CERTIFICATE-----
-MIIDlzCCAn+gAwIBAgIUNS9qfJRgoLrr68mAfmhzBior0JAwDQYJKoZIhvcNAQEL
-BQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
-GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20w
-HhcNMjMwNjA4MDkwNDAxWhcNNDkwMTI3MDkwNDAxWjBbMQswCQYDVQQGEwJBVTET
-MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
-dHkgTHRkMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBALltBNRAyeFczK2kdKTkW6E3/+rptXBcaw3SyltY/Ft8DPe3
-lW/GWbbgAU7ceYC0LkYxOEnyQTlXhTyLHSk+PCQLi2ESgwWGnGY5Eusk1DRJ162q
-hLUkmz25KdTILnh402fgoziF2RU6PnA7iIwAVntT5uh4S1yPW7TWkdPsE0tc1yBx
-3SAHTXDkoahjKSot35Q87Jw92fxUd7WqVmDrgcLMvp8rXNjdg+HG4fQGoMcSQq2Z
-nVPIoWSTOGdL6SzSs6Wvllz3n7YWShTqp9CmGctCGubt02fHF+8G/dMTTukntG4q
-EjBtVfeDFx8yXUdOgN4egFxZGYATAUsLcyzNhQcCAwEAAaNTMFEwHQYDVR0OBBYE
-FDkuqwU8KxIvwAwMqVpNFYlgd6WbMB8GA1UdIwQYMBaAFDkuqwU8KxIvwAwMqVpN
-FYlgd6WbMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ9Nb/C0
-2gkxhWm3S2/9onPMhFOxDLsYxBvZtJMeOvRRrJaxN2m0aXkm0Ww8Bq0qEegZE51m
-0T57IOS256rQlxEnZNeAWT2tf8FEqQGjbI1c/prj420e6afn18x8CSQgiOG4PJ+S
-YfufRjSHAYwiiNi+tiKBQ8jOuayFhih9/GsDJvAMW0HjaYZyG6jlA9p4bL1rSqD3
-gE+upYhkpVN1lFRZAhxRZOVIqJV/ppHp9RAuawsUdSNKm+rnlaExJKoVys+tZL9+
-djWWQVqnttYocQQ0umX4ydCTr1RM+7ziwWTR5kpOESA/gZePZQqeH9k+XrPEQtLn
-6QBxk+Ft53ZPVso=
------END CERTIFICATE-----
-"""
-
-
-PEM_OTHER_NAME = b"""\
------BEGIN CERTIFICATE-----
-MIID/DCCAuSgAwIBAgIJAIS0TSddIw6cMA0GCSqGSIb3DQEBBQUAMGwxFDASBgNV
-BAMTC2V4YW1wbGUuY29tMSAwHgYJKoZIhvcNAQkBFhFib2d1c0BleGFtcGxlLmNv
-bTEUMBIGA1UEChMLRXhhbXBsZSBJbmMxDzANBgNVBAcTBkJlcmxpbjELMAkGA1UE
-BhMCREUwHhcNMTQwMzA2MTYyNTA5WhcNMTUwMzA2MTYyNTA5WjBsMRQwEgYDVQQD
-EwtleGFtcGxlLmNvbTEgMB4GCSqGSIb3DQEJARYRYm9ndXNAZXhhbXBsZS5jb20x
-FDASBgNVBAoTC0V4YW1wbGUgSW5jMQ8wDQYDVQQHEwZCZXJsaW4xCzAJBgNVBAYT
-AkRFMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxGQUcOc8cAdzSJbk
-0eCHA1qBY2XwRG8YQzihgQS8Ey+3j69Xf0mtWOlL6v23v8J1ilA7ERs87Y4nbV/9
-GJVhC/jTMZmrC6ogwtVIl1wL8sTiHaQZ/4pbpx57YW3qCdefLQrZqAMUgAe20z0G
-YVU97u5EGXHYahG4TnB3xN6Qd3BGKP7K69Lb7ZOES2Esq533AZxZShseYR4JNYAc
-2anag2/DpHw6k8ZaxtWHR4SmxlkCoW5IPK0YypeUY91PFY+dxJQEewtisfALKltE
-SYnOTWkc0K9YuLuYVogx0K285wX4/Yha2wyo6KSAm0txJayOhcrEP2/34aWCl62m
-xOtPbQIDAQABo4GgMIGdMIGaBgNVHREEgZIwgY+CDSouZXhhbXBsZS5uZXSCC2V4
-YW1wbGUuY29thwTAqAABhxAAEwAAAAAAAAAAAAAAAAAXhhNodHRwOi8vZXhhbXBs
-ZS5jb20voCYGCCsGAQUFBwgHoBoWGF94bXBwLWNsaWVudC5leGFtcGxlLm5ldKAc
-BggrBgEFBQcIBaAQDA5pbS5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA
-ACVQcgEKzXEw0M9mmVFFXL2SyDk/4oaDFZbnNfyUp+H7bnxdVBG2M3DzQQLw5yH5
-k4GNPvHOKshBbaFcZWiG1sdrfQJy/UjIWnaC5410npfBv7kJWafKKxZzMq3gp4rd
-jPO2LxuWcYVOnUtA3CBe12tRV7ynGU8KmKOsU9bOWhUKo8DJ4a6XHB+YwXeOTPyU
-mG7XBpQebT01I3OijFJ+apKR2ubjwZE8l1+BAlTzHyUmmcTTWTQk8FTFcP3nZuIr
-VyudDBMASs4yVGHzQxmMalYYzd7ZDzM1NrgfG1KyKWqZEA0MzUxiYdUbZN79xL52
-EyKUOXPHw78G6zsVmAE1Aw==
------END CERTIFICATE-----"""
-
-
-PEM_EVERYTHING = b"""\
------BEGIN CERTIFICATE-----
-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
-3HotzNMgpPpx8C2KKLKKaiIGRY0pg/WC6w==
------END CERTIFICATE-----"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/service-identity-23.1.0/tox.ini 
new/service-identity-24.1.0/tox.ini
--- old/service-identity-23.1.0/tox.ini 2023-06-14 09:54:35.000000000 +0200
+++ new/service-identity-24.1.0/tox.ini 2024-01-14 08:05:19.000000000 +0100
@@ -25,7 +25,17 @@
     pyopenssl-oldest: PIP_CONSTRAINT = tests/constraints/oldest-pyopenssl.txt
 commands =
     coverage run -m pytest {posargs}
-    py311-pyopenssl-latest-idna: coverage run -m pytest --doctest-modules 
--doctest-glob='*.rst' {posargs}
+    py312-pyopenssl-latest-idna: coverage run -m pytest --doctest-modules 
--doctest-glob='*.rst' {posargs}
+
+
+[testenv:coverage-report]
+# keep in-sync with .python-version-default
+base_python = py312
+deps = coverage[toml]>=5.0.2
+skip_install = true
+commands =
+    coverage combine
+    coverage report
 
 
 [testenv:lint]
@@ -46,18 +56,25 @@
 
 [testenv:docs]
 # Keep in-sync with gh-actions and .readthedocs.yaml.
-base_python = py311
+base_python = py312
 extras = docs
 commands =
     sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html
     sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html
 
-
-[testenv:coverage-report]
-# keep in-sync with .python-version-default
-base_python = py311
-deps = coverage[toml]>=5.0.2
-skip_install = true
+[testenv:docs-watch]
+package = editable
+base_python = {[testenv:docs]base_python}
+extras = {[testenv:docs]extras}
+deps = watchfiles
 commands =
-    coverage combine
-    coverage report
+    watchfiles \
+        --ignore-paths docs/_build/ \
+        'sphinx-build -W -n --jobs auto -b html -d {envtmpdir}/doctrees docs 
docs/_build/html' \
+        src \
+        docs
+
+[testenv:docs-linkcheck]
+base_python = {[testenv:docs]base_python}
+extras = {[testenv:docs]extras}
+commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs 
docs/_build/html

Reply via email to