Run Pylint 4 from lint target - #87
Conversation
|
This PR integrates a PyPy-backed focused Pylint 4 pass into the repository lint gate and CI, supplies a PyPy/Astroid compatibility shim and wrapper, tightens and documents the Pylint allow-list, modernises typing to collections.abc across the codebase, and applies broad reorganisation, refactors and test consolidation. The unified lint target (make lint) now runs architecture checks, Ruff, then the PyPy-backed Pylint pass; CI also runs a CPython-only Pylint syntax check to surface Python 3.14-only syntax. Summary of the most important changes
Review notes and recommended follow-ups
Design/doc links
Execplan note
WalkthroughSummarise: split canonical persistence into protocol and focused storage modules; add strict OpenAI payload validators/adapters; add Pedante evaluator and parsing; centralise ORM models/mappers; convert Protocol stubs to raise NotImplementedError; replace many typing generics with collections.abc; wire PyPy-run Pylint into Makefile/CI; add many shared test fixtures and new tests. ChangesCanonical persistence: protocols & compatibility exports
ORM scaffolding and model decomposition
Mappers modularisation and re-exports
Repository implementations & UoW wiring
OpenAI payload validation, adapters and client façade
Pedante evaluator, parsing and types
Orchestration DTOs and result split
Typing hygiene: switch typing generics → collections.abc
Protocol stub semantics & protocol-stub tests
Tooling, linting and CI
Shared test fixtures, helpers and new tests
Small behaviour-preserving cleanups
Sequence Diagram(s)sequenceDiagram
rect rgba(0,128,255,0.5)
participant Client
end
rect rgba(0,200,100,0.5)
participant App
end
rect rgba(255,128,0,0.5)
participant LLM
end
rect rgba(200,0,200,0.5)
participant DB
end
Client->>App: POST /series-profiles/{id}/pedante (PedanteEvaluationRequest)
App->>App: PedanteEvaluator.build_prompt(request)
App->>LLM: LLMPort.generate(LLMRequest with prompt)
LLM-->>App: LLMResponse (text + usage + metadata)
App->>App: PedanteEvaluationResult.from_json(response.text, usage)
App->>DB: Optional reads/writes via SqlAlchemyUnitOfWork
DB-->>App: Query results / commit
App-->>Client: 200 OK with typed PedanteEvaluationResult
Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +27 to +80 def _object_build_without_pypy_descriptor_aliases(
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
obj: types.ModuleType | type,
) -> None:
"""Build Astroid nodes while ignoring non-string PyPy ``dir()`` entries."""
if obj in self._done:
return
self._done[obj] = node
for alias in dir(obj):
if type(alias) is not str:
continue
pypy__class_getitem__ = IS_PYPY and alias == "__class_getitem__"
try:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
member = getattr(obj, alias)
except _IGNORED_GETATTR_ERRORS:
attach_dummy_node(node, alias)
continue
if inspect.ismethod(member) and not pypy__class_getitem__:
member = member.__func__
if inspect.isfunction(member):
child = _build_from_function(node, member, self._module)
elif inspect.isbuiltin(member) or pypy__class_getitem__:
if self.imported_member(node, member, alias):
continue
child = object_build_methoddescriptor(node, member)
elif inspect.isclass(member):
if self.imported_member(node, member, alias):
continue
if member in self._done:
child = self._done[member]
assert isinstance(child, nodes.ClassDef)
else:
child = object_build_class(node, member)
self.object_build(child, member)
elif inspect.ismethoddescriptor(member):
child = object_build_methoddescriptor(node, member)
elif inspect.isdatadescriptor(member):
child = object_build_datadescriptor(node, member)
elif isinstance(member, tuple(node_classes.CONST_CLS)):
if alias in node.special_attributes:
continue
child = nodes.const_factory(member)
elif inspect.isroutine(member):
child = _build_from_function(node, member, self._module)
elif _safe_has_attribute(member, "__all__"):
child = build_module(alias)
self.object_build(child, member)
else:
child = build_dummy(member)
if child not in node.locals.get(alias, ()):
node.add_local_node(child, alias)❌ New issue: Complex Method |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +27 to +80 def _object_build_without_pypy_descriptor_aliases(
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
obj: types.ModuleType | type,
) -> None:
"""Build Astroid nodes while ignoring non-string PyPy ``dir()`` entries."""
if obj in self._done:
return
self._done[obj] = node
for alias in dir(obj):
if type(alias) is not str:
continue
pypy__class_getitem__ = IS_PYPY and alias == "__class_getitem__"
try:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
member = getattr(obj, alias)
except _IGNORED_GETATTR_ERRORS:
attach_dummy_node(node, alias)
continue
if inspect.ismethod(member) and not pypy__class_getitem__:
member = member.__func__
if inspect.isfunction(member):
child = _build_from_function(node, member, self._module)
elif inspect.isbuiltin(member) or pypy__class_getitem__:
if self.imported_member(node, member, alias):
continue
child = object_build_methoddescriptor(node, member)
elif inspect.isclass(member):
if self.imported_member(node, member, alias):
continue
if member in self._done:
child = self._done[member]
assert isinstance(child, nodes.ClassDef)
else:
child = object_build_class(node, member)
self.object_build(child, member)
elif inspect.ismethoddescriptor(member):
child = object_build_methoddescriptor(node, member)
elif inspect.isdatadescriptor(member):
child = object_build_datadescriptor(node, member)
elif isinstance(member, tuple(node_classes.CONST_CLS)):
if alias in node.special_attributes:
continue
child = nodes.const_factory(member)
elif inspect.isroutine(member):
child = _build_from_function(node, member, self._module)
elif _safe_has_attribute(member, "__all__"):
child = build_module(alias)
self.object_build(child, member)
else:
child = build_dummy(member)
if child not in node.locals.get(alias, ()):
node.add_local_node(child, alias)❌ New issue: Bumpy Road Ahead |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +83 to +109 def _build_child_node( # noqa: PLR0913
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
member: object,
alias: str,
*,
pypy__class_getitem__: bool,
) -> nodes.NodeNG | object:
"""Dispatch member conversion to the matching Astroid child builder."""
if inspect.isbuiltin(member) or pypy__class_getitem__:
child = _build_builtin_child(self, node, member, alias)
elif inspect.isclass(member):
child = _build_class_child(self, node, member, alias)
elif inspect.ismethoddescriptor(member):
child = object_build_methoddescriptor(node, member)
elif inspect.isdatadescriptor(member):
child = object_build_datadescriptor(node, member)
elif isinstance(member, tuple(node_classes.CONST_CLS)):
child = _build_const_child(node, member, alias)
elif inspect.isfunction(member) or inspect.isroutine(member):
child = _build_from_function(node, member, self._module)
elif _safe_has_attribute(member, "__all__"):
child = build_module(alias)
self.object_build(child, member)
else:
child = build_dummy(member)
return child❌ New issue: Complex Method |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +112 to +141 def _object_build_without_pypy_descriptor_aliases(
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
obj: types.ModuleType | type,
) -> None:
"""Build Astroid nodes while ignoring non-string PyPy ``dir()`` entries."""
if obj in self._done:
return
self._done[obj] = node
for alias in dir(obj):
if type(alias) is not str:
continue
pypy__class_getitem__ = IS_PYPY and alias == "__class_getitem__"
member = _get_member(obj, alias)
if member is _GET_MEMBER_FAILED:
attach_dummy_node(node, alias)
continue
if inspect.ismethod(member) and not pypy__class_getitem__:
member = member.__func__
child = _build_child_node(
self,
node,
member,
alias,
pypy__class_getitem__=pypy__class_getitem__,
)
if child is _SKIP:
continue
if child not in node.locals.get(alias, ()):
node.add_local_node(child, alias)❌ New issue: Complex Method |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph. If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced. Comment on lines +87 to +110 def _dispatch_member_to_child(
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
member: object,
alias: str,
) -> nodes.NodeNG | object:
"""Dispatch non-PyPy-special members to the matching Astroid builder."""
if inspect.isbuiltin(member):
return _build_builtin_child(self, node, member, alias)
if inspect.isclass(member):
return _build_class_child(self, node, member, alias)
if inspect.ismethoddescriptor(member):
return object_build_methoddescriptor(node, member)
if inspect.isdatadescriptor(member):
return object_build_datadescriptor(node, member)
if isinstance(member, tuple(node_classes.CONST_CLS)):
return _build_const_child(node, member, alias)
if inspect.isroutine(member):
return _build_from_function(node, member, self._module)
if _safe_has_attribute(member, "__all__"):
child = build_module(alias)
self.object_build(child, member)
return child
return build_dummy(member)❌ New issue: Code Duplication |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +139 to +162 def _object_build_without_pypy_descriptor_aliases(
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
obj: types.ModuleType | type,
) -> None:
"""Build Astroid nodes while ignoring non-string PyPy ``dir()`` entries."""
if obj in self._done:
return
self._done[obj] = node
for alias in dir(obj):
if type(alias) is not str:
continue
member, pypy__class_getitem__ = _get_member(obj, alias)
if member is _GET_MEMBER_FAILED:
attach_dummy_node(node, alias)
continue
if pypy__class_getitem__:
child = _build_builtin_child(self, node, member, alias)
else:
child = _build_child_node(self, node, member, alias)
if child is _SKIP:
continue
if child not in node.locals.get(alias, ()):
node.add_local_node(child, alias)❌ New issue: Complex Method |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +97 to +124 def _dispatch_member_to_child( # noqa: C901, PLR0913
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
member: object,
alias: str,
*,
pypy__class_getitem__: bool = False,
) -> nodes.NodeNG | object:
"""Dispatch members to the matching Astroid builder."""
if pypy__class_getitem__:
return _build_builtin_child(self, node, member, alias)
if inspect.isbuiltin(member):
return _build_builtin_child(self, node, member, alias)
if inspect.isclass(member):
return _build_class_child(self, node, member, alias)
if inspect.ismethoddescriptor(member):
return object_build_methoddescriptor(node, member)
if inspect.isdatadescriptor(member):
return object_build_datadescriptor(node, member)
if isinstance(member, tuple(node_classes.CONST_CLS)):
return _build_const_child(node, member, alias)
if inspect.isroutine(member):
return _build_from_function(node, member, self._module)
if _safe_has_attribute(member, "__all__"):
child = build_module(alias)
self.object_build(child, member)
return child
return build_dummy(member)❌ New issue: Complex Method |
This comment was marked as resolved.
This comment was marked as resolved.
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +146 to +184 def _build_child_for_member( # noqa: PLR0913, PLR0917
builder: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
member: object,
alias: str,
pypy__class_getitem__: bool, # noqa: FBT001
) -> nodes.NodeNG | None:
"""Build an Astroid child for *member* or return None when it should skip."""
if inspect.isfunction(member):
return _build_from_function(node, member, builder._module)
if inspect.isbuiltin(member) or pypy__class_getitem__:
if builder.imported_member(node, member, alias):
return None
return object_build_methoddescriptor(node, member)
if inspect.isclass(member):
if builder.imported_member(node, member, alias):
return None
if member in builder._done:
child = builder._done[member]
assert isinstance(child, nodes.ClassDef)
return child
child = object_build_class(node, member)
builder.object_build(child, member)
return child
if inspect.ismethoddescriptor(member):
return object_build_methoddescriptor(node, member)
if inspect.isdatadescriptor(member):
return object_build_datadescriptor(node, member)
if isinstance(member, tuple(node_classes.CONST_CLS)):
if alias in node.special_attributes:
return None
return nodes.const_factory(member)
if inspect.isroutine(member):
return _build_from_function(node, member, builder._module)
if _safe_has_attribute(member, "__all__"):
child = build_module(alias)
builder.object_build(child, member)
return child
return build_dummy(member)❌ New issue: Excess Number of Function Arguments |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on file # ruff: noqa: C901, PLR0911, S101, TC003❌ New issue: Overall Code Complexity |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +146 to +184 def _build_child_for_member( # noqa: PLR0913, PLR0917
builder: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
member: object,
alias: str,
pypy__class_getitem__: bool, # noqa: FBT001
) -> nodes.NodeNG | None:
"""Build an Astroid child for *member* or return None when it should skip."""
if inspect.isfunction(member):
return _build_from_function(node, member, builder._module)
if inspect.isbuiltin(member) or pypy__class_getitem__:
if builder.imported_member(node, member, alias):
return None
return object_build_methoddescriptor(node, member)
if inspect.isclass(member):
if builder.imported_member(node, member, alias):
return None
if member in builder._done:
child = builder._done[member]
assert isinstance(child, nodes.ClassDef)
return child
child = object_build_class(node, member)
builder.object_build(child, member)
return child
if inspect.ismethoddescriptor(member):
return object_build_methoddescriptor(node, member)
if inspect.isdatadescriptor(member):
return object_build_datadescriptor(node, member)
if isinstance(member, tuple(node_classes.CONST_CLS)):
if alias in node.special_attributes:
return None
return nodes.const_factory(member)
if inspect.isroutine(member):
return _build_from_function(node, member, builder._module)
if _safe_has_attribute(member, "__all__"):
child = build_module(alias)
builder.object_build(child, member)
return child
return build_dummy(member)❌ New issue: Bumpy Road Ahead |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +97 to +124 def _dispatch_member_to_child( # noqa: PLR0913
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
member: object,
alias: str,
*,
pypy__class_getitem__: bool = False,
) -> nodes.NodeNG | object:
"""Dispatch members to the matching Astroid builder."""
if pypy__class_getitem__:
return _build_builtin_child(self, node, member, alias)
if inspect.isbuiltin(member):
return _build_builtin_child(self, node, member, alias)
if inspect.isclass(member):
return _build_class_child(self, node, member, alias)
if inspect.ismethoddescriptor(member):
return object_build_methoddescriptor(node, member)
if inspect.isdatadescriptor(member):
return object_build_datadescriptor(node, member)
if isinstance(member, tuple(node_classes.CONST_CLS)):
return _build_const_child(node, member, alias)
if inspect.isroutine(member):
return _build_from_function(node, member, self._module)
if _safe_has_attribute(member, "__all__"):
child = build_module(alias)
self.object_build(child, member)
return child
return build_dummy(member)❌ New issue: Complex Method |
1 similar comment
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Comment on lines +97 to +124 def _dispatch_member_to_child( # noqa: PLR0913
self: raw_building.InspectBuilder,
node: nodes.Module | nodes.ClassDef,
member: object,
alias: str,
*,
pypy__class_getitem__: bool = False,
) -> nodes.NodeNG | object:
"""Dispatch members to the matching Astroid builder."""
if pypy__class_getitem__:
return _build_builtin_child(self, node, member, alias)
if inspect.isbuiltin(member):
return _build_builtin_child(self, node, member, alias)
if inspect.isclass(member):
return _build_class_child(self, node, member, alias)
if inspect.ismethoddescriptor(member):
return object_build_methoddescriptor(node, member)
if inspect.isdatadescriptor(member):
return object_build_datadescriptor(node, member)
if isinstance(member, tuple(node_classes.CONST_CLS)):
return _build_const_child(node, member, alias)
if inspect.isroutine(member):
return _build_from_function(node, member, self._module)
if _safe_has_attribute(member, "__all__"):
child = build_module(alias)
self.object_build(child, member)
return child
return build_dummy(member)❌ New issue: Complex Method |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix: Large Methodtests/test_pedante_evaluator_integration.py: test_pedante_evaluator_returns_typed_findings_and_usage What lead to degradation?test_pedante_evaluator_returns_typed_findings_and_usage has 86 lines, threshold = 70 Why does this problem occur?Overly long functions make the code harder to read. The recommended maximum function length for the Python language is 70 lines of code. Severity: Brain Method - Complex Method - Long Method. How to fix it?We recommend to be careful here -- just splitting long functions don't necessarily make the code easier to read. Instead, look for natural chunks inside the functions that expresses a specific task or concern. Often, such concerns are indicated by a Code Comment followed by an if-statement. Use the EXTRACT FUNCTION refactoring to encapsulate that concern. |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix: Large Methodtests/test_orchestration_langgraph_properties.py: test_langgraph_total_tokens_non_negative What lead to degradation?test_langgraph_total_tokens_non_negative has 75 lines, threshold = 70 Why does this problem occur?Overly long functions make the code harder to read. The recommended maximum function length for the Python language is 70 lines of code. Severity: Brain Method - Complex Method - Long Method. How to fix it?We recommend to be careful here -- just splitting long functions don't necessarily make the code easier to read. Instead, look for natural chunks inside the functions that expresses a specific task or concern. Often, such concerns are indicated by a Code Comment followed by an if-statement. Use the EXTRACT FUNCTION refactoring to encapsulate that concern. |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Move evaluator setup and request/result assertions out of the integration scenario so the test body stays focused on the payload and invocation. Keep existing typed finding and provider metadata checks intact.
Move planner/action construction, graph execution, and usage rollup checks out of the Hypothesis property body. Keep the property focused on the orchestration flow while preserving the token and result assertions.
Expand OpenAI chat and Pedante DTO documentation where review requested public contract details. Add runtime validation for Pedante enum fields, result findings, and usage metadata so malformed DTOs fail at construction. Refactor show-notes generation tests to share payload fixtures and keep assertion failures diagnostic.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
episodic/qa/pedante/types.py (1)
102-106:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReject
Noneforusageand enforce the declared contract.Tighten this guard. Line 104 currently admits
None, even thoughPedanteEvaluationResult.usageis typed asLLMUsage, which weakens contract safety and defers failures.Patch
def _require_usage(value: object) -> None: """Reject usage metadata that is not normalized LLM usage.""" - if value is not None and not isinstance(value, LLMUsage): + if not isinstance(value, LLMUsage): msg = "usage must be an LLMUsage value." raise TypeError(msg)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@episodic/qa/pedante/types.py` around lines 102 - 106, The guard in _require_usage currently allows None which contradicts the declared PedanteEvaluationResult.usage: LLMUsage; update _require_usage so it rejects None as well as non-LLMUsage types — i.e., treat value is None as an error and raise a TypeError with a clear message; locate the function _require_usage and change its validation to require isinstance(value, LLMUsage) (no None allowed) so callers and the PedanteEvaluationResult.usage contract are enforced.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@episodic/qa/pedante/types.py`:
- Around line 102-106: The guard in _require_usage currently allows None which
contradicts the declared PedanteEvaluationResult.usage: LLMUsage; update
_require_usage so it rejects None as well as non-LLMUsage types — i.e., treat
value is None as an error and raise a TypeError with a clear message; locate the
function _require_usage and change its validation to require isinstance(value,
LLMUsage) (no None allowed) so callers and the PedanteEvaluationResult.usage
contract are enforced.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 7e1e08d4-7a4c-4100-8615-6085d28b03b3
📒 Files selected for processing (4)
episodic/llm/openai_chat.pyepisodic/qa/pedante/types.pytests/test_pedante_validation.pytests/test_show_notes_generation.py
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix: Large Methodtests/test_orchestration_langgraph_properties.py: test_langgraph_total_tokens_non_negative What lead to degradation?test_langgraph_total_tokens_non_negative has 75 lines, threshold = 70 Why does this problem occur?Overly long functions make the code harder to read. The recommended maximum function length for the Python language is 70 lines of code. Severity: Brain Method - Complex Method - Long Method. How to fix it?We recommend to be careful here -- just splitting long functions don't necessarily make the code easier to read. Instead, look for natural chunks inside the functions that expresses a specific task or concern. Often, such concerns are indicated by a Code Comment followed by an if-statement. Use the EXTRACT FUNCTION refactoring to encapsulate that concern. |
Summary
This branch adds a focused Pylint 4 pass to
make lintand keeps that passusable on the managed PyPy runtime. It routes Pylint through
tools/pylint_pypy.py, stores the allow-listed message selection and rule rationale inpyproject.toml, and updatesMakefileso lint, Ruff, architecture checks, and Pylint run as one gate.The branch also flattens the PyPy Astroid object-builder compatibility patch,
removes dead helper code left by the refactor, restores strict test assertions
that should not be truthiness checks, and makes CI call
make lintdirectly.Review walkthrough
pyproject.tomlandMakefileto review the Pylint 4 rule allow-list, rationale comments, and lint-target wiring.tools/pylint_pypy.pyfor the PyPy-safe Astroid patch, parse-incompatible file handling, and flattened member dispatch.tests/test_pylint_pypy.pyfor focused wrapper coverage that avoids adding Astroid to the project test environment.tests/test_reference_document_api_support.pyandtests/test_interpreter_executor.pyfor restored strict equality assertions.docs/developers-guide.mdand.github/workflows/ci.ymlfor the developer-facing lint workflow and CI gate change.Validation
mbake validate Makefile: passedmake check-fmt: passedmake markdownlint: passedmake nixie: passedmake lint: passed, Pylint reported10.00/10make typecheck: passedmake test: passed,461 passed, 3 skippedNotes
disable = ["all"]and an explicitenablelist for the requested Pylint 4 messages that are available in the installed 4.x release.syntax-errorremains disabled for this Pylint pass because the available managed PyPy is currently Python 3.11 while the project targets Python 3.14 syntax. The wrapper reports skipped parse-incompatible files instead of hiding other Pylint findings.Summary by Sourcery
Add a PyPy-backed Pylint 4 lint gate and align codebase with the new focused rule set.
New Features:
Enhancements:
CI:
Documentation: