repo-review 0.11.2__tar.gz → 0.12.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. {repo_review-0.11.2 → repo_review-0.12.0}/.github/CONTRIBUTING.md +5 -11
  2. {repo_review-0.11.2 → repo_review-0.12.0}/.github/workflows/cd.yml +3 -0
  3. {repo_review-0.11.2 → repo_review-0.12.0}/.github/workflows/ci.yml +9 -1
  4. {repo_review-0.11.2 → repo_review-0.12.0}/.pre-commit-config.yaml +10 -9
  5. {repo_review-0.11.2 → repo_review-0.12.0}/.readthedocs.yaml +1 -3
  6. {repo_review-0.11.2 → repo_review-0.12.0}/PKG-INFO +9 -28
  7. {repo_review-0.11.2 → repo_review-0.12.0}/README.md +7 -3
  8. {repo_review-0.11.2 → repo_review-0.12.0}/docs/checks.md +1 -1
  9. {repo_review-0.11.2 → repo_review-0.12.0}/docs/cli.md +1 -1
  10. {repo_review-0.11.2 → repo_review-0.12.0}/docs/families.md +2 -2
  11. {repo_review-0.11.2 → repo_review-0.12.0}/docs/index.html +5 -5
  12. {repo_review-0.11.2 → repo_review-0.12.0}/docs/intro.md +19 -3
  13. {repo_review-0.11.2 → repo_review-0.12.0}/pyproject.toml +56 -24
  14. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/__main__.py +33 -3
  15. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/_version.py +2 -2
  16. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/checks.py +22 -8
  17. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/fixtures.py +4 -5
  18. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/ghpath.py +1 -1
  19. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/html.py +6 -1
  20. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/processor.py +19 -4
  21. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/testing.py +1 -1
  22. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_self.py +13 -5
  23. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_utilities/pyproject.py +43 -2
  24. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_utilities/pyproject.toml +8 -0
  25. {repo_review-0.11.2 → repo_review-0.12.0}/.devcontainer/devcontainer.json +0 -0
  26. {repo_review-0.11.2 → repo_review-0.12.0}/.git_archival.txt +0 -0
  27. {repo_review-0.11.2 → repo_review-0.12.0}/.gitattributes +0 -0
  28. {repo_review-0.11.2 → repo_review-0.12.0}/.github/ISSUE_TEMPLATE/new-issue.md +0 -0
  29. {repo_review-0.11.2 → repo_review-0.12.0}/.github/dependabot.yml +0 -0
  30. {repo_review-0.11.2 → repo_review-0.12.0}/.github/release.yml +0 -0
  31. {repo_review-0.11.2 → repo_review-0.12.0}/.gitignore +0 -0
  32. {repo_review-0.11.2 → repo_review-0.12.0}/.pre-commit-hooks.yaml +0 -0
  33. {repo_review-0.11.2 → repo_review-0.12.0}/LICENSE +0 -0
  34. {repo_review-0.11.2 → repo_review-0.12.0}/action.yml +0 -0
  35. {repo_review-0.11.2 → repo_review-0.12.0}/docs/.nojekyll +0 -0
  36. {repo_review-0.11.2 → repo_review-0.12.0}/docs/api/repo_review.resources.rst +0 -0
  37. {repo_review-0.11.2 → repo_review-0.12.0}/docs/api/repo_review.rst +0 -0
  38. {repo_review-0.11.2 → repo_review-0.12.0}/docs/changelog.md +0 -0
  39. {repo_review-0.11.2 → repo_review-0.12.0}/docs/conf.py +0 -0
  40. {repo_review-0.11.2 → repo_review-0.12.0}/docs/fixtures.md +0 -0
  41. {repo_review-0.11.2 → repo_review-0.12.0}/docs/index.md +0 -0
  42. {repo_review-0.11.2 → repo_review-0.12.0}/docs/plugins.md +0 -0
  43. {repo_review-0.11.2 → repo_review-0.12.0}/docs/programmatic.md +0 -0
  44. {repo_review-0.11.2 → repo_review-0.12.0}/docs/webapp.js +0 -0
  45. {repo_review-0.11.2 → repo_review-0.12.0}/docs/webapp.md +0 -0
  46. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/__init__.py +0 -0
  47. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/_compat/__init__.py +0 -0
  48. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/_compat/importlib/__init__.py +0 -0
  49. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/_compat/importlib/resources/__init__.py +0 -0
  50. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/_compat/importlib/resources/abc.py +0 -0
  51. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/_compat/tomllib.py +0 -0
  52. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/_compat/typing.py +0 -0
  53. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/_version.pyi +0 -0
  54. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/families.py +0 -0
  55. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/py.typed +0 -0
  56. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/resources/__init__.py +0 -0
  57. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/resources/repo-review.schema.json +0 -0
  58. {repo_review-0.11.2 → repo_review-0.12.0}/src/repo_review/schema.py +0 -0
  59. {repo_review-0.11.2 → repo_review-0.12.0}/tests/conftest.py +0 -0
  60. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_checks.py +0 -0
  61. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_cmd.py +0 -0
  62. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_depends.py +0 -0
  63. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_families.py +0 -0
  64. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_fixtures.py +0 -0
  65. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_multi.py +0 -0
  66. {repo_review-0.11.2 → repo_review-0.12.0}/tests/test_package.py +0 -0
@@ -32,23 +32,17 @@ $ hatch run example:repo-review <args> # Run an example
32
32
  Hatch handles everything for you, including setting up an temporary virtual
33
33
  environment.
34
34
 
