diff --git a/.github/dependabot.yml b/.github/dependabot.yml index be006de..2b2eb8c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,8 @@ updates: groups: github-actions: patterns: - - "*" # Group all Actions updates into a single larger pull request + - "*" # Group all Actions updates into a single larger pull request schedule: interval: weekly + cooldown: + default-days: 7 diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index dd34d66..a91c4d8 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -16,25 +16,30 @@ defaults: run: shell: bash +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + with: + persist-credentials: false - uses: pre-commit/action@v3.0.1 mypy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + with: + persist-credentials: false - uses: actions/setup-python@v6 - name: Install package run: pip install -e ".[test]" - name: Mypy - uses: liskin/gh-problem-matcher-wrap@v3 - with: - linters: mypy - run: mypy --show-column-numbers + run: mypy --show-column-numbers test: runs-on: ubuntu-latest @@ -43,6 +48,8 @@ jobs: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15-dev"] steps: - uses: actions/checkout@v6 + with: + persist-credentials: false - uses: actions/setup-python@v6 name: Setup Python ${{ matrix.python-version }} with: @@ -52,7 +59,7 @@ jobs: - name: Run tests run: pytest --cov-report=xml - name: Upload coverage - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -60,11 +67,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + with: + persist-credentials: false - name: Install build run: pip install build - name: Build package run: python -m build - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v7 with: name: python-dist path: dist/* @@ -80,9 +89,9 @@ jobs: name: pypi url: https://pypi.org/project/tcod-ecs/${{ github.ref_name }}/ permissions: - id-token: write + id-token: write # Attestation steps: - - uses: actions/download-artifact@v6 + - uses: actions/download-artifact@v8 with: name: python-dist path: dist/ @@ -93,14 +102,17 @@ jobs: name: Create Release runs-on: ubuntu-latest permissions: - contents: write + contents: write # Publish GitHub Releases steps: - name: Checkout code uses: actions/checkout@v6 + with: + persist-credentials: false - name: Generate body run: scripts/get_release_description.py | tee release_body.md - name: Create Release id: create_release - uses: ncipollo/release-action@v1 - with: - bodyFile: release_body.md + # https://cli.github.com/manual/gh_release_create + run: gh release create "${GITHUB_REF_NAME}" --verify-tag --notes-file release_body.md + env: + GH_TOKEN: ${{ github.token }} diff --git a/.github/zizmor.yaml b/.github/zizmor.yaml new file mode 100644 index 0000000..d9e822c --- /dev/null +++ b/.github/zizmor.yaml @@ -0,0 +1,9 @@ +rules: + anonymous-definition: + disable: true + cache-poisoning: + disable: true + excessive-permissions: + disable: true + unpinned-uses: + disable: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index da481ac..524851c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,8 +17,12 @@ repos: - id: fix-byte-order-marker - id: detect-private-key - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.6 + rev: v0.15.14 hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix] - id: ruff-format + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: v1.25.2 + hooks: + - id: zizmor diff --git a/.vscode/settings.json b/.vscode/settings.json index 445fcc1..c0fdc09 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,7 @@ "buildapi", "cattrs", "codecov", + "cooldown", "docstrings", "doctest", "doctests", @@ -60,7 +61,8 @@ "unstructure", "WASD", "WAXD", - "WINDOWLEAVE" + "WINDOWLEAVE", + "zizmor" ], "editor.codeActionsOnSave": { "source.fixAll": "always" diff --git a/CHANGELOG.md b/CHANGELOG.md index 315869e..4f14484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [5.5.0] - 2026-01-14 + +### Changed + +- Removed dependency on `sentinel-value`. + +## [5.4.2] - 2025-11-28 + ### Removed - Dropped support for Python 3.8 & 3.9. diff --git a/docs/api.rst b/docs/api.rst index 5f6450b..d1e60e9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5,7 +5,7 @@ API reference :members: :undoc-members: :show-inheritance: - :exclude-members: Registry, World, Entity + :exclude-members: Registry, World, Entity, IsA .. automodule:: tcod.ecs.registry :members: diff --git a/pyproject.toml b/pyproject.toml index 0b84935..84cea06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,8 +21,7 @@ requires-python = ">=3.10" dependencies = [ "attrs >=23.1.0", "cattrs >=23.1.2", - "sentinel-value >=1.0.0", - "typing-extensions >=4.13.1", + "typing-extensions >=4.14.0", ] [tool.setuptools_scm] diff --git a/tcod/ecs/constants.py b/tcod/ecs/constants.py index 6c7980d..a7a122a 100644 --- a/tcod/ecs/constants.py +++ b/tcod/ecs/constants.py @@ -2,9 +2,20 @@ from __future__ import annotations -from typing import Final +from typing_extensions import Never -from sentinel_value import sentinel -IsA: Final = sentinel("IsA") -"""The default is-a relationship tag used for entity inheritance.""" +class _IgnoreSetState(type): + def __setstate__(cls, _state: object) -> None: + """Ignore setstate on outdated sentinel-value pickle data.""" + + +class IsA(metaclass=_IgnoreSetState): + """The default is-a relationship tag used for entity inheritance.""" + + def __new__(cls: type[IsA], *_: object) -> Never: # noqa: D102 + # Return own type instead of instance, for outdated sentinel-value pickle data. + return cls # type: ignore[misc] + + +_sentinel_IsA = IsA # Compatibility with sentinel-value, deprecated since 5.4 # noqa: N816 diff --git a/tcod/ecs/entity.py b/tcod/ecs/entity.py index 6013bee..3f5d1dc 100644 --- a/tcod/ecs/entity.py +++ b/tcod/ecs/entity.py @@ -14,8 +14,7 @@ from weakref import WeakKeyDictionary, WeakValueDictionary import attrs -from sentinel_value import sentinel -from typing_extensions import Self, TypeForm, deprecated +from typing_extensions import Self, Sentinel, TypeForm, deprecated import tcod.ecs.callbacks import tcod.ecs.query @@ -34,7 +33,7 @@ _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") -_raise: Final = sentinel("_raise") +_raise: Final = Sentinel("_raise") _entity_table: WeakKeyDictionary[Registry, WeakValueDictionary[object, Entity]] = WeakKeyDictionary() """A weak table of registries and unique identifiers to entity objects. diff --git a/tests/test_ecs.py b/tests/test_ecs.py index a245f24..95c334c 100644 --- a/tests/test_ecs.py +++ b/tests/test_ecs.py @@ -301,3 +301,10 @@ def test_type_form() -> None: world[None].components[TupleKey] = (1, 2) x, y = world[None].components[TupleKey] assert (x, y) == (1, 2) + + +PICKLED_ISA = b"\x80\x03ctcod.ecs.constants\n_sentinel_IsA\nq\x00X\x12\x00\x00\x00tcod.ecs.constantsq\x01X\x03\x00\x00\x00IsAq\x02\x86q\x03\x81q\x04}q\x05(X\r\x00\x00\x00instance_nameq\x06h\x02X\x0b\x00\x00\x00module_nameq\x07h\x01X\x0e\x00\x00\x00qualified_nameq\x08X\x16\x00\x00\x00tcod.ecs.constants.IsAq\tub." # cspell: disable-line + + +def test_unpickle_is_a() -> None: + assert pickle.loads(PICKLED_ISA) is tcod.ecs.IsA # noqa: S301