diff --git a/.github/workflows/build-and-upload-to-pypi.yml b/.github/workflows/build-and-upload-to-pypi.yml index 44b6a90..3bc449a 100644 --- a/.github/workflows/build-and-upload-to-pypi.yml +++ b/.github/workflows/build-and-upload-to-pypi.yml @@ -54,7 +54,15 @@ jobs: # "Unknown enable group". cp313t is not buildable at this pin at any # setting, so 3.13t is covered by test.yml instead. Build selection # follows requires-python (>=3.10) in pyproject.toml. - uses: pypa/cibuildwheel@v4.1.0 + # + # 4.2.0 is the first pin that knows about CPython 3.15, and it builds + # and tests cp315/cp315t by default -- no `CIBW_ENABLE: + # cpython-prerelease` needed. cibuildwheel gates a Python behind that + # enable group only until its first release candidate, because the ABI + # is frozen at rc1; 3.15.0rc1 is out, so wheels built here are + # forward-compatible with 3.15.0 final. (Under the previous 4.1.0 pin + # 3.15 could not be built at all, at any setting.) + uses: pypa/cibuildwheel@v4.2.0 env: CIBW_ARCHS: ${{ matrix.cibw_archs }} diff --git a/.github/workflows/test-system-uchardet.yml b/.github/workflows/test-system-uchardet.yml index b34ee56..650da47 100644 --- a/.github/workflows/test-system-uchardet.yml +++ b/.github/workflows/test-system-uchardet.yml @@ -19,6 +19,8 @@ jobs: system-uchardet: name: Build against system uchardet (from git), Python ${{ matrix.python-version }} runs-on: ubuntu-latest + # Only the 3.15 row below sets this; the rest stay hard requirements. + continue-on-error: ${{ matrix.experimental }} strategy: fail-fast: false matrix: @@ -30,6 +32,14 @@ jobs: # cibuildwheel cannot produce cp313t wheels at all, which makes 3.13t # the least-covered configuration in the project overall. python-version: ['3.12', '3.13t', '3.14t'] + experimental: [false] + include: + # Free-threaded 3.15 (a pre-release; see test.yml for why these rows + # are advisory and how to promote them). The system build is where a + # new interpreter is most likely to break first: it compiles against + # upstream uchardet's headers rather than the vendored sources. + - python-version: '3.15t' + experimental: true steps: - uses: actions/checkout@v4 @@ -43,6 +53,8 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + # Required for the 3.15 row; see test.yml for the full reasoning. + allow-prereleases: true - name: Build and install uchardet from git run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4146113..c644355 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,6 +12,9 @@ jobs: test: name: Test on Python ${{ matrix.python-version }} runs-on: ubuntu-latest + # Only the 3.15 rows below set this; every other row is `false` and stays + # a hard requirement. + continue-on-error: ${{ matrix.experimental }} strategy: fail-fast: false matrix: @@ -19,9 +22,27 @@ jobs: # The `t` entries are the free-threaded (no-GIL) builds. They are the # only coverage for the freethreading_compatible declaration -- without # them a regression is invisible until someone imports cchardet on a - # free-threaded interpreter. Note cibuildwheel 4.1.0 cannot build + # free-threaded interpreter. Note cibuildwheel 4.2.0 still cannot build # cp313t wheels at all, so 3.13t is tested here or nowhere. python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.13t', '3.14t'] + experimental: [false] + include: + # Python 3.15, resolved by `allow-prereleases` below to whatever + # pre-release the runner image currently publishes -- 3.15.0rc1 at + # the time of writing, and 3.15.0 final once it ships, with no change + # needed here. Both the default and the free-threaded build, matching + # the coverage the 3.14 rows get. + # + # Advisory (`continue-on-error`) while 3.15 is a pre-release: the + # extension builds and the suite passes on 3.15.0rc1 today, but rc2 + # and the final release are still to come and a CPython-side + # regression must not block merges. Move these entries up into the + # `python-version` list to make 3.15 a required check once 3.15.0 + # final is out. + - python-version: '3.15' + experimental: true + - python-version: '3.15t' + experimental: true steps: - uses: actions/checkout@v4 @@ -35,6 +56,12 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + # Needed for the 3.15 rows -- a bare `3.15` only matches stable + # releases and fails with "Version 3.15 was not found in the local + # cache". Harmless for the rest: this widens `3.X` to `~3.X.0-0`, and + # a pre-release is only chosen when no stable release satisfies the + # spec, so 3.10-3.14 still resolve to their newest stable patch. + allow-prereleases: true - name: Install package and test dependencies run: | diff --git a/pyproject.toml b/pyproject.toml index 3f750f9..b0bc29a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", # Tier 2: multithreaded use is tested and the thread-safety caveats are # documented (detect() is safe to call concurrently; a UniversalDetector # instance is per-stream and must not be shared). See the README. diff --git a/tox.ini b/tox.ini index 3950b52..11ea938 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,8 @@ [tox] -envlist = py36, py37, py38, py39, py310, py311 +# Matches requires-python (>=3.10) in pyproject.toml and the matrix in +# .github/workflows/test.yml. py315 is Python 3.15, still a pre-release -- +# tox will skip it unless a 3.15 interpreter is on PATH. +envlist = py310, py311, py312, py313, py314, py315 [testenv] whitelist_externals = make