35
+ Using `uv run` directly is also supported.
36
+
35
37
  ## Setting up a development environment manually
36
38
 
37
- You can set up a development environment by running:
39
+ You can set up a development environment in `.venv` by running:
38
40
 
39
41
  ```bash
40
- python3 -m venv .venv
41
- source ./.venv/bin/activate
42
- pip install -v -e .[dev]
42
+ uv sync
43
43
  ```
44
44
 
45
- If you have the [Python Launcher for Unix](https://github.com/brettcannon/python-launcher),
46
- you can instead do:
47
-
48
- ```bash
49
- py -m venv .venv
50
- py -m install -v -e .[dev]
51
- ```
45
+ Or just prefix every command by `uv run`.
52
46
 
53
47
  # Post setup
54
48
 
@@ -6,6 +6,8 @@ on:
6
6
  types:
7
7
  - published
8
8
 
9
+ permissions: {}
10
+
9
11
  jobs:
10
12
  dist:
11
13
  name: Distribution build
@@ -15,6 +17,7 @@ jobs:
15
17
  - uses: actions/checkout@v4
16
18
  with:
17
19
  fetch-depth: 0
20
+ persist-credentials: false
18
21
 
19
22
  - uses: hynek/build-and-inspect-python-package@v2
20
23
 
@@ -17,12 +17,16 @@ concurrency:
17
17
  env:
18
18
  FORCE_COLOR: 3
19
19
 
20
+ permissions: {}
21
+
20
22
  jobs:
21
23
  pre-commit:
22
24
  name: Format
23
25
  runs-on: ubuntu-latest
24
26
  steps:
25
27
  - uses: actions/checkout@v4
28
+ with:
29
+ persist-credentials: false
26
30
  - uses: actions/setup-python@v5
27
31
  with:
28
32
  python-version: "3.x"
@@ -42,6 +46,7 @@ jobs:
42
46
  - uses: actions/checkout@v4
43
47
  with:
44
48
  fetch-depth: 0
49
+ persist-credentials: false
45
50
 
46
51
  - uses: actions/setup-python@v5
47
52
  with:
@@ -69,6 +74,7 @@ jobs:
69
74
  - uses: actions/checkout@v4
70
75
  with:
71
76
  fetch-depth: 0
77
+ persist-credentials: false
72
78
 
73
79
  - uses: hynek/build-and-inspect-python-package@v2
74
80
 
@@ -79,6 +85,7 @@ jobs:
79
85
  - uses: actions/checkout@v4
80
86
  with:
81
87
  fetch-depth: 0
88
+ persist-credentials: false
82
89
 
83
90
  - name: Setup uv
84
91
  uses: yezz123/setup-uv@v4
@@ -108,8 +115,9 @@ jobs:
108
115
  - uses: actions/checkout@v4
109
116
  with:
110
117
  fetch-depth: 0
118
+ persist-credentials: false
111
119
 
112
120
  - name: Run repo-review action
113
121
  uses: ./
114
122
  with:
115
- plugins: sp-repo-review==2024.08.19
123
+ plugins: sp-repo-review==2025.01.22
@@ -5,26 +5,26 @@ ci:
5
5
 
6
6
  repos:
7
7
  - repo: https://github.com/adamchainz/blacken-docs
8
- rev: 1.18.0
8
+ rev: 1.19.1
9
9
  hooks:
10
10
  - id: blacken-docs
11
- additional_dependencies: [black==23.*]
11
+ additional_dependencies: [black==24.*]
12
12
 
13
13
  - repo: https://github.com/astral-sh/ruff-pre-commit
14
- rev: "v0.6.1"
14
+ rev: "v0.9.4"
15
15
  hooks:
16
16
  - id: ruff
17
17
  args: ["--fix", "--show-fixes"]
18
18
  - id: ruff-format
19
19
 
20
20
  - repo: https://github.com/rbubley/mirrors-prettier
21
- rev: "v3.3.3"
21
+ rev: "v3.4.2"
22
22
  hooks:
23
23
  - id: prettier
24
24
  types_or: [yaml, markdown, html, css, scss, javascript, json]
25
25
 
26
26
  - repo: https://github.com/pre-commit/pre-commit-hooks
27
- rev: v4.6.0
27
+ rev: v5.0.0
28
28
  hooks:
29
29
  - id: check-added-large-files
30
30
  - id: check-case-conflict
@@ -45,7 +45,7 @@ repos:
45
45
  - id: rst-inline-touching-normal
46
46
 
47
47
  - repo: https://github.com/pre-commit/mirrors-mypy
48
- rev: v1.11.1
48
+ rev: v1.14.1
49
49
  hooks:
50
50
  - id: mypy
51
51
  files: (src|web|tests)
@@ -53,13 +53,14 @@ repos:
53
53
  additional_dependencies:
54
54
  - click>=8.1.5
55
55
  - markdown-it-py
56
+ - orjson
56
57
  - pytest
57
58
  - rich
58
59
  - tomli
59
60
  - types-PyYAML
60
61
 
61
62
  - repo: https://github.com/codespell-project/codespell
62
- rev: v2.3.0
63
+ rev: v2.4.1
63
64
  hooks:
64
65
  - id: codespell
65
66
  args: ["-Lhist,absense", "-w"]
@@ -78,12 +79,12 @@ repos:
78
79
  exclude: .pre-commit-config.yaml
79
80
 
80
81
  - repo: https://github.com/henryiii/validate-pyproject-schema-store
81
- rev: 2024.08.19
82
+ rev: 2025.02.03
82
83
  hooks:
83
84
  - id: validate-pyproject
84
85
 
85
86
  - repo: https://github.com/python-jsonschema/check-jsonschema
86
- rev: 0.29.1
87
+ rev: 0.31.1
87
88
  hooks:
88
89
  - id: check-dependabot
89
90
  - id: check-github-workflows
@@ -13,6 +13,4 @@ build:
13
13
  - asdf plugin add uv
14
14
  - asdf install uv latest
15
15
  - asdf global uv latest
16
- - uv venv
17
- - uv pip install .[docs]
18
- - . .venv/bin/activate && sphinx-build -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html
16
+ - uv run --group docs sphinx-build -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: repo_review
3
- Version: 0.11.2
3
+ Version: 0.12.0
4
4
  Summary: Framework that can run checks on repos
5
5
  Project-URL: Changelog, https://github.com/scientific-python/repo-review/releases
6
6
  Project-URL: Demo, https://scientific-python.github.io/repo-review
@@ -34,29 +34,6 @@ Provides-Extra: cli
34
34
  Requires-Dist: click>=8; extra == 'cli'
35
35
  Requires-Dist: rich-click; extra == 'cli'
36
36
  Requires-Dist: rich>=12.2; extra == 'cli'
37
- Provides-Extra: dev
38
- Requires-Dist: click>=8; extra == 'dev'
39
- Requires-Dist: pytest>=7; extra == 'dev'
40
- Requires-Dist: rich-click; extra == 'dev'
41
- Requires-Dist: rich>=12.2; extra == 'dev'
42
- Requires-Dist: sp-repo-review>=2024.08.19; extra == 'dev'
43
- Requires-Dist: validate-pyproject>=0.14; extra == 'dev'
44
- Provides-Extra: docs
45
- Requires-Dist: click>=8; extra == 'docs'
46
- Requires-Dist: furo; extra == 'docs'
47
- Requires-Dist: myst-parser>=0.13; extra == 'docs'
48
- Requires-Dist: rich-click; extra == 'docs'
49
- Requires-Dist: rich>=12.2; extra == 'docs'
50
- Requires-Dist: sphinx-autodoc-typehints; extra == 'docs'
51
- Requires-Dist: sphinx-copybutton; extra == 'docs'
52
- Requires-Dist: sphinx-github-changelog; extra == 'docs'
53
- Requires-Dist: sphinx>=4.0; extra == 'docs'
54
- Requires-Dist: sphinxcontrib-programoutput; extra == 'docs'
55
- Requires-Dist: sphinxext-opengraph; extra == 'docs'
56
- Provides-Extra: test
57
- Requires-Dist: pytest>=7; extra == 'test'
58
- Requires-Dist: sp-repo-review>=2024.08.19; extra == 'test'
59
- Requires-Dist: validate-pyproject>=0.14; extra == 'test'
60
37
  Description-Content-Type: text/markdown
61
38
 
62
39
  # repo-review
@@ -125,8 +102,12 @@ select = ["A", "B", "C100"]
125
102
  ignore = ["A100"]
126
103
  ```
127
104
 
105
+ The ignore list can also be a table, with reasons for values.
106
+
128
107
  If `--select` or `--ignore` are given on the command line, they will override
129
- the `pyproject.toml` config.
108
+ the `pyproject.toml` config. You can use `--extend-select` and `--extend-ignore`
109
+ on the command line to extend the `pyproject.toml` config. These CLI options
110
+ are comma separated.
130
111
 
131
112
  ## Comparison to other frameworks
132
113
 
@@ -159,7 +140,7 @@ webapp. It also would allow `zipfile.Path` to work just as well, too - no need
159
140
  to extract.
160
141
 
161
142
  [Checks][] can request [fixtures][] (like [pytest][]) as arguments. Check files
162
- can add new fixtures as needed. Fixtures are are specified with entry points,
143
+ can add new fixtures as needed. Fixtures are specified with entry points,
163
144
  and take any other fixture as arguments as well - the `root` and `package`
164
145
  fixtures represents the root of the repository and of the package you are
165
146
  checking, respectively, and are the basis for the other fixtures, which are
@@ -195,7 +176,7 @@ collection functions, the family entry-point also supports fixtures.
195
176
  This project inspired [Try-PyHF](https://kratsg.github.io/try-pyhf/), an
196
177
  interface for a High Energy Physics package in Scikit-HEP.
197
178
 
198
- This project inspired [abSENSE](https://princetonuniversity.github.io/abSENSE/), an
179
+ This project inspired [abSENSE](https://princetonuniversity.github.io/abSENSE/), a
199
180
  web interface to abSENSE.
200
181
 
201
182
  This was developed for [Scikit-HEP][] before moving to Scientific-Python.
@@ -64,8 +64,12 @@ select = ["A", "B", "C100"]
64
64
  ignore = ["A100"]
65
65
  ```
66
66
 
67
+ The ignore list can also be a table, with reasons for values.
68
+
67
69
  If `--select` or `--ignore` are given on the command line, they will override
68
- the `pyproject.toml` config.
70
+ the `pyproject.toml` config. You can use `--extend-select` and `--extend-ignore`
71
+ on the command line to extend the `pyproject.toml` config. These CLI options
72
+ are comma separated.
69
73
 
70
74
  ## Comparison to other frameworks
71
75
 
@@ -98,7 +102,7 @@ webapp. It also would allow `zipfile.Path` to work just as well, too - no need
98
102
  to extract.
99
103
 
100
104
  [Checks][] can request [fixtures][] (like [pytest][]) as arguments. Check files
101
- can add new fixtures as needed. Fixtures are are specified with entry points,
105
+ can add new fixtures as needed. Fixtures are specified with entry points,
102
106
  and take any other fixture as arguments as well - the `root` and `package`
103
107
  fixtures represents the root of the repository and of the package you are
104
108
  checking, respectively, and are the basis for the other fixtures, which are
@@ -134,7 +138,7 @@ collection functions, the family entry-point also supports fixtures.
134
138
  This project inspired [Try-PyHF](https://kratsg.github.io/try-pyhf/), an
135
139
  interface for a High Energy Physics package in Scikit-HEP.
136
140
 
137
- This project inspired [abSENSE](https://princetonuniversity.github.io/abSENSE/), an
141
+ This project inspired [abSENSE](https://princetonuniversity.github.io/abSENSE/), a
138
142
  web interface to abSENSE.
139
143
 
140
144
  This was developed for [Scikit-HEP][] before moving to Scientific-Python.
@@ -30,7 +30,7 @@ explanation instead of the `check()` docstring, you can return a non-empty
30
30
  string from the check instead of `False`. Returning `None` makes a check
31
31
  "skipped". Docstrings/error messages can access their own object with `{self}`
32
32
  and check name with `{name}` (these are processed with `.format()`, so escape `{}`
33
- as `{{}}`). The error message is in markdown format.
33
+ as `{{}}`). The error message is in Markdown format.
34
34
 
35
35
  ```{versionchanged} 0.9
36
36
  The string return value is not processed via `.format`. You can use `self` and
@@ -17,7 +17,7 @@ pass `--package-dir <path>`.
17
17
 
18
18
  There are four output formats; `rich` produces great terminal output, `svg`
19
19
  produces an SVG based on the rich output, `html` produces a custom HTML report,
20
- and `json` produces a output that can be processed easily. To make it easier to
20
+ and `json` produces an output that can be processed easily. To make it easier to
21
21
  support tools like GitHub Actions, there is also a `--stderr FORMAT` output
22
22
  option that produces the selected format on stderr as well, and disables
23
23
  producing terminal escape codes on stdout, even if `FORCE_COLOR` is set. This
@@ -15,7 +15,7 @@ class Family(typing.TypedDict, total=False):
15
15
 
16
16
  The `name` will be shown instead if given. The families will be sorted by
17
17
  `order` then key. And a `description` will be shown after the name if provided;
18
- it is expected to be in markdown format.
18
+ it is expected to be in Markdown format.
19
19
 
20
20
  ```{versionadded} 0.9
21
21
  Descriptions are now supported.
@@ -24,7 +24,7 @@ Descriptions are now supported.
24
24
  Then you can provide a function that maps family strings to this extra information:
25
25
 
26
26
  ```python
27
- def get_familes() -> dict[str, Family]:
27
+ def get_families() -> dict[str, Family]:
28
28
  return {
29
29
  "general": Family(
30
30
  name="General",
@@ -6,7 +6,7 @@
6
6
  content="initial-scale=1, width=device-width"
7
7
  />
8
8
  <script
9
- src="https://cdn.jsdelivr.net/pyodide/v0.26.2/full/pyodide.js"
9
+ src="https://cdn.jsdelivr.net/pyodide/v0.27.1/full/pyodide.js"
10
10
  crossorigin
11
11
  ></script>
12
12
  <!-- Production -->
@@ -64,10 +64,10 @@
64
64
  <App
65
65
  header={true}
66
66
  deps={[
67
- "repo-review~=0.11.0",
68
- "sp-repo-review==2024.08.19",
69
- "validate-pyproject-schema-store== 2024.08.19",
70
- "validate-pyproject[all]~=0.19.0",
67
+ "repo-review~=0.11.3",
68
+ "sp-repo-review==2025.01.22",
69
+ "validate-pyproject-schema-store==2025.01.20",
70
+ "validate-pyproject[all]~=0.22.0",
71
71
  ]}
72
72
  />,
73
73
  );
@@ -15,7 +15,7 @@ to use it is:
15
15
  pipx run <plugin-name>[cli] .
16
16
  ```
17
17
 
18
- This uses [pipx][] (pip for executables) to download the plugin and all of it's
18
+ This uses [pipx][] (pip for executables) to download the plugin and all of its
19
19
  dependencies (including repo-review itself) into a temporary virtual
20
20
  environment (cached for a week), then runs it. For example:
21
21
 
@@ -51,10 +51,26 @@ You can explicitly list checks to select or skip in your `pyproject.toml`:
51
51
 
52
52
  ```toml
53
53
  [tool.repo-review]
54
- select = ["..."]
55
- ignore = ["..."]
54
+ select = ["A", "B", "C100"]
55
+ ignore = ["A100"]
56
56
  ```
57
57
 
58
+ You can list the letter prefix or the exact check name. The ignore list can also
59
+ be a table, with reasons for values. These will be shown explicitly in the report if
60
+ a reason is given.
61
+
62
+ ```toml
63
+ [tool.repo-review.ignore]
64
+ A = "Skipping this whole family"
65
+ B101 = "Skipping this specific check"
66
+ C101 = "" # Hidden from report, like a normal ignore
67
+ ```
68
+
69
+ If `--select` or `--ignore` are given on the command line, they will override
70
+ the `pyproject.toml` config. You can use `--extend-select` and `--extend-ignore`
71
+ on the command line to extend the `pyproject.toml` config. These CLI options
72
+ are comma separated.
73
+
58
74
  ## Pre-commit
59
75
 
60
76
  You can also use this from pre-commit:
@@ -37,19 +37,44 @@ dependencies = [
37
37
  "typing-extensions; python_version < '3.11'",
38
38
  ]
39
39
 
40
+ [project.urls]
41
+ Changelog = "https://github.com/scientific-python/repo-review/releases"
42
+ Demo = "https://scientific-python.github.io/repo-review"
43
+ Documentation = "https://repo-review.readthedocs.io"
44
+ Homepage = "https://repo-review.readthedocs.io"
45
+ Source = "https://github.com/scientific-python/repo-review"
46
+
47
+ [project.scripts]
48
+ repo-review = "repo_review.__main__:main"
49
+
50
+ [project.entry-points."repo_review.fixtures"]
51
+ pyproject = "repo_review.fixtures:pyproject"
52
+ list_all = "repo_review.fixtures:list_all"
53
+
54
+ [project.entry-points."validate_pyproject.tool_schema"]
55
+ repo-review = "repo_review.schema:get_schema"
56
+
40
57
  [project.optional-dependencies]
41
58
  cli = [
42
59
  "click >=8",
43
60
  "rich >=12.2",
44
61
  "rich-click",
45
62
  ]
63
+
64
+ [dependency-groups]
46
65
  test = [
47
66
  "pytest >=7",
48
- "sp-repo-review >=2024.08.19",
67
+ "sp-repo-review >=2025.01.22",
49
68
  "validate-pyproject >=0.14",
50
69
  ]
70
+ cov = [
71
+ { include-group = "test" },
72
+ "pytest-cov",
73
+ ]
51
74
  dev = [
52
- "repo-review[test,cli]",
75
+ { include-group = "cov" },
76
+ "sp-repo-review[cli]",
77
+ "validate-pyproject-schema-store[all]",
53
78
  ]
54
79
  docs = [
55
80
  "furo",
@@ -63,24 +88,6 @@ docs = [
63
88
  "sphinx-github-changelog",
64
89
  ]
65
90
 
66
- [project.urls]
67
- Changelog = "https://github.com/scientific-python/repo-review/releases"
68
- Demo = "https://scientific-python.github.io/repo-review"
69
- Documentation = "https://repo-review.readthedocs.io"
70
- Homepage = "https://repo-review.readthedocs.io"
71
- Source = "https://github.com/scientific-python/repo-review"
72
-
73
- [project.scripts]
74
- repo-review = "repo_review.__main__:main"
75
-
76
- [project.entry-points."repo_review.fixtures"]
77
- pyproject = "repo_review.fixtures:pyproject"
78
- list_all = "repo_review.fixtures:list_all"
79
-
80
- [project.entry-points."validate_pyproject.tool_schema"]
81
- repo-review = "repo_review.schema:get_schema"
82
-
83
-
84
91
  [tool.hatch]
85
92
  version.source = "vcs"
86
93
  build.hooks.vcs.version-file = "src/repo_review/_version.py"
@@ -89,7 +96,13 @@ build.hooks.vcs.version-file = "src/repo_review/_version.py"
89
96
  installer = "uv"
90
97
 
91
98
  [tool.hatch.envs.hatch-test]
92
- features = ["test", "cli"]
99
+ features = ["cli"]
100
+ # duplicated since hatch doesn't support groups yet
101
+ dependencies = [
102
+ "pytest >=7",
103
+ "sp-repo-review >=2025.01.22",
104
+ "validate-pyproject >=0.14",
105
+ ]
93
106
  env-vars.PYTHONWARNDEFAULTENCODING = "1"
94
107
 
95
108
  [tool.hatch.envs.lint]
@@ -103,8 +116,19 @@ dependencies = ["pylint>=3.2"]
103
116
  scripts.lint = "pylint repo_review {args}"
104
117
 
105
118
  [tool.hatch.envs.docs]
106
- features = ["docs"]
107
- dependencies = ["sphinx-autobuild"]
119
+ # duplicated since hatch doesn't support groups yet
120
+ dependencies = [
121
+ "furo",
122
+ "myst_parser >=0.13",
123
+ "repo-review[cli]",
124
+ "sphinx >=4.0",
125
+ "sphinx-autodoc-typehints",
126
+ "sphinx-copybutton",
127
+ "sphinxcontrib-programoutput",
128
+ "sphinxext-opengraph",
129
+ "sphinx-github-changelog",
130
+ "sphinx-autobuild",
131
+ ]
108
132
  scripts.linkcheck = "sphinx-build -b=linkcheck docs docs/_build/linkcheck {args}"
109
133
  scripts.html = "sphinx-build --keep-going -n -T -b=html docs docs/_build/html {args}"
110
134
  scripts.serve = "sphinx-autobuild -n -T -b=html docs docs/_build/html {args}"
@@ -204,7 +228,6 @@ extend-select = [
204
228
  ]
205
229
  ignore = [
206
230
  "ISC001", # May collide with formatter
207
- "PT004", # Incorrect check, usefixtures is the correct way to do this
208
231
  "PLR09", # Too many X
209
232
  "PLR2004", # Magic value in comparison
210
233
  ]
@@ -223,3 +246,12 @@ typing-modules = ["repo_review._compat.typing"]
223
246
  [tool.ruff.lint.per-file-ignores]
224
247
  "src/repo_review/_compat/**.py" = ["TID251"]
225
248
  "src/**/__main__.py" = ["T20"]
249
+
250
+ [tool.coverage]
251
+ report.exclude_also = [
252
+ "def __dir__()",
253
+ "if TYPE_CHECKING:",
254
+ '\.\.\.',
255
+ ]
256
+ report.show_missing = true
257
+ report.skip_empty = true
@@ -34,9 +34,9 @@ from .ghpath import GHPath
34
34
  from .html import to_html
35
35
  from .processor import Result, as_simple_dict, collect_all, process
36
36
 
37
- __all__ = ["main", "Formats", "Show", "Status"]
37
+ __all__ = ["Formats", "Show", "Status", "main"]
38
38
 
39
- CODE_THEME = "default"
39
+ CODE_THEME = "ansi_light"
40
40
 
41
41
 
42
42
  def __dir__() -> list[str]:
@@ -163,6 +163,13 @@ def rich_printer(
163
163
  msg.append(rich.text.Text.from_markup(description, style=style))
164
164
  if result.result is None:
165
165
  msg.append(" [skipped]", style="yellow bold")
166
+ if result.skip_reason:
167
+ sr_style = "yellow"
168
+ msg.append(" (", style=sr_style)
169
+ msg.append(
170
+ rich.text.Text.from_markup(result.skip_reason, style=sr_style)
171
+ )
172
+ msg.append(")", style=sr_style)
166
173
  tree.add(msg)
167
174
  elif result.result:
168
175
  msg.append(rich.text.Text.from_markup(" :white_check_mark:"))
@@ -315,6 +322,16 @@ def _remote_path_processor(package: Path) -> Path | GHPath:
315
322
  help="Ignore a check or checks, comma separated.",
316
323
  default="",
317
324
  )
325
+ @click.option(
326
+ "--extend-select",
327
+ help="Checks to run in addition to the ones selected.",
328
+ default="",
329
+ )
330
+ @click.option(
331
+ "--extend-ignore",
332
+ help="Checks to ignore in addition to the ones ignored.",
333
+ default="",
334
+ )
318
335
  @click.option(
319
336
  "--package-dir",
320
337
  "-p",
@@ -327,6 +344,8 @@ def main(
327
344
  stderr_fmt: Formats | None,
328
345
  select: str,
329
346
  ignore: str,
347
+ extend_select: str,
348
+ extend_ignore: str,
330
349
  package_dir: str,
331
350
  show: Show,
332
351
  ) -> None:
@@ -351,6 +370,8 @@ def main(
351
370
  stderr_fmt,
352
371
  select,
353
372
  ignore,
373
+ extend_select,
374
+ extend_ignore,
354
375
  package_dir,
355
376
  add_header=len(packages) > 1,
356
377
  show=show,
@@ -378,6 +399,8 @@ def on_each(
378
399
  stderr_fmt: Literal["rich", "json", "html", "svg"] | None,
379
400
  select: str,
380
401
  ignore: str,
402
+ extend_select: str,
403
+ extend_ignore: str,
381
404
  package_dir: str,
382
405
  *,
383
406
  add_header: bool,
@@ -387,6 +410,8 @@ def on_each(
387
410
 
388
411
  ignore_list = {x.strip() for x in ignore.split(",") if x}
389
412
  select_list = {x.strip() for x in select.split(",") if x}
413
+ extend_ignore_list = {x.strip() for x in extend_ignore.split(",") if x}
414
+ extend_select_list = {x.strip() for x in extend_select.split(",") if x}
390
415
 
391
416
  collected = collect_all(package, subdir=package_dir)
392
417
  if len(collected.checks) == 0:
@@ -407,7 +432,12 @@ def on_each(
407
432
  header = package.name
408
433
 
409
434
  families, processed = process(
410
- base_package, select=select_list, ignore=ignore_list, subdir=package_dir
435
+ base_package,
436
+ select=select_list,
437
+ ignore=ignore_list,
438
+ extend_select=extend_select_list,
439
+ extend_ignore=extend_ignore_list,
440
+ subdir=package_dir,
411
441
  )
412
442
 
413
443
  status: Status = "passed" if processed else "empty"
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.11.2'
16
- __version_tuple__ = version_tuple = (0, 11, 2)
15
+ __version__ = version = '0.12.0'
16
+ __version_tuple__ = version_tuple = (0, 12, 0)
@@ -6,7 +6,7 @@ from typing import Any, Protocol
6
6
 
7
7
  from .fixtures import apply_fixtures
8
8
 
9
- __all__ = ["Check", "collect_checks", "is_allowed", "get_check_url"]
9
+ __all__ = ["Check", "collect_checks", "get_check_url", "is_allowed", "name_matches"]
10
10
 
11
11
 
12
12
  def __dir__() -> list[str]:
@@ -70,6 +70,25 @@ def collect_checks(fixtures: Mapping[str, Any]) -> dict[str, Check]:
70
70
  }
71
71
 
72
72
 
73
+ def name_matches(name: str, selectors: Set[str]) -> str:
74
+ """
75
+ Checks if the name is contained in the matchers. The selectors can be the
76
+ exact name or just the non-number prefix. Returns the selector that matched,
77
+ or an empty string if no match.
78
+
79
+ :param name: The name to check.
80
+ :param expr: The expression to check against.
81
+
82
+ :return: The matched selector if the name matches a selector, or an empty string if no match.
83
+ """
84
+ if name in selectors:
85
+ return name
86
+ short_name = name.rstrip("0123456789")
87
+ if short_name in selectors:
88
+ return short_name
89
+ return ""
90
+
91
+
73
92
  def is_allowed(select: Set[str], ignore: Set[str], name: str) -> bool:
74
93
  """
75
94
  Skips the check if the name is in the ignore list or if the name without the
@@ -82,15 +101,10 @@ def is_allowed(select: Set[str], ignore: Set[str], name: str) -> bool:
82
101
 
83
102
  :return: True if this check is allowed, False otherwise.
84
103
  """
85
- if (
86
- select
87
- and name not in select
88
- and name.rstrip("0123456789") not in select
89
- and "*" not in select
90
- ):
104
+ if select and not name_matches(name, select) and "*" not in select:
91
105
  return False
92
106
 
93
- return name not in ignore and name.rstrip("0123456789") not in ignore
107
+ return not name_matches(name, ignore)
94
108
 
95
109
 
96
110
  def get_check_url(name: str, check: Check) -> str:
@@ -12,11 +12,11 @@ from ._compat.importlib.resources.abc import Traversable
12
12
  from .ghpath import EmptyTraversable
13
13
 
14
14
  __all__ = [
15
- "pyproject",
16
- "list_all",
17
- "compute_fixtures",
18
15
  "apply_fixtures",
19
16
  "collect_fixtures",
17
+ "compute_fixtures",
18
+ "list_all",
19
+ "pyproject",
20
20
  ]
21
21
 
22
22
 
@@ -36,8 +36,7 @@ def pyproject(package: Traversable) -> dict[str, Any]:
36
36
  pyproject_path = package.joinpath("pyproject.toml")
37
37
  if pyproject_path.is_file():
38
38
  with pyproject_path.open("rb") as f:
39
- # Type ignore fixed in https://github.com/hukkin/tomli/pull/215
40
- return tomllib.load(f) # type: ignore[arg-type]
39
+ return tomllib.load(f)
41
40
  return {}
42
41
 
43
42
 
@@ -14,7 +14,7 @@ from typing import Literal
14
14
 
15
15
  from ._compat.importlib.resources.abc import Traversable
16
16
 
17
- __all__ = ["GHPath", "EmptyTraversable"]
17
+ __all__ = ["EmptyTraversable", "GHPath"]
18
18
 
19
19
 
20
20
  def __dir__() -> list[str]:
@@ -61,7 +61,7 @@ def to_html(
61
61
  else "red"
62
62
  )
63
63
  icon = (
64
- "&#9888;&#65039;"
64
+ ("&#128311;" if result.skip_reason else "&#9888;&#65039;")
65
65
  if result.result is None
66
66
  else "&#9989;"
67
67
  if result.result
@@ -79,6 +79,11 @@ def to_html(
79
79
  if result.url
80
80
  else result.description
81
81
  )
82
+ if result.skip_reason:
83
+ description += (
84
+ f'<br/><span style="color:DarkKhaki;"><b>Skipped:</b> '
85
+ f"<em>{md.render(result.skip_reason)}</em></span>"
86
+ )
82
87
  print(f'<tr style="color: {color};">')
83
88
  print(f'<td><span role="img" aria-label="{result_txt}">{icon}</span></td>')
84
89
  print(f'<td nowrap="nowrap">{result.name}</td>')
@@ -17,6 +17,7 @@ from .checks import (
17
17
  collect_checks,
18
18
  get_check_url,
19
19
  is_allowed,
20
+ name_matches,
20
21
  process_result_bool,
21
22
  )
22
23
  from .families import Family, collect_families
@@ -30,8 +31,8 @@ __all__ = [
30
31
  "ResultDict",
31
32
  "as_simple_dict",
32
33
  "collect_all",
33
- "process",
34
34
  "md_as_html",
35
+ "process",
35
36
  ]
36
37
 
37
38
 
@@ -63,6 +64,7 @@ class ResultDict(typing.TypedDict):
63
64
  result: bool | None #: The result, None means skip
64
65
  err_msg: str #: The error message if the result is false, in markdown format
65
66
  url: str #: An optional URL (empty string if missing)
67
+ skip_reason: str #: The reason for the skip, if given (empty string if not)
66
68
 
67
69
 
68
70
  @dataclasses.dataclass(frozen=True, kw_only=True)
@@ -75,6 +77,7 @@ class Result:
75
77
  name: str #: The name of the check
76
78
  description: str #: The short description of what the check looks for
77
79
  result: bool | None #: The result, None means skip
80
+ skip_reason: str = "" #: The reason for the skip, if given
78
81
  err_msg: str = "" #: The error message if the result is false, in markdown format
79
82
  url: str = "" #: An optional URL (empty string if missing)
80
83
 
@@ -179,6 +182,8 @@ def process(
179
182
  *,
180
183
  select: Set[str] = frozenset(),
181
184
  ignore: Set[str] = frozenset(),
185
+ extend_select: Set[str] = frozenset(),
186
+ extend_ignore: Set[str] = frozenset(),
182
187
  subdir: str = "",
183
188
  ) -> ProcessReturn:
184
189
  """
@@ -199,8 +204,12 @@ def process(
199
204
 
200
205
  # Collect our own config
201
206
  config = pyproject(package).get("tool", {}).get("repo-review", {})
202
- select_checks = select if select else frozenset(config.get("select", ()))
203
- skip_checks = ignore if ignore else frozenset(config.get("ignore", ()))
207
+ ignore_pyproject: list[str] | dict[str, str] = config.get("ignore", [])
208
+ select_checks = (
209
+ select if select else frozenset(config.get("select", ()))
210
+ ) | extend_select
211
+ skip_checks = (ignore if ignore else frozenset(ignore_pyproject)) | extend_ignore
212
+ skip_reasons = ignore_pyproject if isinstance(ignore_pyproject, dict) else {}
204
213
 
205
214
  # Make a graph of the check's interdependencies
206
215
  graph: dict[str, Set[str]] = {
@@ -234,9 +243,14 @@ def process(
234
243
  result = None if completed[task_name] is None else not completed[task_name]
235
244
  doc = check.__doc__ or ""
236
245
  err_msg = completed[task_name] or ""
246
+ skip_reason = ""
237
247
 
238
248
  if not is_allowed(select_checks, skip_checks, task_name):
239
- continue
249
+ key = name_matches(task_name, skip_reasons.keys())
250
+ if not key or not skip_reasons.get(key, ""):
251
+ continue
252
+ result = None
253
+ skip_reason = skip_reasons[key]
240
254
 
241
255
  result_list.append(
242
256
  Result(
@@ -246,6 +260,7 @@ def process(
246
260
  result=result,
247
261
  err_msg=textwrap.dedent(err_msg),
248
262
  url=get_check_url(task_name, check),
263
+ skip_reason=skip_reason,
249
264
  )
250
265
  )
251
266
 
@@ -13,7 +13,7 @@ from .checks import Check, get_check_url, process_result_bool
13
13
  from .fixtures import apply_fixtures
14
14
  from .processor import Result
15
15
 
16
- __all__ = ["toml_loads", "compute_check"]
16
+ __all__ = ["compute_check", "toml_loads"]
17
17
 
18
18
 
19
19
  def __dir__() -> list[str]:
@@ -6,12 +6,14 @@ import repo_review.processor
6
6
 
7
7
 
8
8
  @pytest.fixture(autouse=True)
9
- def patch_entry_points(local_entry_points: object) -> None: # noqa: ARG001
9
+ def patch_entry_points(local_entry_points: object) -> None:
10
10
  pass
11
11
 
12
12
 
13
13
  def test_pyproject() -> None:
14
- families, results = repo_review.processor.process(Path())
14
+ families, results = repo_review.processor.process(
15
+ Path(), extend_ignore={"X", "PP303"}
16
+ )
15
17
 
16
18
  assert families == {
17
19
  "general": {},
@@ -26,7 +28,9 @@ def test_pyproject() -> None:
26
28
 
27
29
 
28
30
  def test_no_pyproject() -> None:
29
- families, results = repo_review.processor.process(Path("tests"))
31
+ families, results = repo_review.processor.process(
32
+ Path("tests"), extend_ignore={"X", "PP303"}
33
+ )
30
34
 
31
35
  assert families == {
32
36
  "general": {},
@@ -58,12 +62,13 @@ def test_empty_pyproject() -> None:
58
62
  "description": "Has flit_core.buildapi backend",
59
63
  "name": "PyProject",
60
64
  },
65
+ "skipped": {},
61
66
  }
62
- assert len(results) == 9
67
+ assert len(results) == 12
63
68
 
64
69
  assert (
65
70
  sum(result.result is None for result in results if result.family == "pyproject")
66
- == 1
71
+ == 2
67
72
  )
68
73
  assert (
69
74
  sum(result.result for result in results if isinstance(result.result, bool)) == 6
@@ -72,3 +77,6 @@ def test_empty_pyproject() -> None:
72
77
  sum(result.result is None for result in results if result.family == "general")
73
78
  == 0
74
79
  )
80
+ assert sum(1 for result in results if result.skip_reason) == 3
81
+ assert sum(1 for result in results if result.skip_reason == "One skip") == 1
82
+ assert sum(1 for result in results if result.skip_reason == "Group skip") == 2
@@ -142,10 +142,51 @@ class PP302(PyProject):
142
142
  return "minversion" in options and float(options["minversion"]) >= 6
143
143
 
144
144
 
145
- def repo_review_checks() -> dict[str, PyProject | General]:
146
- return {p.__name__: p() for p in PyProject.__subclasses__()} | {
145
+ class PP999(PyProject):
146
+ "Skipped check (single)"
147
+
148
+ @staticmethod
149
+ def check() -> bool:
150
+ "Not used"
151
+ return False
152
+
153
+
154
+ class X101:
155
+ "Skipped check (multi)"
156
+
157
+ family = "skipped"
158
+
159
+ @staticmethod
160
+ def check() -> bool:
161
+ "Not used"
162
+ return False
163
+
164
+
165
+ class X102:
166
+ "Skipped check (multi)"
167
+
168
+ family = "skipped"
169
+
170
+ @staticmethod
171
+ def check() -> bool:
172
+ "Not used"
173
+ return False
174
+
175
+
176
+ def repo_review_checks(
177
+ pyproject: dict[str, Any],
178
+ ) -> dict[str, PyProject | General | X101 | X102]:
179
+ ret = {p.__name__: p() for p in PyProject.__subclasses__()} | {
147
180
  p.__name__: p() for p in General.__subclasses__()
148
181
  }
182
+ extra_checks = (
183
+ pyproject.get("tool", {}).get("repo-review-local", {}).get("extra", False)
184
+ )
185
+ return (
186
+ (ret | {"X101": X101()} | {"X102": X102()})
187
+ if extra_checks
188
+ else {k: v for k, v in ret.items() if k != "PP999"}
189
+ )
149
190
 
150
191
 
151
192
  def repo_review_families(pyproject: dict[str, Any]) -> dict[str, dict[str, str]]:
@@ -16,3 +16,11 @@ pyproject = "pyproject:repo_review_checks"
16
16
  pyproject = "pyproject:repo_review_families"
17
17
 
18
18
  [tool.example]
19
+
20
+
21
+ [tool.repo-review.ignore]
22
+ "PP999" = "One skip"
23
+ "X" = "Group skip"
24
+
25
+ [tool.repo-review-local]
26
+ extra = true
File without changes
File without changes
File without changes
File without changes
File without changes