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
4 changes: 2 additions & 2 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0

- uses: hynek/build-and-inspect-python-package@v2
persist-credentials: false
- uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516 # v2.14.0

test-publish:
needs: [dist]
Expand Down
31 changes: 14 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,22 @@ env:
FORCE_COLOR: 3

jobs:
pre-commit:
format:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Install uv
uses: astral-sh/setup-uv@v6
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format job uses astral-sh/setup-uv@v6 (unpinned) while the tests job uses astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 (pinned with commit hash). These should be consistent - either both should use pinned versions with commit hashes for security and reproducibility, or both should use version tags. The recommended practice is to pin both with commit hashes.

Suggested change
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2

Copilot uses AI. Check for mistakes.
- name: Install the project
run: uv sync --locked --group dev

- uses: pre-commit/action@v3.0.1
with:
extra_args: --hook-stage manual --all-files
- name: Run PyLint
run: |
echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json"
uv run nox -s pylint
- name: Install
run: uv sync --no-default-groups --group nox --group lint --locked
- name: Lint
run: uv run --frozen nox -s lint

tests:
name: Check Python ${{ matrix.python-version }} on ${{ matrix.runs-on }}
runs-on: ${{ matrix.runs-on }}
needs: [pre-commit]
needs: [format]
strategy:
fail-fast: false
matrix:
Expand All @@ -50,14 +44,17 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Install uv and set the python version
uses: astral-sh/setup-uv@v6
- name: Install uv
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
with:
python-version: ${{ matrix.python-version }}

- name: Install
run: uv sync --no-default-groups --group nox --group test --locked

- name: Test package
run: >-
uv run pytest --cov --cov-report=xml --cov-report=term --durations=20
run: |
uv run --frozen nox -s test -- --cov --cov-report=xml --cov-report=term --durations=20

- name: Upload coverage report
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
Expand All @@ -67,7 +64,7 @@ jobs:
status:
name: CI Pass
runs-on: ubuntu-latest
needs: [pre-commit, tests]
needs: [format, tests]
if: always()
steps:
- name: All required jobs passed
Expand Down
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ repos:
rev: "v2.4.1"
hooks:
- id: codespell
args: [--skip=uv.lock]
args: ["--toml", "pyproject.toml"]
additional_dependencies: ["tomli"]

- repo: https://github.com/shellcheck-py/shellcheck-py
rev: "v0.10.0.1"
Expand Down
92 changes: 29 additions & 63 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -1,104 +1,70 @@
"""Nox sessions."""
"""Nox setup."""

import shutil
from pathlib import Path

import nox

DIR = Path(__file__).parent.resolve()
from nox_uv import session

nox.needs_version = ">=2024.3.2"
nox.options.sessions = [
# Linting
"lint",
"pylint",
"precommit",
# Testing
"tests",
# Packaging
"build",
]
nox.options.default_venv_backend = "uv"

DIR = Path(__file__).parent.resolve()

# =============================================================================
# Linting


@nox.session(venv_backend="uv")
def lint(session: nox.Session, /) -> None:
@session(uv_groups=["lint"], reuse_venv=True)
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lint session uses s.notify() to call other sessions, but it doesn't actually need the uv_groups=["lint"] parameter since it only delegates to other sessions and doesn't run any commands itself. The uv_groups parameter installs dependencies, which is unnecessary overhead for a session that only notifies other sessions.

Suggested change
@session(uv_groups=["lint"], reuse_venv=True)
@session(reuse_venv=True)

Copilot uses AI. Check for mistakes.
def lint(s: nox.Session, /) -> None:
"""Run the linter."""
precommit(session) # reuse pre-commit session
pylint(session) # reuse pylint session
s.notify("precommit")
s.notify("pylint")


@nox.session(venv_backend="uv")
def precommit(session: nox.Session, /) -> None:
@session(uv_groups=["lint"], reuse_venv=True)
def precommit(s: nox.Session, /) -> None:
"""Run pre-commit."""
session.run_install(
"uv",
"sync",
"--group=lint",
f"--python={session.virtualenv.location}",
env={"UV_PROJECT_ENVIRONMENT": session.virtualenv.location},
)
session.run("pre-commit", "run", "--all-files", *session.posargs)


@nox.session(venv_backend="uv")
def pylint(session: nox.Session, /) -> None:
s.run("pre-commit", "run", "--all-files", *s.posargs)


@session(uv_groups=["lint"], reuse_venv=True)
def pylint(s: nox.Session, /) -> None:
"""Run PyLint."""
session.run_install(
"uv",
"sync",
"--group=lint",
f"--python={session.virtualenv.location}",
env={"UV_PROJECT_ENVIRONMENT": session.virtualenv.location},
)
session.run("pylint", "xmmutablemap", *session.posargs)
s.run("pylint", "xmmutablemap", *s.posargs)


