Skip to content

Modernise tooling, packaging and static type checking - #24

Open
Risto97 wants to merge 15 commits into
HEP-SoC:masterfrom
Risto97:pyproject
Open

Modernise tooling, packaging and static type checking#24
Risto97 wants to merge 15 commits into
HEP-SoC:masterfrom
Risto97:pyproject

Conversation

@Risto97

@Risto97 Risto97 commented Apr 16, 2026

Copy link
Copy Markdown
Collaborator

Add modern Python environment.
Use ruff, ty for static code checking, automatic formatting, trailing whitespace...

@benoitdenkinger

Copy link
Copy Markdown
Contributor

I was actually looking into pre-commit hooks to add and found this PR. Can this be merged?

@Risto97

Risto97 commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

I used cookiecutter template to scaffold the project. Which is what I do for every Python project today.

It sets up the pre commit hooks with static type checking, linting, formatting, ...

Unfortunately I didnt finish this pull request. But if you want to pick it up, that would be great

@benoitdenkinger

Copy link
Copy Markdown
Contributor

I see. Do you have a list of what you would like to put in this PR?

@Risto97

Risto97 commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator Author

I wanted to add pyproject.toml, and static code checkers, and then also have lint, type clean project. But this opened up a lot of issues in the code, so it will require some time to do it. If you want you can merge this even with type errors, and we can fix later.

Benoit Denkinger added 11 commits June 26, 2026 11:10
- Replaced pylint + pip-based lint job with pre-commit (ruff lint/format)
- Added test job with pytest across Python 3.10–3.13 matrix and Codecov upload
- Replaced  with  (produces sdist + wheel)
- Gated build job on check + test passing
- Added pull_request trigger so checks run on PRs, not only on master/tags
- Dropped deprecated from PyPI publish step
Pin uv to 0.11.24 for reproducible builds and add .dockerignore to
prevent .venv, docs, dist and git metadata from being copied into
the image.
@benoitdenkinger benoitdenkinger changed the title Use pyproject.toml Modernise tooling, packaging and static type checking Jun 30, 2026
@benoitdenkinger

Copy link
Copy Markdown
Contributor

Summary

This branch migrates the project from setup.py to pyproject.toml, updates the CI and dev-container configuration to use uv, and wires ty into the test suite as a mandatory static type check. It also resolves all errors ty reports on the existing code.


Infrastructure & packaging

  • Replace setup.py / setup.cfg with pyproject.toml (PEP 517/518)
  • Add uv as the package manager and pin version in Docker
  • Update CI workflow to use uv and call Makefile targets directly
  • Add ty to the make test target so type errors are caught in CI
  • Add a devcontainer for VS Code / Codespaces
  • Add .dockerignore

Type checking fixes (halnode.py)

Wrong annotation for children_type / descendants_type

# before — annotated as a Node *instance*
def halchildren(self, children_type: 'Node' = Node, ...) -> Iterator['Node']:
# after — annotated as a Node *class*
def halchildren(self, children_type: type[Node] = Node, ...) -> Iterator[HalBaseNode]:

_halfactory return type too broad

Changed from Optional['Node'] to HalBaseNode | None. The factory only ever returns one of the five concrete HAL subclasses or None (for SignalNode). The broader type forced every caller to cast down before accessing any HAL-specific attribute.

Missing None guard after _halfactory

halchildren called _halfactory and then immediately used the result without checking for None. This was both a type error and a latent runtime AttributeError: if a SignalNode child appeared, the code would try to call .orig_type_name on None at the type-dict update block further down. The added if halchild is None: continue fixes both.

Return types of traversal methods

halunrolled, halchildren, and haldescendants all returned Iterator['Node']. Callers need HAL-specific attributes (is_bus, haldescendants, address_offset, orig_type_name) on the yielded values. Narrowing the return types to Iterator[HalBaseNode] makes those attributes visible to the type checker without requiring casts at every call site.

address_offset not declared on HalBaseNode

All five concrete subclasses define address_offset, but the base class did not, so the type checker could not see it on HalBaseNode. A base implementation (raise NotImplementedError) is added toHalBaseNode. halchildren, haldescendants, and both array-register address_offset overrides.

halunrolled wrong constructor call

# before — passes self as first arg (expects a Component)
N = cls(self)
N.current_idx = idxs  # type: ignore
# after — matches the Node.__init__ signature
N = cls(self.inst, self.env, self.parent)

bus_offset not declared on HalBaseNode

halchildren assigns halchild.bus_offset = bus_offset on every non-bus child, but bus_offset was only set in subclass __init__ methods. Adding bus_offset: int = 0 as a class attribute on HalBaseNode makes the assignment valid for all subclasses and removes the ClassVar vs instance-variable ambiguity.


Remaining cast calls

Explicit cast calls are required for type check to pass.

Location Reason
cast(str, super().type_name) systemrdl types type_name as str | None. The surrounding logic already guarantees it is set, but the type signature does not encode that
cast(list[int], self.array_dimensions) array_dimensions is list[int] | None in systemrdl. The is_array guard makes None impossible here, but there is no type-level invariant linking the two
cast(AddressableNode, N).current_idx cls = type(self) after an isinstance(self, AddressableNode) check — the narrowing does not flow through type(self)(...), so the constructed value is typed as HalBaseNode rather than HalBaseNode & AddressableNode

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants