repo-review 0.11.3__tar.gz → 0.12.1__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.
- {repo_review-0.11.3 → repo_review-0.12.1}/.github/workflows/cd.yml +3 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.github/workflows/ci.yml +9 -1
- {repo_review-0.11.3 → repo_review-0.12.1}/.pre-commit-config.yaml +5 -5
- {repo_review-0.11.3 → repo_review-0.12.1}/PKG-INFO +8 -3
- {repo_review-0.11.3 → repo_review-0.12.1}/README.md +5 -1
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/index.html +5 -5
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/intro.md +18 -2
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/webapp.js +9 -5
- {repo_review-0.11.3 → repo_review-0.12.1}/pyproject.toml +16 -3
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/__main__.py +31 -1
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_version.py +2 -2
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/checks.py +22 -8
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/html.py +10 -8
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/processor.py +39 -4
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_self.py +12 -4
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_utilities/pyproject.py +43 -2
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_utilities/pyproject.toml +8 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.devcontainer/devcontainer.json +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.git_archival.txt +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.gitattributes +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.github/CONTRIBUTING.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.github/ISSUE_TEMPLATE/new-issue.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.github/dependabot.yml +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.github/release.yml +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.gitignore +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.pre-commit-hooks.yaml +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/.readthedocs.yaml +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/LICENSE +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/action.yml +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/.nojekyll +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/api/repo_review.resources.rst +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/api/repo_review.rst +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/changelog.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/checks.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/cli.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/conf.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/families.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/fixtures.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/index.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/plugins.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/programmatic.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/docs/webapp.md +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/__init__.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_compat/__init__.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_compat/importlib/__init__.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_compat/importlib/resources/__init__.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_compat/importlib/resources/abc.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_compat/tomllib.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_compat/typing.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_version.pyi +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/families.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/fixtures.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/ghpath.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/py.typed +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/resources/__init__.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/resources/repo-review.schema.json +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/schema.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/testing.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/conftest.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_checks.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_cmd.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_depends.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_families.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_fixtures.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_multi.py +0 -0
- {repo_review-0.11.3 → repo_review-0.12.1}/tests/test_package.py +0 -0
@@ -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==
|
123
|
+
plugins: sp-repo-review==2025.01.22
|
@@ -11,7 +11,7 @@ repos:
|
|
11
11
|
additional_dependencies: [black==24.*]
|
12
12
|
|
13
13
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
14
|
-
rev: "v0.
|
14
|
+
rev: "v0.9.4"
|
15
15
|
hooks:
|
16
16
|
- id: ruff
|
17
17
|
args: ["--fix", "--show-fixes"]
|
@@ -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.
|
48
|
+
rev: v1.14.1
|
49
49
|
hooks:
|
50
50
|
- id: mypy
|
51
51
|
files: (src|web|tests)
|
@@ -60,7 +60,7 @@ repos:
|
|
60
60
|
- types-PyYAML
|
61
61
|
|
62
62
|
- repo: https://github.com/codespell-project/codespell
|
63
|
-
rev: v2.
|
63
|
+
rev: v2.4.1
|
64
64
|
hooks:
|
65
65
|
- id: codespell
|
66
66
|
args: ["-Lhist,absense", "-w"]
|
@@ -79,12 +79,12 @@ repos:
|
|
79
79
|
exclude: .pre-commit-config.yaml
|
80
80
|
|
81
81
|
- repo: https://github.com/henryiii/validate-pyproject-schema-store
|
82
|
-
rev:
|
82
|
+
rev: 2025.02.03
|
83
83
|
hooks:
|
84
84
|
- id: validate-pyproject
|
85
85
|
|
86
86
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
87
|
-
rev: 0.
|
87
|
+
rev: 0.31.1
|
88
88
|
hooks:
|
89
89
|
- id: check-dependabot
|
90
90
|
- id: check-github-workflows
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: repo_review
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.12.1
|
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
|
@@ -8,6 +8,7 @@ Project-URL: Documentation, https://repo-review.readthedocs.io
|
|
8
8
|
Project-URL: Homepage, https://repo-review.readthedocs.io
|
9
9
|
Project-URL: Source, https://github.com/scientific-python/repo-review
|
10
10
|
Author-email: Henry Schreiner <henryfs@princeton.edu>
|
11
|
+
License-File: LICENSE
|
11
12
|
Classifier: Development Status :: 4 - Beta
|
12
13
|
Classifier: Environment :: Console
|
13
14
|
Classifier: Environment :: WebAssembly :: Emscripten
|
@@ -101,8 +102,12 @@ select = ["A", "B", "C100"]
|
|
101
102
|
ignore = ["A100"]
|
102
103
|
```
|
103
104
|
|
105
|
+
The ignore list can also be a table, with reasons for values.
|
106
|
+
|
104
107
|
If `--select` or `--ignore` are given on the command line, they will override
|
105
|
-
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.
|
106
111
|
|
107
112
|
## Comparison to other frameworks
|
108
113
|
|
@@ -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
|
|
@@ -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.
|
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.
|
68
|
-
"sp-repo-review==
|
69
|
-
"validate-pyproject-schema-store==
|
70
|
-
"validate-pyproject[all]~=0.
|
67
|
+
"repo-review~=0.12.0",
|
68
|
+
"sp-repo-review==2025.01.22",
|
69
|
+
"validate-pyproject-schema-store==2025.02.03",
|
70
|
+
"validate-pyproject[all]~=0.22.0",
|
71
71
|
]}
|
72
72
|
/>,
|
73
73
|
);
|
@@ -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:
|
@@ -93,7 +93,7 @@ function Results(props) {
|
|
93
93
|
variant="body2"
|
94
94
|
color="text.disabled"
|
95
95
|
>
|
96
|
-
{
|
96
|
+
{` [skipped] ${result.skip_reason}`}
|
97
97
|
</MaterialUI.Typography>
|
98
98
|
);
|
99
99
|
const msg = (
|
@@ -200,6 +200,7 @@ class App extends React.Component {
|
|
200
200
|
msg: `<p>${DEFAULT_MSG}</p><h4>Packages:</h4> ${deps_str}`,
|
201
201
|
progress: false,
|
202
202
|
err_msg: "",
|
203
|
+
skip_reason: "",
|
203
204
|
url: "",
|
204
205
|
};
|
205
206
|
this.pyodide_promise = prepare_pyodide(props.deps);
|
@@ -231,15 +232,17 @@ class App extends React.Component {
|
|
231
232
|
families_checks = pyodide.runPython(`
|
232
233
|
from repo_review.processor import process, md_as_html
|
233
234
|
from repo_review.ghpath import GHPath
|
235
|
+
from dataclasses import replace
|
234
236
|
|
235
237
|
package = GHPath(repo="${state.repo}", branch="${state.branch}")
|
236
|
-
|
238
|
+
families, checks = process(package)
|
237
239
|
|
238
|
-
for v in
|
240
|
+
for v in families.values():
|
239
241
|
if v.get("description"):
|
240
242
|
v["description"] = md_as_html(v["description"])
|
243
|
+
checks = [replace(v, err_msg=md_as_html(v.err_msg), skip_reason=md_as_html(v.skip_reason)) for v in checks]
|
241
244
|
|
242
|
-
|
245
|
+
(families, checks)
|
243
246
|
`);
|
244
247
|
} catch (e) {
|
245
248
|
if (e.message.includes("KeyError: 'tree'")) {
|
@@ -276,8 +279,9 @@ class App extends React.Component {
|
|
276
279
|
name: val.name.toString(),
|
277
280
|
description: val.description.toString(),
|
278
281
|
state: val.result,
|
279
|
-
err_msg: val.
|
282
|
+
err_msg: val.err_msg.toString(),
|
280
283
|
url: val.url.toString(),
|
284
|
+
skip_reason: val.skip_reason.toString(),
|
281
285
|
});
|
282
286
|
}
|
283
287
|
|
@@ -64,11 +64,15 @@ cli = [
|
|
64
64
|
[dependency-groups]
|
65
65
|
test = [
|
66
66
|
"pytest >=7",
|
67
|
-
"sp-repo-review >=
|
67
|
+
"sp-repo-review >=2025.01.22",
|
68
68
|
"validate-pyproject >=0.14",
|
69
69
|
]
|
70
|
-
|
70
|
+
cov = [
|
71
71
|
{ include-group = "test" },
|
72
|
+
"pytest-cov",
|
73
|
+
]
|
74
|
+
dev = [
|
75
|
+
{ include-group = "cov" },
|
72
76
|
"sp-repo-review[cli]",
|
73
77
|
"validate-pyproject-schema-store[all]",
|
74
78
|
]
|
@@ -96,7 +100,7 @@ features = ["cli"]
|
|
96
100
|
# duplicated since hatch doesn't support groups yet
|
97
101
|
dependencies = [
|
98
102
|
"pytest >=7",
|
99
|
-
"sp-repo-review >=
|
103
|
+
"sp-repo-review >=2025.01.22",
|
100
104
|
"validate-pyproject >=0.14",
|
101
105
|
]
|
102
106
|
env-vars.PYTHONWARNDEFAULTENCODING = "1"
|
@@ -242,3 +246,12 @@ typing-modules = ["repo_review._compat.typing"]
|
|
242
246
|
[tool.ruff.lint.per-file-ignores]
|
243
247
|
"src/repo_review/_compat/**.py" = ["TID251"]
|
244
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
|
@@ -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,
|
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"
|
@@ -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", "get_check_url", "is_allowed"]
|
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
|
107
|
+
return not name_matches(name, ignore)
|
94
108
|
|
95
109
|
|
96
110
|
def get_check_url(name: str, check: Check) -> str:
|
@@ -6,10 +6,8 @@ import io
|
|
6
6
|
import typing
|
7
7
|
from collections.abc import Mapping, Sequence
|
8
8
|
|
9
|
-
import markdown_it
|
10
|
-
|
11
9
|
from .families import Family, get_family_description, get_family_name
|
12
|
-
from .processor import Result
|
10
|
+
from .processor import Result, md_as_html
|
13
11
|
|
14
12
|
if typing.TYPE_CHECKING:
|
15
13
|
from .__main__ import Status
|
@@ -37,16 +35,15 @@ def to_html(
|
|
37
35
|
"""
|
38
36
|
out = io.StringIO()
|
39
37
|
print = functools.partial(builtins.print, file=out)
|
40
|
-
md = markdown_it.MarkdownIt()
|
41
38
|
|
42
39
|
for family in families:
|
43
40
|
family_name = get_family_name(families, family)
|
44
41
|
family_description = get_family_description(families, family)
|
45
|
-
family_results = [r for r in processed if r.family == family]
|
42
|
+
family_results = [r.md_as_html() for r in processed if r.family == family]
|
46
43
|
if family_results or family_description:
|
47
44
|
print(f"<h3>{family_name}</h3>")
|
48
45
|
if family_description:
|
49
|
-
print(
|
46
|
+
print("<p>", md_as_html(family_description), "</p>")
|
50
47
|
if family_results:
|
51
48
|
print("<table>")
|
52
49
|
print(
|
@@ -61,7 +58,7 @@ def to_html(
|
|
61
58
|
else "red"
|
62
59
|
)
|
63
60
|
icon = (
|
64
|
-
"⚠️"
|
61
|
+
("🔷" if result.skip_reason else "⚠️")
|
65
62
|
if result.result is None
|
66
63
|
else "✅"
|
67
64
|
if result.result
|
@@ -79,6 +76,11 @@ def to_html(
|
|
79
76
|
if result.url
|
80
77
|
else result.description
|
81
78
|
)
|
79
|
+
if result.skip_reason:
|
80
|
+
description += (
|
81
|
+
f'<br/><span style="color:DarkKhaki;"><b>Skipped:</b> '
|
82
|
+
f"<em>{result.skip_reason}</em></span>"
|
83
|
+
)
|
82
84
|
print(f'<tr style="color: {color};">')
|
83
85
|
print(f'<td><span role="img" aria-label="{result_txt}">{icon}</span></td>')
|
84
86
|
print(f'<td nowrap="nowrap">{result.name}</td>')
|
@@ -88,7 +90,7 @@ def to_html(
|
|
88
90
|
print("<td>")
|
89
91
|
print(description)
|
90
92
|
print("<br/>")
|
91
|
-
print(
|
93
|
+
print(result.err_msg)
|
92
94
|
print("</td>")
|
93
95
|
print("</tr>")
|
94
96
|
if family_results:
|
@@ -3,11 +3,12 @@ from __future__ import annotations
|
|
3
3
|
import copy
|
4
4
|
import dataclasses
|
5
5
|
import graphlib
|
6
|
+
import sys
|
6
7
|
import textwrap
|
7
8
|
import typing
|
8
9
|
import warnings
|
9
10
|
from collections.abc import Mapping, Set
|
10
|
-
from typing import Any, TypeVar
|
11
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
11
12
|
|
12
13
|
import markdown_it
|
13
14
|
|
@@ -17,12 +18,19 @@ from .checks import (
|
|
17
18
|
collect_checks,
|
18
19
|
get_check_url,
|
19
20
|
is_allowed,
|
21
|
+
name_matches,
|
20
22
|
process_result_bool,
|
21
23
|
)
|
22
24
|
from .families import Family, collect_families
|
23
25
|
from .fixtures import apply_fixtures, collect_fixtures, compute_fixtures, pyproject
|
24
26
|
from .ghpath import EmptyTraversable
|
25
27
|
|
28
|
+
if TYPE_CHECKING:
|
29
|
+
if sys.version_info >= (3, 11):
|
30
|
+
from typing import Self
|
31
|
+
else:
|
32
|
+
from typing_extensions import Self
|
33
|
+
|
26
34
|
__all__ = [
|
27
35
|
"CollectionReturn",
|
28
36
|
"ProcessReturn",
|
@@ -63,6 +71,7 @@ class ResultDict(typing.TypedDict):
|
|
63
71
|
result: bool | None #: The result, None means skip
|
64
72
|
err_msg: str #: The error message if the result is false, in markdown format
|
65
73
|
url: str #: An optional URL (empty string if missing)
|
74
|
+
skip_reason: str #: The reason for the skip, if given (empty string if not)
|
66
75
|
|
67
76
|
|
68
77
|
@dataclasses.dataclass(frozen=True, kw_only=True)
|
@@ -75,15 +84,29 @@ class Result:
|
|
75
84
|
name: str #: The name of the check
|
76
85
|
description: str #: The short description of what the check looks for
|
77
86
|
result: bool | None #: The result, None means skip
|
87
|
+
skip_reason: str = "" #: The reason for the skip, if given
|
78
88
|
err_msg: str = "" #: The error message if the result is false, in markdown format
|
79
89
|
url: str = "" #: An optional URL (empty string if missing)
|
80
90
|
|
81
91
|
def err_as_html(self) -> str:
|
82
92
|
"""
|
83
93
|
Produces HTML from the error message, assuming it is in markdown.
|
94
|
+
Deprecated, use :meth:`md_as_html` directly instead.
|
84
95
|
"""
|
85
96
|
return md_as_html(self.err_msg)
|
86
97
|
|
98
|
+
def md_as_html(self) -> Self:
|
99
|
+
"""
|
100
|
+
Process fields that are assumed to be markdown.
|
101
|
+
|
102
|
+
.. versionadded:: 0.12.1
|
103
|
+
"""
|
104
|
+
return dataclasses.replace(
|
105
|
+
self,
|
106
|
+
err_msg=md_as_html(self.err_msg),
|
107
|
+
skip_reason=md_as_html(self.skip_reason),
|
108
|
+
)
|
109
|
+
|
87
110
|
|
88
111
|
class ProcessReturn(typing.NamedTuple):
|
89
112
|
"""
|
@@ -179,6 +202,8 @@ def process(
|
|
179
202
|
*,
|
180
203
|
select: Set[str] = frozenset(),
|
181
204
|
ignore: Set[str] = frozenset(),
|
205
|
+
extend_select: Set[str] = frozenset(),
|
206
|
+
extend_ignore: Set[str] = frozenset(),
|
182
207
|
subdir: str = "",
|
183
208
|
) -> ProcessReturn:
|
184
209
|
"""
|
@@ -199,8 +224,12 @@ def process(
|
|
199
224
|
|
200
225
|
# Collect our own config
|
201
226
|
config = pyproject(package).get("tool", {}).get("repo-review", {})
|
202
|
-
|
203
|
-
|
227
|
+
ignore_pyproject: list[str] | dict[str, str] = config.get("ignore", [])
|
228
|
+
select_checks = (
|
229
|
+
select if select else frozenset(config.get("select", ()))
|
230
|
+
) | extend_select
|
231
|
+
skip_checks = (ignore if ignore else frozenset(ignore_pyproject)) | extend_ignore
|
232
|
+
skip_reasons = ignore_pyproject if isinstance(ignore_pyproject, dict) else {}
|
204
233
|
|
205
234
|
# Make a graph of the check's interdependencies
|
206
235
|
graph: dict[str, Set[str]] = {
|
@@ -234,9 +263,14 @@ def process(
|
|
234
263
|
result = None if completed[task_name] is None else not completed[task_name]
|
235
264
|
doc = check.__doc__ or ""
|
236
265
|
err_msg = completed[task_name] or ""
|
266
|
+
skip_reason = ""
|
237
267
|
|
238
268
|
if not is_allowed(select_checks, skip_checks, task_name):
|
239
|
-
|
269
|
+
key = name_matches(task_name, skip_reasons.keys())
|
270
|
+
if not key or not skip_reasons.get(key, ""):
|
271
|
+
continue
|
272
|
+
result = None
|
273
|
+
skip_reason = skip_reasons[key]
|
240
274
|
|
241
275
|
result_list.append(
|
242
276
|
Result(
|
@@ -246,6 +280,7 @@ def process(
|
|
246
280
|
result=result,
|
247
281
|
err_msg=textwrap.dedent(err_msg),
|
248
282
|
url=get_check_url(task_name, check),
|
283
|
+
skip_reason=skip_reason,
|
249
284
|
)
|
250
285
|
)
|
251
286
|
|
@@ -11,7 +11,9 @@ def patch_entry_points(local_entry_points: object) -> None:
|
|
11
11
|
|
12
12
|
|
13
13
|
def test_pyproject() -> None:
|
14
|
-
families, results = repo_review.processor.process(
|
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(
|
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) ==
|
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
|
-
==
|
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
|
-
|
146
|
-
|
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]]:
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_compat/importlib/resources/__init__.py
RENAMED
File without changes
|
{repo_review-0.11.3 → repo_review-0.12.1}/src/repo_review/_compat/importlib/resources/abc.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|