pytest-eo 0.5.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.
- pytest_eo-0.5.1/.claude/hooks/eo-gate.sh +53 -0
- pytest_eo-0.5.1/.claude/settings.json +29 -0
- pytest_eo-0.5.1/.github/workflows/codecov.yml +20 -0
- pytest_eo-0.5.1/.github/workflows/pypi.yml +19 -0
- pytest_eo-0.5.1/.gitignore +10 -0
- pytest_eo-0.5.1/.rultor.yml +17 -0
- pytest_eo-0.5.1/CLAUDE.md +101 -0
- pytest_eo-0.5.1/LICENSE.txt +21 -0
- pytest_eo-0.5.1/Makefile +9 -0
- pytest_eo-0.5.1/PKG-INFO +177 -0
- pytest_eo-0.5.1/README.md +157 -0
- pytest_eo-0.5.1/pyproject.toml +54 -0
- pytest_eo-0.5.1/src/pytest_eo/__init__.py +1 -0
- pytest_eo-0.5.1/src/pytest_eo/angry_tests.py +44 -0
- pytest_eo-0.5.1/src/pytest_eo/assertion.py +30 -0
- pytest_eo-0.5.1/src/pytest_eo/assigned.py +32 -0
- pytest_eo-0.5.1/src/pytest_eo/blank_lines.py +42 -0
- pytest_eo-0.5.1/src/pytest_eo/bodies.py +36 -0
- pytest_eo-0.5.1/src/pytest_eo/bundled.py +20 -0
- pytest_eo-0.5.1/src/pytest_eo/callee.py +23 -0
- pytest_eo-0.5.1/src/pytest_eo/capacity.py +50 -0
- pytest_eo-0.5.1/src/pytest_eo/check.py +17 -0
- pytest_eo-0.5.1/src/pytest_eo/command.py +13 -0
- pytest_eo-0.5.1/src/pytest_eo/comment.py +42 -0
- pytest_eo-0.5.1/src/pytest_eo/comments.py +42 -0
- pytest_eo-0.5.1/src/pytest_eo/config/flake8.ini +23 -0
- pytest_eo-0.5.1/src/pytest_eo/config/mypy.ini +5 -0
- pytest_eo-0.5.1/src/pytest_eo/config/ruff.toml +43 -0
- pytest_eo-0.5.1/src/pytest_eo/edges.py +28 -0
- pytest_eo-0.5.1/src/pytest_eo/encapsulation.py +30 -0
- pytest_eo-0.5.1/src/pytest_eo/ending.py +30 -0
- pytest_eo-0.5.1/src/pytest_eo/eo_item.py +41 -0
- pytest_eo-0.5.1/src/pytest_eo/eo_table.py +29 -0
- pytest_eo-0.5.1/src/pytest_eo/excludes.py +25 -0
- pytest_eo-0.5.1/src/pytest_eo/extension.py +12 -0
- pytest_eo-0.5.1/src/pytest_eo/fields.py +40 -0
- pytest_eo-0.5.1/src/pytest_eo/files.py +12 -0
- pytest_eo-0.5.1/src/pytest_eo/final_assertion.py +46 -0
- pytest_eo-0.5.1/src/pytest_eo/flake_check.py +49 -0
- pytest_eo-0.5.1/src/pytest_eo/heading.py +35 -0
- pytest_eo-0.5.1/src/pytest_eo/inline_comments.py +44 -0
- pytest_eo-0.5.1/src/pytest_eo/layout.py +15 -0
- pytest_eo-0.5.1/src/pytest_eo/margin.py +42 -0
- pytest_eo-0.5.1/src/pytest_eo/message.py +60 -0
- pytest_eo-0.5.1/src/pytest_eo/module_constants.py +32 -0
- pytest_eo-0.5.1/src/pytest_eo/mypy_check.py +43 -0
- pytest_eo-0.5.1/src/pytest_eo/narrow_suppressions.py +72 -0
- pytest_eo-0.5.1/src/pytest_eo/negative_messages.py +40 -0
- pytest_eo-0.5.1/src/pytest_eo/output.py +37 -0
- pytest_eo-0.5.1/src/pytest_eo/owned.py +28 -0
- pytest_eo-0.5.1/src/pytest_eo/place.py +21 -0
- pytest_eo-0.5.1/src/pytest_eo/plugin.py +45 -0
- pytest_eo-0.5.1/src/pytest_eo/printout.py +18 -0
- pytest_eo-0.5.1/src/pytest_eo/profile.py +13 -0
- pytest_eo-0.5.1/src/pytest_eo/project.py +37 -0
- pytest_eo-0.5.1/src/pytest_eo/project_files.py +39 -0
- pytest_eo-0.5.1/src/pytest_eo/python_module.py +36 -0
- pytest_eo-0.5.1/src/pytest_eo/raising.py +27 -0
- pytest_eo-0.5.1/src/pytest_eo/remark.py +25 -0
- pytest_eo-0.5.1/src/pytest_eo/ruff_check.py +50 -0
- pytest_eo-0.5.1/src/pytest_eo/ruff_format_check.py +46 -0
- pytest_eo-0.5.1/src/pytest_eo/rule.py +12 -0
- pytest_eo-0.5.1/src/pytest_eo/shared.py +35 -0
- pytest_eo-0.5.1/src/pytest_eo/span.py +28 -0
- pytest_eo-0.5.1/src/pytest_eo/strict.py +112 -0
- pytest_eo-0.5.1/src/pytest_eo/style_violations_error.py +15 -0
- pytest_eo-0.5.1/src/pytest_eo/suppression_check.py +44 -0
- pytest_eo-0.5.1/src/pytest_eo/table.py +11 -0
- pytest_eo-0.5.1/src/pytest_eo/text.py +28 -0
- pytest_eo-0.5.1/src/pytest_eo/tool_failed_error.py +15 -0
- pytest_eo-0.5.1/src/pytest_eo/trial.py +37 -0
- pytest_eo-0.5.1/src/pytest_eo/verified.py +28 -0
- pytest_eo-0.5.1/src/pytest_eo/violation.py +24 -0
- pytest_eo-0.5.1/src/pytest_eo/wide_suppressions.py +45 -0
- pytest_eo-0.5.1/tests/__init__.py +1 -0
- pytest_eo-0.5.1/tests/conftest.py +3 -0
- pytest_eo-0.5.1/tests/it/__init__.py +1 -0
- pytest_eo-0.5.1/tests/it/test_eo_gate.py +30 -0
- pytest_eo-0.5.1/tests/it/test_plugin.py +469 -0
- pytest_eo-0.5.1/tests/support/__init__.py +1 -0
- pytest_eo-0.5.1/tests/support/fake_command.py +22 -0
- pytest_eo-0.5.1/tests/support/fake_files.py +22 -0
- pytest_eo-0.5.1/tests/support/fake_table.py +22 -0
- pytest_eo-0.5.1/tests/test_angry_tests.py +293 -0
- pytest_eo-0.5.1/tests/test_assertion.py +37 -0
- pytest_eo-0.5.1/tests/test_assigned.py +29 -0
- pytest_eo-0.5.1/tests/test_blank_lines.py +69 -0
- pytest_eo-0.5.1/tests/test_bodies.py +37 -0
- pytest_eo-0.5.1/tests/test_bundled.py +13 -0
- pytest_eo-0.5.1/tests/test_callee.py +21 -0
- pytest_eo-0.5.1/tests/test_capacity.py +88 -0
- pytest_eo-0.5.1/tests/test_comment.py +38 -0
- pytest_eo-0.5.1/tests/test_comments.py +46 -0
- pytest_eo-0.5.1/tests/test_edges.py +23 -0
- pytest_eo-0.5.1/tests/test_encapsulation.py +22 -0
- pytest_eo-0.5.1/tests/test_ending.py +28 -0
- pytest_eo-0.5.1/tests/test_eo_table.py +63 -0
- pytest_eo-0.5.1/tests/test_excludes.py +24 -0
- pytest_eo-0.5.1/tests/test_fields.py +46 -0
- pytest_eo-0.5.1/tests/test_final_assertion.py +29 -0
- pytest_eo-0.5.1/tests/test_flake_check.py +52 -0
- pytest_eo-0.5.1/tests/test_heading.py +44 -0
- pytest_eo-0.5.1/tests/test_inline_comments.py +61 -0
- pytest_eo-0.5.1/tests/test_margin.py +45 -0
- pytest_eo-0.5.1/tests/test_message.py +53 -0
- pytest_eo-0.5.1/tests/test_module_constants.py +38 -0
- pytest_eo-0.5.1/tests/test_mypy_check.py +70 -0
- pytest_eo-0.5.1/tests/test_narrow_suppressions.py +82 -0
- pytest_eo-0.5.1/tests/test_negative_messages.py +37 -0
- pytest_eo-0.5.1/tests/test_output.py +37 -0
- pytest_eo-0.5.1/tests/test_owned.py +31 -0
- pytest_eo-0.5.1/tests/test_place.py +11 -0
- pytest_eo-0.5.1/tests/test_project.py +51 -0
- pytest_eo-0.5.1/tests/test_project_files.py +46 -0
- pytest_eo-0.5.1/tests/test_raising.py +34 -0
- pytest_eo-0.5.1/tests/test_ruff_check.py +74 -0
- pytest_eo-0.5.1/tests/test_ruff_format_check.py +49 -0
- pytest_eo-0.5.1/tests/test_shared.py +49 -0
- pytest_eo-0.5.1/tests/test_span.py +39 -0
- pytest_eo-0.5.1/tests/test_strict.py +29 -0
- pytest_eo-0.5.1/tests/test_style_violations_error.py +23 -0
- pytest_eo-0.5.1/tests/test_suppression_check.py +320 -0
- pytest_eo-0.5.1/tests/test_text.py +29 -0
- pytest_eo-0.5.1/tests/test_tool_failed_error.py +11 -0
- pytest_eo-0.5.1/tests/test_trial.py +39 -0
- pytest_eo-0.5.1/tests/test_verified.py +23 -0
- pytest_eo-0.5.1/tests/test_violation.py +12 -0
- pytest_eo-0.5.1/tests/test_wide_suppressions.py +78 -0
- pytest_eo-0.5.1/third_party/yegor256-prompt-LICENSE.txt +21 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude Code hook: run `pytest --eo` and, when it is red, give the
|
|
3
|
+
# findings back to Claude (exit code 2) so it fixes them before going on.
|
|
4
|
+
# Used for PostToolUse (after editing a Python or config file) and Stop.
|
|
5
|
+
set -u
|
|
6
|
+
|
|
7
|
+
payload=$(cat)
|
|
8
|
+
root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
9
|
+
cd "$root" || exit 0
|
|
10
|
+
python=".venv/bin/python"
|
|
11
|
+
if [ ! -x "$python" ]; then
|
|
12
|
+
echo "pytest --eo gate skipped: no .venv here, run python3 -m venv .venv && .venv/bin/pip install -e ." >&2
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
field() {
|
|
17
|
+
printf '%s' "$payload" | "$python" -I -c '
|
|
18
|
+
import json, sys
|
|
19
|
+
value = json.load(sys.stdin)
|
|
20
|
+
for key in sys.argv[1].split("."):
|
|
21
|
+
value = value.get(key, "") if isinstance(value, dict) else ""
|
|
22
|
+
print(value)
|
|
23
|
+
' "$1"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
event=$(field hook_event_name)
|
|
27
|
+
if [ "$event" = "PostToolUse" ]; then
|
|
28
|
+
case "$(field tool_input.file_path)" in
|
|
29
|
+
*.py|*.toml|*.ini|*.cfg) ;;
|
|
30
|
+
*) exit 0 ;;
|
|
31
|
+
esac
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
report=$("$python" -m pytest --eo -q --tb=short 2>&1)
|
|
35
|
+
if [ $? -eq 0 ]; then
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
findings=$(printf '%s\n' "$report" | tail -n 120)
|
|
40
|
+
summary=$(printf '%s\n' "$report" | tail -n 1)
|
|
41
|
+
|
|
42
|
+
if [ "$event" = "Stop" ] && [ "$(field stop_hook_active)" = "True" ]; then
|
|
43
|
+
printf '{"systemMessage": %s}\n' \
|
|
44
|
+
"$(printf 'pytest --eo is still red after a retry: %s' "$summary" | "$python" -I -c 'import json, sys; print(json.dumps(sys.stdin.read()))')"
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
echo "pytest --eo is red; fix every finding below, never suppress it without a reason"
|
|
50
|
+
printf '%s\n' "$findings"
|
|
51
|
+
echo "$summary"
|
|
52
|
+
} >&2
|
|
53
|
+
exit 2
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PostToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Edit|Write|MultiEdit",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/eo-gate.sh",
|
|
10
|
+
"timeout": 180,
|
|
11
|
+
"statusMessage": "pytest --eo"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"Stop": [
|
|
17
|
+
{
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/eo-gate.sh",
|
|
22
|
+
"timeout": 180,
|
|
23
|
+
"statusMessage": "pytest --eo"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: codecov
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
jobs:
|
|
7
|
+
codecov:
|
|
8
|
+
runs-on: ubuntu-24.04
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
- uses: actions/setup-python@v5
|
|
12
|
+
with:
|
|
13
|
+
python-version: "3.11"
|
|
14
|
+
- run: python3 -m venv .venv
|
|
15
|
+
- run: .venv/bin/pip install -e '.[dev]'
|
|
16
|
+
- run: make coverage
|
|
17
|
+
- uses: codecov/codecov-action@v5
|
|
18
|
+
with:
|
|
19
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
20
|
+
files: coverage.xml
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: pypi
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags: ["*.*.*"]
|
|
5
|
+
jobs:
|
|
6
|
+
pypi:
|
|
7
|
+
runs-on: ubuntu-24.04
|
|
8
|
+
environment: pypi
|
|
9
|
+
permissions:
|
|
10
|
+
id-token: write
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.11"
|
|
16
|
+
- run: grep -q "^version = \"${GITHUB_REF_NAME}\"$" pyproject.toml || { echo "Tag ${GITHUB_REF_NAME} is not the version in pyproject.toml"; exit 1; }
|
|
17
|
+
- run: python3 -m pip install build==1.3.0
|
|
18
|
+
- run: python3 -m build
|
|
19
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
docker:
|
|
3
|
+
image: python:3.11
|
|
4
|
+
merge:
|
|
5
|
+
script: |-
|
|
6
|
+
python3 -m venv .venv
|
|
7
|
+
.venv/bin/pip install -e '.[dev]'
|
|
8
|
+
make check
|
|
9
|
+
release:
|
|
10
|
+
script: |-
|
|
11
|
+
[[ "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Tag ${tag} is not a version like 1.2.3"; exit 1; }
|
|
12
|
+
sed -i -E "s/^version = \"[^\"]*\"$/version = \"${tag}\"/" pyproject.toml
|
|
13
|
+
grep -q "^version = \"${tag}\"$" pyproject.toml || { echo "Cannot set version ${tag} in pyproject.toml"; exit 1; }
|
|
14
|
+
python3 -m venv .venv
|
|
15
|
+
.venv/bin/pip install -e '.[dev]'
|
|
16
|
+
make check
|
|
17
|
+
git commit -am "${tag}"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<!-- Source: https://github.com/yegor256/prompt (README.txt) at commit 83ec21442f7349179a291445d0d369361273c891.
|
|
2
|
+
MIT License, Copyright (c) 2025 Yegor Bugayenko; see third_party/yegor256-prompt-LICENSE.txt.
|
|
3
|
+
Changed for Linux: gsed, gfind, ggrep, gcat are written as sed, find, grep, cat. -->
|
|
4
|
+
|
|
5
|
+
# Be Quality Dogmatic
|
|
6
|
+
|
|
7
|
+
Keep chat replies and your answers easy to comprehend.
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
Use GNU (GNU's Not Unix) tools such as `sed`, `find`, `grep`, and `cat`.
|
|
12
|
+
Commit changes with `courier:commit-changes-to-git` skill.
|
|
13
|
+
Submit pull requests with `courier:submit-a-pull-request` skill.
|
|
14
|
+
File issues with `bugscribe:submit-an-issue` skill.
|
|
15
|
+
|
|
16
|
+
## Discipline
|
|
17
|
+
|
|
18
|
+
Write concise, direct, human prose.
|
|
19
|
+
Speak plain English in full sentences, avoiding jargon, slang, and shorthand.
|
|
20
|
+
Push back on technical mistakes, deferring to users on vision and architecture.
|
|
21
|
+
Stay on scope, refactoring only what tasks require.
|
|
22
|
+
Design top-down, whole before parts, composition before ingredients.
|
|
23
|
+
Never work around design obstacles, stopping to ask me instead.
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
|
|
27
|
+
Pull from Git before changes, push back only with explicit user permission.
|
|
28
|
+
Name each branch after its GitHub issue integer, asking when unsure.
|
|
29
|
+
Always practice TDD (test-driven development), reproducing bugs before fixing.
|
|
30
|
+
Invest in unit tests until bugs reproduce, trusting intuition over debugging.
|
|
31
|
+
Log extensively when problems stay hard.
|
|
32
|
+
|
|
33
|
+
## Changes
|
|
34
|
+
|
|
35
|
+
Tolerate upstream bugs, suggesting issues and waiting for fixes.
|
|
36
|
+
Keep changes focused and minimal.
|
|
37
|
+
Leave puzzles for later, following Puzzle Driven Development.
|
|
38
|
+
Flag smells and refactoring, suggesting issues rather than fixing silently.
|
|
39
|
+
Fix style violations in code, trusting checkers rather than suppressing them.
|
|
40
|
+
|
|
41
|
+
## Code
|
|
42
|
+
|
|
43
|
+
Favor aesthetics over functionality.
|
|
44
|
+
Skip inline comments.
|
|
45
|
+
Prepend classes with plain-text docblocks explaining purpose, not usage.
|
|
46
|
+
Name variables as single nouns.
|
|
47
|
+
Inline single-use expressions, dropping unnecessary local variables.
|
|
48
|
+
Name methods as single nouns or verbs, separating commands from queries.
|
|
49
|
+
Pack method bodies without blank lines.
|
|
50
|
+
|
|
51
|
+
## Style
|
|
52
|
+
|
|
53
|
+
Keep error and log messages to one sentence with context.
|
|
54
|
+
Strip trailing periods from messages.
|
|
55
|
+
Follow "Paired Brackets" and "Monotonic Indentation".
|
|
56
|
+
|
|
57
|
+
## Objects
|
|
58
|
+
|
|
59
|
+
Honor Elegant Objects and SOLID (single, open, Liskov, interface, dependency).
|
|
60
|
+
Follow DDD (domain-driven design) and YAGNI (you arent gonna need it).
|
|
61
|
+
Apply SRP (single-responsibility) and DRY (dont repeat yourself).
|
|
62
|
+
Favor failing immediately over failing silently.
|
|
63
|
+
Allow only assignments in one primary constructor, delegating from secondaries.
|
|
64
|
+
Compose objects instead of inheriting implementation.
|
|
65
|
+
Reject -er names and utility classes.
|
|
66
|
+
Make every class final, holding one to four attributes.
|
|
67
|
+
|
|
68
|
+
## Methods
|
|
69
|
+
|
|
70
|
+
Build only immutable objects, freezing attributes after assignment.
|
|
71
|
+
Drop static methods, public static literals, setters, and getters.
|
|
72
|
+
Declare methods in interfaces first, exposing nothing public outside them.
|
|
73
|
+
Reject null returns and arguments.
|
|
74
|
+
Skip type introspection, casting, and reflection.
|
|
75
|
+
|
|
76
|
+
## Tests
|
|
77
|
+
|
|
78
|
+
Follow "Angry Tests".
|
|
79
|
+
Place one assertion per test, as its last statement.
|
|
80
|
+
Phrase each failure message negatively.
|
|
81
|
+
Keep tests short, verifying one behavior with one statement.
|
|
82
|
+
Map test files one-to-one with feature files, naming tests as sentences.
|
|
83
|
+
Spell "cannot" and "dont" without apostrophes.
|
|
84
|
+
Isolate tests from shared attributes, setUp, tearDown, constants, and cleanup.
|
|
85
|
+
|
|
86
|
+
## Fixtures
|
|
87
|
+
|
|
88
|
+
Provide custom arguments to tested objects, not defaults.
|
|
89
|
+
Skip irrelevant code, side effects, accessors, constructors, and errors.
|
|
90
|
+
Favor fakes and stubs over mocks.
|
|
91
|
+
Use Hamcrest matchers with irregular, random inputs.
|
|
92
|
+
Inline brief fixtures, generating bulky ones at runtime.
|
|
93
|
+
Create fixture objects against duplication, using temporary directories.
|
|
94
|
+
Keep filesystem, sockets, and memory unmocked.
|
|
95
|
+
|
|
96
|
+
## Runtime
|
|
97
|
+
|
|
98
|
+
Disable logging in tests.
|
|
99
|
+
Bound every wait with timeouts.
|
|
100
|
+
Test concurrency, retrying flaky blocks.
|
|
101
|
+
Assume no Internet, using ephemeral ports.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RaulIQ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pytest_eo-0.5.1/Makefile
ADDED
pytest_eo-0.5.1/PKG-INFO
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pytest-eo
|
|
3
|
+
Version: 0.5.1
|
|
4
|
+
Summary: pytest --eo: strict style gate over ruff, mypy --strict and pyeo
|
|
5
|
+
Author: RaulIQ
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE.txt
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Requires-Dist: eo-styleguide==0.2.0
|
|
10
|
+
Requires-Dist: flake8-override==0.1.1
|
|
11
|
+
Requires-Dist: flake8==7.4.1
|
|
12
|
+
Requires-Dist: mypy==2.4.0
|
|
13
|
+
Requires-Dist: pyhamcrest>=2.1
|
|
14
|
+
Requires-Dist: pytest>=8
|
|
15
|
+
Requires-Dist: ruff==0.17.1
|
|
16
|
+
Requires-Dist: typing-extensions>=4.4
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: coverage>=7.10; extra == 'dev'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# pytest-eo
|
|
22
|
+
|
|
23
|
+
[](https://codecov.io/gh/RaulIQ/pytest-eo)
|
|
24
|
+
|
|
25
|
+
`pytest --eo` is a strict quality gate in the spirit of
|
|
26
|
+
[Qulice](https://github.com/yegor256/qulice), built from ready-made tools.
|
|
27
|
+
The plugin runs the tools, reads their reports, and shows each tool as a
|
|
28
|
+
separate test; its own rules cover only tests and suppressions.
|
|
29
|
+
|
|
30
|
+
| Test | Tool |
|
|
31
|
+
|-------------------|------------------------------------------------|
|
|
32
|
+
| `eo::ruff` | `ruff check`, all rules (`select = ["ALL"]`) |
|
|
33
|
+
| `eo::ruff-format` | `ruff format --check` |
|
|
34
|
+
| `eo::mypy` | `mypy --strict` |
|
|
35
|
+
| `eo::flake8` | `flake8` with [pyeo](https://pypi.org/project/eo-styleguide/) (Elegant Objects rules) |
|
|
36
|
+
| `eo::suppressions`| `# noqa` and `# ruff: ignore` comments |
|
|
37
|
+
|
|
38
|
+
The Elegant Objects rules enabled so far, most of them from
|
|
39
|
+
[pyeo](https://github.com/blablatdinov/pyeo) and the rest from its sibling
|
|
40
|
+
packages:
|
|
41
|
+
|
|
42
|
+
| Code | Rule | Package |
|
|
43
|
+
|----------|----------------------------------------------|---------|
|
|
44
|
+
| `FIN100` | every class is `@final` | pyeo |
|
|
45
|
+
| `PEO101` | `__init__` contains only assignments | pyeo |
|
|
46
|
+
| `PEO102` | `@classmethod` contains only a `cls()` call | pyeo |
|
|
47
|
+
| `PEO200` | every class is frozen (immutable) | pyeo |
|
|
48
|
+
| `PEO300` | no class names ending in "-er" | pyeo |
|
|
49
|
+
| `PEO301` | no public attributes | pyeo |
|
|
50
|
+
| `PEO400` | no static methods | pyeo |
|
|
51
|
+
| `PEO500` | no `@property`, a getter in disguise | pyeo |
|
|
52
|
+
| `PEO601` | no methods starting with `get` | pyeo |
|
|
53
|
+
| `PEO602` | no getter methods | pyeo |
|
|
54
|
+
| `PEO700` | no introspection, casting, or reflection | pyeo |
|
|
55
|
+
| `NPM100` | no private functions or methods | [flake8-no-private-methods](https://github.com/blablatdinov/flake8-no-private-methods) |
|
|
56
|
+
| `OVR100` | every public method is an `@override` of an interface method | [flake8-override](https://github.com/blablatdinov/flake8-override) |
|
|
57
|
+
| `FOC100` | one public class per module | [flake8-one-class](https://github.com/blablatdinov/flake8-one-class) |
|
|
58
|
+
|
|
59
|
+
mypy runs with `explicit-override` and `warn_unreachable` on top of
|
|
60
|
+
`--strict`: an `@override` must override a real method, every overriding
|
|
61
|
+
method must say so, and code that can never run is an error.
|
|
62
|
+
Python 3.11 has no `typing.override`; import it from `typing_extensions`.
|
|
63
|
+
|
|
64
|
+
The package also brings a flake8 plugin of its own, with rules in the spirit
|
|
65
|
+
of "Angry Tests" for the files pytest collects (`test_*.py`, `*_test.py`):
|
|
66
|
+
|
|
67
|
+
| Code | Rule |
|
|
68
|
+
|----------|----------------------------------------------------------|
|
|
69
|
+
| `TST100` | a test ends with its only assertion, or with `pytest.raises` |
|
|
70
|
+
| `TST200` | a test module keeps no constants, except `pytest_plugins` |
|
|
71
|
+
| `TST300` | an assertion carries a negative message, such as `"x doesnt y"` |
|
|
72
|
+
|
|
73
|
+
More rules of its own cover every file, tests or not:
|
|
74
|
+
|
|
75
|
+
| Code | Rule |
|
|
76
|
+
|----------|----------------------------------------------------------|
|
|
77
|
+
| `ENC100` | a class encapsulates one to four attributes; a `Protocol` is exempt |
|
|
78
|
+
| `BOD100` | a function body holds no blank lines, except around a nested `def` or `class` |
|
|
79
|
+
| `BOD200` | a function body holds no comments, except suppressions |
|
|
80
|
+
|
|
81
|
+
As in Qulice, which rejects plain JUnit assertions, a test asserts through a
|
|
82
|
+
Hamcrest matcher, never through a plain `assert`; ruff's `S101` stays on in
|
|
83
|
+
tests too. PyHamcrest comes with the package:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from hamcrest import assert_that, equal_to
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_checks_the_whole_directory_by_default(tmp_path: Path) -> None:
|
|
90
|
+
assert_that(
|
|
91
|
+
Project(EoTable(tmp_path / "pyproject.toml"), tmp_path).paths(),
|
|
92
|
+
equal_to((".",)),
|
|
93
|
+
"Project doesnt check the whole directory by default",
|
|
94
|
+
)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
A rule may be suppressed on one line only, with a reason:
|
|
98
|
+
`# noqa: PEO200 - pytest.Item is mutable by design`, or
|
|
99
|
+
`# ruff: ignore[unused-import] - re-exported for plugins`.
|
|
100
|
+
`eo::suppressions` enforces it:
|
|
101
|
+
|
|
102
|
+
| Code | Rule |
|
|
103
|
+
|----------|----------------------------------------------------------|
|
|
104
|
+
| `SUP001` | a suppression gives a reason after ` - ` |
|
|
105
|
+
| `SUP002` | a suppression never names `TID251` (`banned-api`), the ban on mocks |
|
|
106
|
+
| `SUP003` | nothing suppresses past one line: no `# ruff: noqa`, `file-ignore` or `disable`, no `# flake8: noqa`, no `# mypy:` comment, no `# type: ignore` above the code |
|
|
107
|
+
|
|
108
|
+
ruff already rejects a bare `# noqa` (`PGH004`), a bare `# type: ignore`
|
|
109
|
+
(`PGH003`) and an unused `# noqa` for its own codes (`RUF100`).
|
|
110
|
+
|
|
111
|
+
The configuration is bundled with the package (`src/pytest_eo/config/`)
|
|
112
|
+
and cannot be overridden by a project. A project may only say what to
|
|
113
|
+
check and what to skip:
|
|
114
|
+
|
|
115
|
+
```toml
|
|
116
|
+
[tool.eo]
|
|
117
|
+
paths = ["src", "tests"] # default: src and tests, if they exist
|
|
118
|
+
exclude = ["generated/"]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Usage
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
pip install 'pytest-eo==0.5.1'
|
|
125
|
+
pytest --eo
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Without `--eo` the plugin does nothing. Pin an exact version: every version
|
|
129
|
+
of the rules is a version of the package, so pip installs new rules only
|
|
130
|
+
when the version in the requirement changes.
|
|
131
|
+
|
|
132
|
+
## Claude Code hooks
|
|
133
|
+
|
|
134
|
+
This repository runs the gate on every Claude Code session that works in it:
|
|
135
|
+
after Claude edits a `.py`, `.toml`, `.ini` or `.cfg` file, and again before
|
|
136
|
+
it finishes. When the gate is red, the findings go back to Claude and it has
|
|
137
|
+
to fix them. If the gate is still red after one retry, Claude may stop, and
|
|
138
|
+
you see a warning instead.
|
|
139
|
+
|
|
140
|
+
To get the same in another project:
|
|
141
|
+
|
|
142
|
+
1. Install pytest-eo into the project's `.venv`.
|
|
143
|
+
2. Copy `.claude/hooks/eo-gate.sh` into the same place in that project and
|
|
144
|
+
make it executable.
|
|
145
|
+
3. Copy the `hooks` block of `.claude/settings.json` into that project's
|
|
146
|
+
`.claude/settings.json`, merging it with what is already there.
|
|
147
|
+
4. Commit both files, so everyone who opens the project with Claude Code gets
|
|
148
|
+
the gate, and run `/hooks` once in an open session to load them.
|
|
149
|
+
|
|
150
|
+
## Development
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
python3 -m venv .venv
|
|
154
|
+
.venv/bin/pip install -e '.[dev]'
|
|
155
|
+
make check
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`make check` runs the tests and `pytest --eo` on the plugin itself; Rultor
|
|
159
|
+
merges on it. `make coverage` measures lines and branches, counting the
|
|
160
|
+
pytest subprocesses of the end-to-end tests too, and writes `coverage.xml`,
|
|
161
|
+
which GitHub Actions sends to Codecov on every push and pull request.
|
|
162
|
+
|
|
163
|
+
`pyproject.toml` on `main` always says `version = "0.0.0"`; a real version
|
|
164
|
+
exists only in a tag. To release, comment in any issue, with the version in
|
|
165
|
+
backticks, since Rultor reads nothing else:
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
@rultor release, tag is `0.3.0`
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Rultor writes the version into `pyproject.toml`, runs
|
|
172
|
+
`make check`, commits the result on a side branch and pushes only the tag.
|
|
173
|
+
GitHub Actions builds that tag and uploads it to PyPI through Trusted
|
|
174
|
+
Publishing, so no token is stored anywhere.
|
|
175
|
+
Every change to the rules or the bundled configuration ships in a release:
|
|
176
|
+
raise the minor number for a new or stricter rule and the patch number for a
|
|
177
|
+
fix.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# pytest-eo
|
|
2
|
+
|
|
3
|
+
[](https://codecov.io/gh/RaulIQ/pytest-eo)
|
|
4
|
+
|
|
5
|
+
`pytest --eo` is a strict quality gate in the spirit of
|
|
6
|
+
[Qulice](https://github.com/yegor256/qulice), built from ready-made tools.
|
|
7
|
+
The plugin runs the tools, reads their reports, and shows each tool as a
|
|
8
|
+
separate test; its own rules cover only tests and suppressions.
|
|
9
|
+
|
|
10
|
+
| Test | Tool |
|
|
11
|
+
|-------------------|------------------------------------------------|
|
|
12
|
+
| `eo::ruff` | `ruff check`, all rules (`select = ["ALL"]`) |
|
|
13
|
+
| `eo::ruff-format` | `ruff format --check` |
|
|
14
|
+
| `eo::mypy` | `mypy --strict` |
|
|
15
|
+
| `eo::flake8` | `flake8` with [pyeo](https://pypi.org/project/eo-styleguide/) (Elegant Objects rules) |
|
|
16
|
+
| `eo::suppressions`| `# noqa` and `# ruff: ignore` comments |
|
|
17
|
+
|
|
18
|
+
The Elegant Objects rules enabled so far, most of them from
|
|
19
|
+
[pyeo](https://github.com/blablatdinov/pyeo) and the rest from its sibling
|
|
20
|
+
packages:
|
|
21
|
+
|
|
22
|
+
| Code | Rule | Package |
|
|
23
|
+
|----------|----------------------------------------------|---------|
|
|
24
|
+
| `FIN100` | every class is `@final` | pyeo |
|
|
25
|
+
| `PEO101` | `__init__` contains only assignments | pyeo |
|
|
26
|
+
| `PEO102` | `@classmethod` contains only a `cls()` call | pyeo |
|
|
27
|
+
| `PEO200` | every class is frozen (immutable) | pyeo |
|
|
28
|
+
| `PEO300` | no class names ending in "-er" | pyeo |
|
|
29
|
+
| `PEO301` | no public attributes | pyeo |
|
|
30
|
+
| `PEO400` | no static methods | pyeo |
|
|
31
|
+
| `PEO500` | no `@property`, a getter in disguise | pyeo |
|
|
32
|
+
| `PEO601` | no methods starting with `get` | pyeo |
|
|
33
|
+
| `PEO602` | no getter methods | pyeo |
|
|
34
|
+
| `PEO700` | no introspection, casting, or reflection | pyeo |
|
|
35
|
+
| `NPM100` | no private functions or methods | [flake8-no-private-methods](https://github.com/blablatdinov/flake8-no-private-methods) |
|
|
36
|
+
| `OVR100` | every public method is an `@override` of an interface method | [flake8-override](https://github.com/blablatdinov/flake8-override) |
|
|
37
|
+
| `FOC100` | one public class per module | [flake8-one-class](https://github.com/blablatdinov/flake8-one-class) |
|
|
38
|
+
|
|
39
|
+
mypy runs with `explicit-override` and `warn_unreachable` on top of
|
|
40
|
+
`--strict`: an `@override` must override a real method, every overriding
|
|
41
|
+
method must say so, and code that can never run is an error.
|
|
42
|
+
Python 3.11 has no `typing.override`; import it from `typing_extensions`.
|
|
43
|
+
|
|
44
|
+
The package also brings a flake8 plugin of its own, with rules in the spirit
|
|
45
|
+
of "Angry Tests" for the files pytest collects (`test_*.py`, `*_test.py`):
|
|
46
|
+
|
|
47
|
+
| Code | Rule |
|
|
48
|
+
|----------|----------------------------------------------------------|
|
|
49
|
+
| `TST100` | a test ends with its only assertion, or with `pytest.raises` |
|
|
50
|
+
| `TST200` | a test module keeps no constants, except `pytest_plugins` |
|
|
51
|
+
| `TST300` | an assertion carries a negative message, such as `"x doesnt y"` |
|
|
52
|
+
|
|
53
|
+
More rules of its own cover every file, tests or not:
|
|
54
|
+
|
|
55
|
+
| Code | Rule |
|
|
56
|
+
|----------|----------------------------------------------------------|
|
|
57
|
+
| `ENC100` | a class encapsulates one to four attributes; a `Protocol` is exempt |
|
|
58
|
+
| `BOD100` | a function body holds no blank lines, except around a nested `def` or `class` |
|
|
59
|
+
| `BOD200` | a function body holds no comments, except suppressions |
|
|
60
|
+
|
|
61
|
+
As in Qulice, which rejects plain JUnit assertions, a test asserts through a
|
|
62
|
+
Hamcrest matcher, never through a plain `assert`; ruff's `S101` stays on in
|
|
63
|
+
tests too. PyHamcrest comes with the package:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from hamcrest import assert_that, equal_to
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_checks_the_whole_directory_by_default(tmp_path: Path) -> None:
|
|
70
|
+
assert_that(
|
|
71
|
+
Project(EoTable(tmp_path / "pyproject.toml"), tmp_path).paths(),
|
|
72
|
+
equal_to((".",)),
|
|
73
|
+
"Project doesnt check the whole directory by default",
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A rule may be suppressed on one line only, with a reason:
|
|
78
|
+
`# noqa: PEO200 - pytest.Item is mutable by design`, or
|
|
79
|
+
`# ruff: ignore[unused-import] - re-exported for plugins`.
|
|
80
|
+
`eo::suppressions` enforces it:
|
|
81
|
+
|
|
82
|
+
| Code | Rule |
|
|
83
|
+
|----------|----------------------------------------------------------|
|
|
84
|
+
| `SUP001` | a suppression gives a reason after ` - ` |
|
|
85
|
+
| `SUP002` | a suppression never names `TID251` (`banned-api`), the ban on mocks |
|
|
86
|
+
| `SUP003` | nothing suppresses past one line: no `# ruff: noqa`, `file-ignore` or `disable`, no `# flake8: noqa`, no `# mypy:` comment, no `# type: ignore` above the code |
|
|
87
|
+
|
|
88
|
+
ruff already rejects a bare `# noqa` (`PGH004`), a bare `# type: ignore`
|
|
89
|
+
(`PGH003`) and an unused `# noqa` for its own codes (`RUF100`).
|
|
90
|
+
|
|
91
|
+
The configuration is bundled with the package (`src/pytest_eo/config/`)
|
|
92
|
+
and cannot be overridden by a project. A project may only say what to
|
|
93
|
+
check and what to skip:
|
|
94
|
+
|
|
95
|
+
```toml
|
|
96
|
+
[tool.eo]
|
|
97
|
+
paths = ["src", "tests"] # default: src and tests, if they exist
|
|
98
|
+
exclude = ["generated/"]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Usage
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
pip install 'pytest-eo==0.5.1'
|
|
105
|
+
pytest --eo
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Without `--eo` the plugin does nothing. Pin an exact version: every version
|
|
109
|
+
of the rules is a version of the package, so pip installs new rules only
|
|
110
|
+
when the version in the requirement changes.
|
|
111
|
+
|
|
112
|
+
## Claude Code hooks
|
|
113
|
+
|
|
114
|
+
This repository runs the gate on every Claude Code session that works in it:
|
|
115
|
+
after Claude edits a `.py`, `.toml`, `.ini` or `.cfg` file, and again before
|
|
116
|
+
it finishes. When the gate is red, the findings go back to Claude and it has
|
|
117
|
+
to fix them. If the gate is still red after one retry, Claude may stop, and
|
|
118
|
+
you see a warning instead.
|
|
119
|
+
|
|
120
|
+
To get the same in another project:
|
|
121
|
+
|
|
122
|
+
1. Install pytest-eo into the project's `.venv`.
|
|
123
|
+
2. Copy `.claude/hooks/eo-gate.sh` into the same place in that project and
|
|
124
|
+
make it executable.
|
|
125
|
+
3. Copy the `hooks` block of `.claude/settings.json` into that project's
|
|
126
|
+
`.claude/settings.json`, merging it with what is already there.
|
|
127
|
+
4. Commit both files, so everyone who opens the project with Claude Code gets
|
|
128
|
+
the gate, and run `/hooks` once in an open session to load them.
|
|
129
|
+
|
|
130
|
+
## Development
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
python3 -m venv .venv
|
|
134
|
+
.venv/bin/pip install -e '.[dev]'
|
|
135
|
+
make check
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`make check` runs the tests and `pytest --eo` on the plugin itself; Rultor
|
|
139
|
+
merges on it. `make coverage` measures lines and branches, counting the
|
|
140
|
+
pytest subprocesses of the end-to-end tests too, and writes `coverage.xml`,
|
|
141
|
+
which GitHub Actions sends to Codecov on every push and pull request.
|
|
142
|
+
|
|
143
|
+
`pyproject.toml` on `main` always says `version = "0.0.0"`; a real version
|
|
144
|
+
exists only in a tag. To release, comment in any issue, with the version in
|
|
145
|
+
backticks, since Rultor reads nothing else:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
@rultor release, tag is `0.3.0`
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Rultor writes the version into `pyproject.toml`, runs
|
|
152
|
+
`make check`, commits the result on a side branch and pushes only the tag.
|
|
153
|
+
GitHub Actions builds that tag and uploads it to PyPI through Trusted
|
|
154
|
+
Publishing, so no token is stored anywhere.
|
|
155
|
+
Every change to the rules or the bundled configuration ships in a release:
|
|
156
|
+
raise the minor number for a new or stricter rule and the patch number for a
|
|
157
|
+
fix.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pytest-eo"
|
|
7
|
+
version = "0.5.1"
|
|
8
|
+
description = "pytest --eo: strict style gate over ruff, mypy --strict and pyeo"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE.txt"]
|
|
12
|
+
authors = [{ name = "RaulIQ" }]
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
dependencies = [
|
|
15
|
+
"pytest>=8",
|
|
16
|
+
"ruff==0.17.1",
|
|
17
|
+
"mypy==2.4.0",
|
|
18
|
+
"flake8==7.4.1",
|
|
19
|
+
"eo-styleguide==0.2.0",
|
|
20
|
+
"flake8-override==0.1.1",
|
|
21
|
+
"typing_extensions>=4.4",
|
|
22
|
+
"PyHamcrest>=2.1",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = ["coverage>=7.10"]
|
|
27
|
+
|
|
28
|
+
[project.entry-points.pytest11]
|
|
29
|
+
eo = "pytest_eo.plugin"
|
|
30
|
+
|
|
31
|
+
[project.entry-points."flake8.extension"]
|
|
32
|
+
TST = "pytest_eo.angry_tests:AngryTests"
|
|
33
|
+
ENC = "pytest_eo.encapsulation:Encapsulation"
|
|
34
|
+
BOD = "pytest_eo.bodies:Bodies"
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["src/pytest_eo"]
|
|
38
|
+
|
|
39
|
+
[tool.eo]
|
|
40
|
+
paths = ["src", "tests"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
pythonpath = ["src"]
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
|
|
46
|
+
[tool.coverage.run]
|
|
47
|
+
branch = true
|
|
48
|
+
source = ["pytest_eo"]
|
|
49
|
+
patch = ["subprocess"]
|
|
50
|
+
parallel = true
|
|
51
|
+
|
|
52
|
+
[tool.coverage.report]
|
|
53
|
+
show_missing = true
|
|
54
|
+
skip_covered = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""pytest --eo: a strict style gate that aggregates ready-made tools."""
|