# =============================================================================
# Testing


@nox.session(venv_backend="uv")
def tests(session: nox.Session, /) -> None:
@session(uv_groups=["test"], reuse_venv=True)
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test session uses s.notify() to delegate to the pytest session but doesn't need the uv_groups=["test"] parameter since it only notifies and doesn't run any commands itself. This causes unnecessary dependency installation overhead.

Suggested change
@session(uv_groups=["test"], reuse_venv=True)
@session(reuse_venv=True)

Copilot uses AI. Check for mistakes.
def test(s: nox.Session, /) -> None:
"""Run the unit and regular tests."""
session.run_install(
"uv",
"sync",
"--group=test",
f"--python={session.virtualenv.location}",
env={"UV_PROJECT_ENVIRONMENT": session.virtualenv.location},
)
session.run("pytest", *session.posargs)
s.notify("pytest", posargs=s.posargs)


@session(uv_groups=["test"], reuse_venv=True)
def pytest(s: nox.Session, /) -> None:
"""Run the unit and regular tests."""
s.run("pytest", *s.posargs)


# =============================================================================
# Packaging


@nox.session(venv_backend="uv")
@session(uv_groups=["build"])
def rm_build(_: nox.Session, /) -> None:
"""Remove the build directory."""
build_path = DIR.joinpath("build")
if build_path.exists():
shutil.rmtree(build_path)


@nox.session(venv_backend="uv")
def build(session: nox.Session, /) -> None:
@session(uv_groups=["build"])
def build(s: nox.Session, /) -> None:
"""Build an SDist and wheel."""
build_path = DIR.joinpath("build")
if build_path.exists():
shutil.rmtree(build_path)
rm_build(s)
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build session calls rm_build(s) directly as a Python function, but rm_build is decorated with @session which makes it a nox session. This mixing of session invocation patterns is inconsistent. Either use s.notify("rm_build") to invoke it as a session, or remove the @session decorator from rm_build and make it a plain helper function.

Suggested change
rm_build(s)
s.notify("rm_build")

Copilot uses AI. Check for mistakes.

session.run_install(
"uv",
"sync",
"--group=build",
f"--python={session.virtualenv.location}",
env={"UV_PROJECT_ENVIRONMENT": session.virtualenv.location},
)
session.run("build")
s.run("python", "-m", "build")
52 changes: 31 additions & 21 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,22 @@ build-backend = "hatchling.build"
dev = [
"ipykernel>=6.29.5",
"cz-conventional-gitmoji>=0.6.1",
{include-group = "build"},
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The build dependency group is included in the dev group (line 49), and the build session in noxfile.py uses uv_groups=["build"]. However, the build package is typically only needed for building distributions, not for general development. Consider whether build should be in the dev group or only installed when explicitly needed via the build session.

Suggested change
{include-group = "build"},

Copilot uses AI. Check for mistakes.
{include-group = "lint"},
{include-group = "nox"},
{include-group = "test"},
]
build = [
"build>=1.3.0",
]
lint = [
"mypy>=1.19.0",
"pre-commit>=4.1.0",
"pylint>=3.3.8",
]
nox = [
"nox>=2024.10.9",
"nox-uv>=0.6.3",
]
test = [
"pytest>=8.4.2",
Expand All @@ -70,29 +76,12 @@ build.hooks.vcs.version-file = "src/xmmutablemap/_version.py"
version.source = "vcs"


[tool.commitizen]
name = "cz_gitmoji"
[tool.codespell]
skip = ["uv.lock"]


[tool.pytest.ini_options]
minversion = "8"
addopts = [
"--showlocals",
"--strict-config",
"--strict-markers",
"-p no:doctest", # using sybil
"-ra",
]
xfail_strict = true
filterwarnings = [
"error",
]
log_cli_level = "INFO"
testpaths = ["README.md", "src/", "tests/"]
norecursedirs = [
".*", # ignores .hypothesis, .git, etc.
"__pycache__"
]
[tool.commitizen]
name = "cz_gitmoji"


[tool.coverage]
Expand Down Expand Up @@ -124,6 +113,27 @@ module = "jax.*"
ignore_missing_imports = true


[tool.pytest.ini_options]
minversion = "8"
addopts = [
"--showlocals",
"--strict-config",
"--strict-markers",
"-p no:doctest", # using sybil
"-ra",
]
xfail_strict = true
filterwarnings = [
"error",
]
log_cli_level = "INFO"
testpaths = ["README.md", "src/", "tests/"]
norecursedirs = [
".*", # ignores .hypothesis, .git, etc.
"__pycache__"
]


[tool.ruff]
src = ["src"]

Expand Down
Loading