-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: corrections while adding docs (#78)
Signed-off-by: Henry Schreiner <[email protected]>
- Loading branch information
Showing
6 changed files
with
201 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,110 @@ | ||
# Checks | ||
|
||
Plugins provide checks; repo-review requires at least one plugin providing checks to operate; there are no built-in checks. | ||
|
||
## Writing a check | ||
|
||
A check is an object following a specific Protocol: | ||
|
||
```python | ||
class Check: | ||
""" | ||
Short description. | ||
""" | ||
|
||
family: str | ||
requires: Set[str] = frozenset() # Optional | ||
url: str = "" # Optional | ||
|
||
def check(self) -> bool | str: | ||
""" | ||
Error message if returns False. | ||
""" | ||
... | ||
``` | ||
|
||
You need to implement `family`, which is a string indicating which family it is | ||
grouped under, and `check()`, which can take [](fixtures), and returns `True` if | ||
the check passes, or `False` if the check fails. If you want a dynamic error | ||
explanation instead of the `check()` docstring, you can return a non-empty | ||
string from the check instead of `False`. Docstrings/error messages can access | ||
their own object with `{self}` and name with `{name}`. The error message is in | ||
markdown format. | ||
|
||
If the check named in `requires` does not pass, the check is skipped. | ||
|
||
A suggested convention for easily writing checks is as follows: | ||
|
||
```python | ||
class General: | ||
family = "general" | ||
|
||
|
||
class PY001(General): | ||
"Has a pyproject.toml" | ||
|
||
@staticmethod | ||
def check(package: Traversable) -> bool: | ||
""" | ||
All projects should have a `pyproject.toml` file to support a modern | ||
build system and support wheel installs properly. | ||
""" | ||
return package.joinpath("pyproject.toml").is_file() | ||
|
||
|
||
class PyProject: | ||
family = "pyproject" | ||
|
||
|
||
class PP002(PyProject): | ||
"Has a proper build-system table" | ||
|
||
requires = {"PY001"} | ||
url = "https://peps.python.org/pep-0517" | ||
|
||
@staticmethod | ||
def check(pyproject: dict[str, Any]) -> bool: | ||
""" | ||
Must have `build-system.requires` *and* `build-system.backend`. Both | ||
should be present in all modern packages. | ||
""" | ||
|
||
match pyproject: | ||
case {"build-system": {"requires": list(), "build-backend": str()}}: | ||
return True | ||
case _: | ||
return False | ||
``` | ||
|
||
Key features: | ||
|
||
- The base class allows setting the family once, and gives a quick shortcut for accessing all the checks via `.__subclasses__`. | ||
- The name of the check class itself is the check code. | ||
- The check method is a classmethod since it has no state. | ||
- Likewise, all attributes are set on the class (`family`, `requires`, `url`) since there is no state. | ||
- `requries` is used so that the pyproject checks are skipped if the pyproject file is missing. | ||
|
||
## Registering checks | ||
|
||
You register checks with a function that returns a dict of checks, with the code | ||
of the check (letters + number) as the key, and check instances as the values. | ||
This function can take [](fixtures), as well, allowing customization of checks | ||
based on repo properties. | ||
|
||
Here is the suggested function for the above example: | ||
|
||
```python | ||
def repo_review_checks() -> dict[str, General | PyProject]: | ||
return {p.__name__: p() for p in General.__subclasses__()} | { | ||
p.__name__: p() for p in PyProject.__subclasses__() | ||
} | ||
``` | ||
|
||
You tell repo review to use this function via an entry-point: | ||
|
||
```toml | ||
[project.entry-points."repo_review.checks"] | ||
general_pyproject = "my_plugin_package.my_checks_module:repo_review_checks" | ||
``` | ||
|
||
The entry-point name doesn't matter. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,36 @@ | ||
# Families | ||
|
||
Families are a set of simple strings that group together similar checks. You can provide a nicer user experience, however, by adding a mapping of information for repo-review to improve the ordering and display of families. | ||
|
||
You can construct a dict with the following optional keys: | ||
|
||
```python | ||
class Family(typing.TypedDict, total=False): | ||
name: str # defaults to key | ||
order: int # defaults to 0 | ||
``` | ||
|
||
Then you can provide a function that maps family strings to this extra information: | ||
|
||
```python | ||
def get_familes() -> dict[str, Family]: | ||
return { | ||
"general": Family( | ||
name="General", | ||
order=-3, | ||
), | ||
"pyproject": Family( | ||
name="PyProject", | ||
order=-2, | ||
), | ||
} | ||
``` | ||
|
||
And finally, you register this function as an entry-point: | ||
|
||
```toml | ||
[project.entry-points."repo_review.families"] | ||
families = "my_plugin_package.my_family_module:get_families" | ||
``` | ||
|
||
The entry-point name doesn't matter. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,50 @@ | ||
# Fixtures | ||
|
||
Like pytest fixtures, fixtures in repo-review are requested by name. There are three built-in fixtures: | ||
|
||
- `root: Traversable` - The repository path. All checks or fixtures that depend on the root of the repository should use this. | ||
- `package: Traversable` - The path to the package directory. This is the same as `root` unless `--package-dir` is passed. | ||
- `pyproject: dict[str, Any]` - The `pyproject.toml` in the package if it exists, an empty dict otherwise. | ||
|
||
Repo-review doesn't necessarily assume any form or language for your repository, | ||
but since it already looks for configuration in `pyproject.toml`, this fixture | ||
is provided. | ||
|
||
## Writing a fixture | ||
|
||
You can provide new fixtures easily. A fixture can take any other fixture(s) as | ||
arguments; repo-review topologically sorts fixtures before computing them. The | ||
result of a fixture is cached and provided when requested. The return from a | ||
fixture should be treated as immutable. | ||
|
||
A fixture function looks like this: | ||
|
||
```python | ||
import yaml | ||
from importlib.resources.abc import Traversable | ||
|
||
|
||
def workflows(root: Traversable) -> dict[str, Any]: | ||
workflows_base_path = package.joinpath(".github/workflows") | ||
workflows_dict: dict[str, Any] = {} | ||
if workflows_base_path.is_dir(): | ||
for workflow_path in workflows_base_path.iterdir(): | ||
if workflow_path.name.endswith(".yml"): | ||
with workflow_path.open("rb") as f: | ||
workflows_dict[Path(workflow_path.name).stem] = yaml.safe_load(f) | ||
|
||
return workflows_dict | ||
``` | ||
|
||
Don't assume a specific `Traversable`, like `Path`; for remote repos or in | ||
WebAssembly, this will be a custom `Traversable`. | ||
|
||
To register the fixture with repo-review, you need to declare it as an entry-point: | ||
|
||
```toml | ||
[project.entry-points."repo_review.fixtures"] | ||
workflows = "my_plugin_package.my_fixture_module:workflows" | ||
``` | ||
|
||
The name of the entry-point is the fixture name. It is recommended that you name | ||
the fixture function with the same name for simplicity, but it is not required. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters