Skip to content

Commit a2e3c8e

Browse files
Document @Final requirement for Tool/ToolSpec methods in CONTRIBUTING.md
Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> Agent-Logs-Url: https://github.com/usethis-python/usethis-python/sessions/a307b5d3-0c33-4642-b872-8712d1c47ffc
1 parent c9519d6 commit a2e3c8e

1 file changed

Lines changed: 17 additions & 0 deletions

File tree

CONTRIBUTING.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,24 @@ Tool implementations are defined in classes in the `usethis._tool.impl` module.
140140
- Declare this new submodule in the `.importlinter` configuration to architecturally describe its dependency relationships with other tools' submodules. For example, does your tool integrate with pre-commit? It should be in a higher layer module than the `pre-commit` submodule.
141141
- Define a `usethis._tool.base.ToolSpec` subclass, e.g. for a tool named Xyz, define a class `XyzToolSpec(ToolSpec)`.
142142
- Start by implementing its `name` property method, then work through the other methods. Most method have default implementations, but even in those cases you will need to consider them individually and determine an appropriate implementation. For example, methods which specify the tool's dependencies default to empty dependencies, but you shouldn't rely on this.
143+
- Mark all methods in your `ToolSpec` subclass with the `@typing.final` decorator. This prevents the methods from being accidentally overridden in the `Tool` subclass, ensuring a clean separation between static tool specification and runtime behavior. For properties, use `@final` above `@property`. For example:
144+
145+
```python
146+
from typing import final
147+
148+
class XyzToolSpec(ToolSpec):
149+
@final
150+
@property
151+
def meta(self) -> ToolMeta:
152+
...
153+
154+
@final
155+
def dev_deps(self, *, unconditional: bool = False) -> list[Dependency]:
156+
...
157+
```
158+
143159
- Then, define a subclass of the `ToolSpec` subclass you just created, which also subclasses `usethis._tool.base.Tool`, e.g. for a tool named Xyz, define a class `XyzTool(XyzToolSpec, Tool)`. The only method this usually requires a non-default implementation for is `config_spec` to specify which configuration sections should be set up for the tool (and which sections the tool manages). However, you may find it helpful to provide custom implementations for other methods as well, e.g. `print_how_to_use`.
160+
- Mark all methods in your `Tool` subclass with `@final` as well, to prevent further subclassing from overriding them.
144161
- Include a comment with a URL linking to the tool's source repo for reference.
145162

146163
#### Register your `Tool` subclass

0 commit comments

Comments
 (0)