Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ This resolves [#548](https://github.com/JarryShaw/PyPCAPKit/issues/548), which r
- the API reference renders what the code declares. `Extractor._vfunc` keeps its annotation ([#1839](https://github.com/JarryShaw/PyPCAPKit/issues/1839), [#1867](https://github.com/JarryShaw/PyPCAPKit/pull/1867)), and the HTTP/2 frame schemas' and the engine classes' annotations resolve ([#1857](https://github.com/JarryShaw/PyPCAPKit/issues/1857), [#1870](https://github.com/JarryShaw/PyPCAPKit/pull/1870)). Under the pinned sphinx-autodoc-typehints 3.15.0 ([#1856](https://github.com/JarryShaw/PyPCAPKit/issues/1856), [#1877](https://github.com/JarryShaw/PyPCAPKit/pull/1877)), `conf.py` renders an `aenum` base as such, keeps a default as written (`byteorder=sys.byteorder` rather than the build host's value), gives a class the parameters of the `__init__` its `__new__` hides and an enumeration `(*values)`, keeps table ids unique, and stops the source index linking a missing page ([#1858](https://github.com/JarryShaw/PyPCAPKit/issues/1858) to [#1863](https://github.com/JarryShaw/PyPCAPKit/issues/1863), [#1881](https://github.com/JarryShaw/PyPCAPKit/pull/1881)). The generated const tables carry no version directives; each note is on the module's hand-written page instead ([#1884](https://github.com/JarryShaw/PyPCAPKit/issues/1884), [#1895](https://github.com/JarryShaw/PyPCAPKit/pull/1895)).
- `pcapkit-cli`'s help strings say what each option does: `-o` describes the output rather than the input, an extension is appended only under `--auto-extension`, and each format flag names the flags that take precedence over it ([#1796](https://github.com/JarryShaw/PyPCAPKit/issues/1796), [#1810](https://github.com/JarryShaw/PyPCAPKit/pull/1810)).
- the Conda package: its vendored dependencies are the package's `dependencies` and its `all` extra, with their requirements, shipped with their data files rather than as `.py` files only ([#1814](https://github.com/JarryShaw/PyPCAPKit/issues/1814), [#1821](https://github.com/JarryShaw/PyPCAPKit/pull/1821)); the `osx-64` package is built on an Intel runner, where converting the arm64 build shipped arm64 binaries ([#1822](https://github.com/JarryShaw/PyPCAPKit/issues/1822), [#1825](https://github.com/JarryShaw/PyPCAPKit/pull/1825)); and on Intel macOS it pins `cryptography` 48.0.1, which has a wheel for that platform where the newer pin has none ([#1827](https://github.com/JarryShaw/PyPCAPKit/issues/1827), [#1828](https://github.com/JarryShaw/PyPCAPKit/pull/1828)). `util/conda_vendor.py --check` reports a package pinned differently under one marker ([#1852](https://github.com/JarryShaw/PyPCAPKit/issues/1852), [#1865](https://github.com/JarryShaw/PyPCAPKit/pull/1865)); the platform pins move to `conda/platform-pins.toml`, each with a review-by date, and Conda Update moves them up ([#1851](https://github.com/JarryShaw/PyPCAPKit/issues/1851), [#1885](https://github.com/JarryShaw/PyPCAPKit/pull/1885)); and the release's conda leg installs none of pcapkit's extras and smoke-tests the package before uploading it ([#1854](https://github.com/JarryShaw/PyPCAPKit/issues/1854), [#1855](https://github.com/JarryShaw/PyPCAPKit/issues/1855), [#1868](https://github.com/JarryShaw/PyPCAPKit/pull/1868)).
- a wheel built with the oldest setuptools `[build-system] requires` allowed, without build isolation, left out `pcapkit/py.typed`, so type checkers read the installed package as untyped. Nothing declares the file, and setuptools ships it unasked only from 69.0.0, so that is the floor now, in `pyproject.toml` and the Conda recipe alike ([#2062](https://github.com/JarryShaw/PyPCAPKit/issues/2062), [#2064](https://github.com/JarryShaw/PyPCAPKit/pull/2064)).

---

Expand Down
4 changes: 2 additions & 2 deletions conda/pypcapkit/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ requirements:
- git
- python
- pip
- setuptools>=62.3.0
- setuptools>=69.0.0

# - dictdumper~=0.8.0 # for formatted output
# - chardet # for bytes decode
Expand All @@ -43,7 +43,7 @@ requirements:
host:
- python
- pip
- setuptools>=62.3.0
- setuptools>=69.0.0

# - dictdumper~=0.8.0 # for formatted output
# - chardet # for bytes decode
Expand Down
5 changes: 5 additions & 0 deletions docs/source/changelog/1.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3371,3 +3371,8 @@ Fixed
them up (:issue:`1851`, :pr:`1885`); and the release's conda leg installs none of
pcapkit's extras and smoke-tests the package before uploading it (:issue:`1854`,
:issue:`1855`, :pr:`1868`).
* a wheel built with the oldest setuptools ``[build-system] requires`` allowed,
without build isolation, left out ``pcapkit/py.typed``, so type checkers read the
installed package as untyped. Nothing declares the file, and setuptools ships it
unasked only from 69.0.0, so that is the floor now, in ``pyproject.toml`` and the
Conda recipe alike (:issue:`2062`, :pr:`2064`).
14 changes: 9 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
[build-system]
requires = [
# 62.3.0 is the first release whose ``package-data`` globs read ``**`` as any
# depth, which the ``pcapkit._extern`` entry below needs (#1814). Measured:
# 61.0.0 and 62.2.0 read it as ``*``, and ship only the files one directory
# down from it.
"setuptools>=62.3.0",
# 69.0.0 is the first release that ships ``pcapkit/py.typed`` unasked
# (pypa/setuptools#3136, in its NEWS.rst): nothing here declares the file, so
# a wheel built with 68.2.2 or older leaves it out and type checkers read the
# package as untyped (#2062). Measured on both sides of the bump. It also
# keeps what the earlier floor of 62.3.0 was for: ``package-data`` globs that
# read ``**`` as any depth, which the ``pcapkit._extern`` entry below needs
# (#1814) -- 61.0.0 and 62.2.0 read it as ``*``, measured. 69.0.0 still
# supports Python 3.8, the ``requires-python`` floor.
"setuptools>=69.0.0",
]
build-backend = "setuptools.build_meta"

Expand Down
76 changes: 76 additions & 0 deletions tests/project/test_setuptools_floor_2062.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
"""#2062: the setuptools floor is one that ships ``pcapkit/py.typed``.

Nothing declares ``py.typed``: :file:`MANIFEST.in` includes ``*.py`` alone, and
:file:`pyproject.toml` has no ``package-data`` entry for ``pcapkit``. setuptools
69.0.0 is the first release that packages it regardless (pypa/setuptools#3136,
in that release's ``NEWS.rst``), measured: a wheel built with 68.2.2 leaves it
out, and one built with 69.0.0 has it. Pinned here:

* ``[build-system] requires`` floors setuptools at 69.0.0 or later;
* every setuptools pin in :file:`conda/pypcapkit/meta.yaml` matches that floor.

These read the declarations, not a built wheel: they catch the floor being
lowered again, not a later setuptools release dropping the behaviour.

"""

from __future__ import annotations

import pathlib
import re
import unittest

try:
import tomllib
except ImportError: # pragma: no cover
tomllib = None # type: ignore[assignment]

#: Test tier (#1958): reads pyproject.toml and the conda recipe, the same on every leg.
__tier__ = 'LATEST'

ROOT = pathlib.Path(__file__).resolve().parents[2]

#: The first setuptools that packages ``py.typed`` without being told to (#2062).
FIRST_WITH_PY_TYPED = (69, 0, 0)

#: A ``setuptools>=X.Y.Z`` requirement, as either file spells it.
FLOOR = re.compile(r'^\s*(?:-\s*)?setuptools\s*>=\s*(\d+)\.(\d+)\.(\d+)\s*$')


def floors(lines: 'list[str]') -> 'list[tuple[int, int, int]]':
"""Every ``setuptools>=`` floor in ``lines``."""
return [tuple(int(part) for part in match.groups()) # type: ignore[misc]
for match in map(FLOOR.match, lines) if match]


class SetuptoolsFloorTests(unittest.TestCase):
"""The build floor packages ``py.typed``, and conda agrees with it."""

def setUp(self) -> None:
if tomllib is None: # pragma: no cover
self.skipTest('tomllib needs Python 3.11')
pyproject = tomllib.loads((ROOT / 'pyproject.toml').read_text(encoding='utf-8'))
requires = pyproject['build-system']['requires']
self.assertEqual([text for text in requires if text.startswith('setuptools')],
[text for text in requires if FLOOR.match(text)],
'every setuptools requirement should be a plain ">=" floor')
self.floors = floors(requires)

def test_build_floor_ships_py_typed(self) -> None:
self.assertEqual(len(self.floors), 1, self.floors)
self.assertGreaterEqual(self.floors[0], FIRST_WITH_PY_TYPED,
'setuptools older than 69.0.0 builds wheels without pcapkit/py.typed')

def test_conda_pins_match_the_build_floor(self) -> None:
lines = (ROOT / 'conda' / 'pypcapkit' / 'meta.yaml').read_text(encoding='utf-8').splitlines()
pins = floors(lines)
self.assertTrue(pins, 'the conda recipe pins no setuptools floor')
self.assertEqual(set(pins), set(self.floors))

def test_py_typed_exists(self) -> None:
self.assertTrue((ROOT / 'pcapkit' / 'py.typed').is_file())


if __name__ == '__main__':
unittest.main()
Loading