smellcheck 0.2.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.
- smellcheck-0.2.1/.claude-plugin/marketplace.json +32 -0
- smellcheck-0.2.1/.github/CODEOWNERS +5 -0
- smellcheck-0.2.1/.github/FUNDING.yml +1 -0
- smellcheck-0.2.1/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
- smellcheck-0.2.1/.github/ISSUE_TEMPLATE/feature_request.md +40 -0
- smellcheck-0.2.1/.github/PULL_REQUEST_TEMPLATE.md +41 -0
- smellcheck-0.2.1/.github/workflows/ci.yml +37 -0
- smellcheck-0.2.1/.github/workflows/lint-commits.yml +14 -0
- smellcheck-0.2.1/.github/workflows/release-please.yml +80 -0
- smellcheck-0.2.1/.gitignore +16 -0
- smellcheck-0.2.1/.pre-commit-config.yaml +17 -0
- smellcheck-0.2.1/.pre-commit-hooks.yaml +7 -0
- smellcheck-0.2.1/.release-please-manifest.json +3 -0
- smellcheck-0.2.1/CHANGELOG.md +61 -0
- smellcheck-0.2.1/CODE_OF_CONDUCT.md +50 -0
- smellcheck-0.2.1/CONTRIBUTING.md +114 -0
- smellcheck-0.2.1/LICENSE +21 -0
- smellcheck-0.2.1/PKG-INFO +299 -0
- smellcheck-0.2.1/README.md +274 -0
- smellcheck-0.2.1/SECURITY.md +41 -0
- smellcheck-0.2.1/action.yml +45 -0
- smellcheck-0.2.1/plugins/python-refactoring/.claude-plugin/plugin.json +5 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/SKILL.md +149 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/architecture.md +172 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/control.md +266 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/functions.md +294 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/hygiene.md +203 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/idioms.md +203 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/metrics.md +432 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/state.md +152 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/references/types.md +280 -0
- smellcheck-0.2.1/plugins/python-refactoring/skills/python-refactoring/scripts/detect_smells.py +32 -0
- smellcheck-0.2.1/pyproject.toml +43 -0
- smellcheck-0.2.1/release-please-config.json +31 -0
- smellcheck-0.2.1/src/smellcheck/__init__.py +14 -0
- smellcheck-0.2.1/src/smellcheck/__main__.py +6 -0
- smellcheck-0.2.1/src/smellcheck/detector.py +2246 -0
- smellcheck-0.2.1/src/smellcheck/py.typed +0 -0
- smellcheck-0.2.1/tests/__init__.py +0 -0
- smellcheck-0.2.1/tests/test_detector.py +235 -0
- smellcheck-0.2.1/tests/test_regressions.py +175 -0
- smellcheck-0.2.1/tests/test_version_parity.py +112 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "smellcheck",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Cheick Berthe",
|
|
5
|
+
"email": "cheick@example.com"
|
|
6
|
+
},
|
|
7
|
+
"metadata": {
|
|
8
|
+
"description": "Python code smell detector with 82+ patterns mapped to a refactoring catalog. AST-based, zero dependencies, stdlib only.",
|
|
9
|
+
"version": "0.2.1"
|
|
10
|
+
},
|
|
11
|
+
"plugins": [
|
|
12
|
+
{
|
|
13
|
+
"name": "python-refactoring",
|
|
14
|
+
"source": "./plugins/python-refactoring",
|
|
15
|
+
"description": "Python refactoring catalog with 82+ patterns covering immutability, class design, control flow, architecture, OO metrics, and Python idioms. Analyze code for smells and apply numbered refactoring patterns.",
|
|
16
|
+
"version": "0.2.1",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Cheick Berthe"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"keywords": [
|
|
22
|
+
"python",
|
|
23
|
+
"refactoring",
|
|
24
|
+
"code-smells",
|
|
25
|
+
"ast",
|
|
26
|
+
"static-analysis",
|
|
27
|
+
"code-quality"
|
|
28
|
+
],
|
|
29
|
+
"category": "development"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
github: cheickmec
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Report a false positive, false negative, crash, or incorrect output
|
|
4
|
+
title: ''
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Describe the bug**
|
|
10
|
+
A clear description of what went wrong.
|
|
11
|
+
|
|
12
|
+
**Python code that triggers the issue**
|
|
13
|
+
```python
|
|
14
|
+
# Paste the minimal code that reproduces the problem
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Expected behavior**
|
|
18
|
+
What should smellcheck report (or not report)?
|
|
19
|
+
|
|
20
|
+
**Actual behavior**
|
|
21
|
+
What does smellcheck actually output? Paste the relevant output:
|
|
22
|
+
```
|
|
23
|
+
# smellcheck output here
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Environment**
|
|
27
|
+
- smellcheck version: (`smellcheck --version`)
|
|
28
|
+
- Python version: (`python --version`)
|
|
29
|
+
- OS:
|
|
30
|
+
- Install method: pip / GitHub Action / pre-commit / Agent Skills
|
|
31
|
+
|
|
32
|
+
**Additional context**
|
|
33
|
+
- Is this a false positive (reported something that isn't a smell)?
|
|
34
|
+
- Is this a false negative (missed a smell that should be detected)?
|
|
35
|
+
- Is this a crash or unexpected error?
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest a new smell check, CLI feature, or improvement
|
|
4
|
+
title: ''
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**What kind of feature?**
|
|
10
|
+
- [ ] New smell check
|
|
11
|
+
- [ ] New CLI option
|
|
12
|
+
- [ ] New output format
|
|
13
|
+
- [ ] Improvement to existing check
|
|
14
|
+
- [ ] Other
|
|
15
|
+
|
|
16
|
+
**For new smell checks: describe the code pattern**
|
|
17
|
+
|
|
18
|
+
What code should be detected?
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
# Before (smelly code)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
What does the refactored version look like?
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
# After (clean code)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Why is this a smell?**
|
|
31
|
+
Explain why this pattern is problematic. References to Fowler, Contieri, or other
|
|
32
|
+
refactoring literature are a plus.
|
|
33
|
+
|
|
34
|
+
**Suggested severity**
|
|
35
|
+
- [ ] error (always wrong, like mutable default args)
|
|
36
|
+
- [ ] warning (usually wrong, should be reviewed)
|
|
37
|
+
- [ ] info (style suggestion, may be intentional)
|
|
38
|
+
|
|
39
|
+
**Additional context**
|
|
40
|
+
Any other context, related patterns, or edge cases to consider.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
## What does this PR do?
|
|
2
|
+
|
|
3
|
+
Brief description of the change.
|
|
4
|
+
|
|
5
|
+
## Type of change
|
|
6
|
+
|
|
7
|
+
- [ ] New smell check (add pattern number, e.g. `#071`)
|
|
8
|
+
- [ ] Bug fix (false positive, false negative, crash)
|
|
9
|
+
- [ ] CLI / output improvement
|
|
10
|
+
- [ ] Documentation
|
|
11
|
+
- [ ] CI / tooling
|
|
12
|
+
- [ ] Other
|
|
13
|
+
|
|
14
|
+
## Checklist
|
|
15
|
+
|
|
16
|
+
- [ ] `pytest tests/ -v` passes (all tests, including version parity)
|
|
17
|
+
- [ ] New check has a test in `tests/test_detector.py`
|
|
18
|
+
- [ ] `smellcheck src/smellcheck/` self-check runs clean (or new findings are intentional)
|
|
19
|
+
- [ ] No dependencies added (stdlib only)
|
|
20
|
+
- [ ] Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/)
|
|
21
|
+
|
|
22
|
+
## For new smell checks
|
|
23
|
+
|
|
24
|
+
- **Pattern number**: `#___`
|
|
25
|
+
- **Severity**: error / warning / info
|
|
26
|
+
- **Category**: functions / classes / complexity / imports / ...
|
|
27
|
+
- **Before** (smelly code):
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
# paste example
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **After** (clean code):
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
# paste example
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Additional context
|
|
40
|
+
|
|
41
|
+
Any other context, edge cases, or related issues.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ['3.10', '3.11', '3.12', '3.13']
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Install package and test deps
|
|
21
|
+
run: pip install -e . && pip install pytest
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: pytest tests/ -v
|
|
24
|
+
- name: Self-check
|
|
25
|
+
run: smellcheck src/smellcheck/ --min-severity warning
|
|
26
|
+
|
|
27
|
+
version-parity:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: '3.12'
|
|
34
|
+
- name: Install package and test deps
|
|
35
|
+
run: pip install -e . && pip install pytest
|
|
36
|
+
- name: Verify version parity
|
|
37
|
+
run: pytest tests/test_version_parity.py -v
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: Lint Commits
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint-commits:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: webiny/action-conventional-commits@v1.3.0
|
|
13
|
+
with:
|
|
14
|
+
allowed-commit-types: "feat,fix,refactor,test,docs,chore,ci,style,perf,build,revert"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: Release Please
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release-please:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
outputs:
|
|
16
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
17
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
18
|
+
steps:
|
|
19
|
+
- id: release
|
|
20
|
+
uses: googleapis/release-please-action@v4
|
|
21
|
+
with:
|
|
22
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
23
|
+
|
|
24
|
+
publish-pypi:
|
|
25
|
+
needs: release-please
|
|
26
|
+
if: needs.release-please.outputs.release_created == 'true'
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
permissions:
|
|
29
|
+
id-token: write
|
|
30
|
+
environment:
|
|
31
|
+
name: pypi
|
|
32
|
+
url: https://pypi.org/p/smellcheck
|
|
33
|
+
env:
|
|
34
|
+
TAG_NAME: ${{ needs.release-please.outputs.tag_name }}
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: '3.12'
|
|
40
|
+
|
|
41
|
+
# Guard 1: tag must match pyproject.toml version
|
|
42
|
+
- name: Verify tag matches package version
|
|
43
|
+
run: |
|
|
44
|
+
TAG_VERSION="${TAG_NAME#v}"
|
|
45
|
+
PKG_VERSION=$(python3 -c "
|
|
46
|
+
import re, pathlib
|
|
47
|
+
text = pathlib.Path('pyproject.toml').read_text()
|
|
48
|
+
m = re.search(r'^version\s*=\s*\"([^\"]+)\"', text, re.MULTILINE)
|
|
49
|
+
print(m.group(1))
|
|
50
|
+
")
|
|
51
|
+
echo "Tag version: $TAG_VERSION"
|
|
52
|
+
echo "Package version: $PKG_VERSION"
|
|
53
|
+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
|
|
54
|
+
echo "::error::Version mismatch: tag=$TAG_VERSION != pyproject.toml=$PKG_VERSION"
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
- name: Install build tools
|
|
59
|
+
run: pip install build
|
|
60
|
+
- name: Build package
|
|
61
|
+
run: python -m build
|
|
62
|
+
|
|
63
|
+
# Guard 2: smoke-test the built wheel before publishing
|
|
64
|
+
- name: Smoke-test wheel
|
|
65
|
+
run: |
|
|
66
|
+
TAG_VERSION="${TAG_NAME#v}"
|
|
67
|
+
pip install dist/smellcheck-*.whl
|
|
68
|
+
INSTALLED=$(smellcheck --version | awk '{print $2}')
|
|
69
|
+
echo "Installed version: $INSTALLED"
|
|
70
|
+
echo "Expected version: $TAG_VERSION"
|
|
71
|
+
if [ "$INSTALLED" != "$TAG_VERSION" ]; then
|
|
72
|
+
echo "::error::Installed version $INSTALLED does not match tag $TAG_VERSION"
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
echo "Smoke test: smellcheck --version OK"
|
|
76
|
+
smellcheck --help > /dev/null
|
|
77
|
+
echo "Smoke test: smellcheck --help OK"
|
|
78
|
+
|
|
79
|
+
- name: Publish to PyPI
|
|
80
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: local
|
|
3
|
+
hooks:
|
|
4
|
+
- id: no-commit-to-main
|
|
5
|
+
name: no-commit-to-main
|
|
6
|
+
entry: bash -c 'branch=$(git rev-parse --abbrev-ref HEAD); if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then echo "Direct commits to $branch are not allowed. Use a feature branch." >&2; exit 1; fi'
|
|
7
|
+
language: system
|
|
8
|
+
pass_filenames: false
|
|
9
|
+
always_run: true
|
|
10
|
+
stages: [pre-commit]
|
|
11
|
+
|
|
12
|
+
- id: version-parity
|
|
13
|
+
name: version-parity
|
|
14
|
+
entry: python -m pytest tests/test_version_parity.py -q --no-header --tb=short
|
|
15
|
+
language: system
|
|
16
|
+
pass_filenames: false
|
|
17
|
+
files: '(pyproject\.toml|\.release-please-manifest\.json|marketplace\.json)$'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.1](https://github.com/cheickmec/smellcheck/compare/v0.2.0...v0.2.1) (2026-02-11)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* initial release -- 82-pattern Python refactoring skill with AST detector ([e96f750](https://github.com/cheickmec/smellcheck/commit/e96f7507e6739bb3463dca93e38ed04bc6cf1597))
|
|
14
|
+
* restructure for PyPI, GitHub Action, pre-commit, and Agent Skills distribution ([e1bf50b](https://github.com/cheickmec/smellcheck/commit/e1bf50b2f4f1b8191903a84c7d262a0c312f163d))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* address 9 code review findings (P1-P3) ([39a62c0](https://github.com/cheickmec/smellcheck/commit/39a62c0b2ce9fb0ce00f54e8dc4e4a97fddfc792))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Documentation
|
|
23
|
+
|
|
24
|
+
* add community files for OSS maturity ([8b7fc5f](https://github.com/cheickmec/smellcheck/commit/8b7fc5fcc81f27c58469f220fd33483a116ab07c))
|
|
25
|
+
* update README for cross-platform compatibility and SEO ([aecccba](https://github.com/cheickmec/smellcheck/commit/aecccba107b831b7206c543e1a1b34641a0c6903))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Miscellaneous
|
|
29
|
+
|
|
30
|
+
* add CHANGELOG.md, fix version to 0.1.1 ([82aae13](https://github.com/cheickmec/smellcheck/commit/82aae130e0830ce38e4f0f1d46c26c095619ee33))
|
|
31
|
+
* add pre-commit hook to prevent direct commits to main ([b23e812](https://github.com/cheickmec/smellcheck/commit/b23e812f123f551ad181d1262959e47ff503d1c0))
|
|
32
|
+
* rename project from pysmells to smellcheck ([71589cc](https://github.com/cheickmec/smellcheck/commit/71589cc6efa36085d7c2bcc0e4d39c11edad1155))
|
|
33
|
+
|
|
34
|
+
## [0.1.1] - 2026-02-11
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- Import analysis now captures full dotted paths, fixing under-reporting of #CYC, #UDE, #FIO on package-based repos
|
|
38
|
+
- RFC metric counts distinct external method calls (not class names), correctly measuring response set size
|
|
39
|
+
- elif chains no longer produce duplicate #014 and #068 findings
|
|
40
|
+
- #058 no longer false-positives on `ExitStack.enter_context(open(...))`
|
|
41
|
+
- Single-file scans now run per-class OO metrics (LCOM, CBO, RFC, MID)
|
|
42
|
+
- CLI docs updated with correct script path from repo root
|
|
43
|
+
- Corrected detection counts: 40 per-file + 10 cross-file + 5 OO metrics = 55
|
|
44
|
+
- Added missing #007 (extract class) to per-file detection table
|
|
45
|
+
- Fixed SKILL.md smell-to-file mappings (003, 005, 006, 008, MID pointed to wrong reference files)
|
|
46
|
+
- `--min-severity` now validates input and rejects invalid values with clear error message
|
|
47
|
+
|
|
48
|
+
## [0.1.0] - 2026-02-10
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
- AST-based smell detector with 55 automated checks (stdlib-only, zero dependencies)
|
|
52
|
+
- 40 per-file patterns (#001-#070, #CC)
|
|
53
|
+
- 10 cross-file patterns (#013, #CYC, #GOD, #FE, #SHO, #DIT, #WHI, #INT, #SPG, #UDE)
|
|
54
|
+
- 5 OO metrics (#LCOM, #CBO, #FIO, #RFC, #MID)
|
|
55
|
+
- 82 refactoring patterns with before/after examples across 8 reference files
|
|
56
|
+
- Agent Skills plugin structure for Claude Code, Codex CLI, Cursor, Copilot, Gemini CLI, Roo Code
|
|
57
|
+
- JSON output mode for CI/CD integration
|
|
58
|
+
- Severity filtering (`--min-severity info|warning|error`)
|
|
59
|
+
|
|
60
|
+
[0.1.1]: https://github.com/cheickmec/smellcheck/compare/v0.1.0...v0.1.1
|
|
61
|
+
[0.1.0]: https://github.com/cheickmec/smellcheck/releases/tag/v0.1.0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and maintainers pledge to make participation in our
|
|
6
|
+
project and our community a harassment-free experience for everyone, regardless of
|
|
7
|
+
age, body size, disability, ethnicity, sex characteristics, gender identity and
|
|
8
|
+
expression, level of experience, education, socio-economic status, nationality,
|
|
9
|
+
personal appearance, race, religion, or sexual identity and orientation.
|
|
10
|
+
|
|
11
|
+
## Our Standards
|
|
12
|
+
|
|
13
|
+
Examples of behavior that contributes to a positive environment:
|
|
14
|
+
|
|
15
|
+
- Using welcoming and inclusive language
|
|
16
|
+
- Being respectful of differing viewpoints and experiences
|
|
17
|
+
- Gracefully accepting constructive criticism
|
|
18
|
+
- Focusing on what is best for the community
|
|
19
|
+
- Showing empathy towards other community members
|
|
20
|
+
|
|
21
|
+
Examples of unacceptable behavior:
|
|
22
|
+
|
|
23
|
+
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
|
24
|
+
- Trolling, insulting/derogatory comments, and personal or political attacks
|
|
25
|
+
- Public or private harassment
|
|
26
|
+
- Publishing others' private information without explicit permission
|
|
27
|
+
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
28
|
+
|
|
29
|
+
## Enforcement Responsibilities
|
|
30
|
+
|
|
31
|
+
Project maintainers are responsible for clarifying and enforcing our standards of
|
|
32
|
+
acceptable behavior and will take appropriate and fair corrective action in response
|
|
33
|
+
to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
|
34
|
+
|
|
35
|
+
## Scope
|
|
36
|
+
|
|
37
|
+
This Code of Conduct applies within all project spaces, including issues, pull
|
|
38
|
+
requests, discussions, and any other communication channel associated with the project.
|
|
39
|
+
|
|
40
|
+
## Enforcement
|
|
41
|
+
|
|
42
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported
|
|
43
|
+
to the project maintainer at **cheick@example.com**. All complaints will be reviewed
|
|
44
|
+
and investigated promptly and fairly.
|
|
45
|
+
|
|
46
|
+
## Attribution
|
|
47
|
+
|
|
48
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
|
|
49
|
+
version 2.1, available at
|
|
50
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Contributing to smellcheck
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing. smellcheck is a focused tool -- a zero-dependency Python code smell detector built entirely on stdlib. Contributions that preserve that simplicity are welcome.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/cheickmec/smellcheck.git
|
|
9
|
+
cd smellcheck
|
|
10
|
+
python -m venv .venv && source .venv/bin/activate
|
|
11
|
+
pip install -e .
|
|
12
|
+
pip install pytest pre-commit
|
|
13
|
+
pre-commit install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Running tests
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pytest tests/ -v
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Self-check (smellcheck analyzing itself):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
smellcheck src/smellcheck/ --min-severity warning
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Project structure
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/smellcheck/
|
|
32
|
+
detector.py # The entire detector: AST visitor, cross-file analysis, CLI
|
|
33
|
+
__init__.py # Public API + version
|
|
34
|
+
__main__.py # python -m smellcheck
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The detector is intentionally a single file. The code is tightly coupled (AST visitor + cross-file passes + output formatting) and works well as one unit. Don't split it into modules without a strong reason.
|
|
38
|
+
|
|
39
|
+
## Adding a new smell check
|
|
40
|
+
|
|
41
|
+
### Per-file check (AST-based)
|
|
42
|
+
|
|
43
|
+
1. Add a `_check_<name>` method to the `SmellDetector` class
|
|
44
|
+
2. Call it from the appropriate `visit_*` method (e.g., `visit_FunctionDef`, `visit_If`)
|
|
45
|
+
3. Use `self._add(line, pattern, name, severity, message, category)` to report findings
|
|
46
|
+
4. Pattern format: `#NNN` for numbered patterns (e.g., `#071`), `#ABC` for named patterns
|
|
47
|
+
5. Add a test in `tests/test_detector.py`
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
def _check_too_many_returns(self, node: ast.FunctionDef | ast.AsyncFunctionDef):
|
|
53
|
+
"""#071 -- Function with too many return statements."""
|
|
54
|
+
returns = [n for n in ast.walk(node) if isinstance(n, ast.Return)]
|
|
55
|
+
if len(returns) > 5:
|
|
56
|
+
self._add(node.lineno, "#071", "Too Many Returns", "info",
|
|
57
|
+
f"`{node.name}` has {len(returns)} return statements",
|
|
58
|
+
"functions")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Cross-file check
|
|
62
|
+
|
|
63
|
+
1. Add a `_detect_<name>(all_data: list[FileData]) -> list[Finding]` function
|
|
64
|
+
2. Call it from `cross_file_analysis()`
|
|
65
|
+
3. If you need new per-file metadata, add fields to `FileData` and populate them in `SmellDetector`
|
|
66
|
+
|
|
67
|
+
### Thresholds
|
|
68
|
+
|
|
69
|
+
Configurable thresholds live at the top of `detector.py` as `Final` constants. If your check has a numeric threshold, add it there.
|
|
70
|
+
|
|
71
|
+
## Code style
|
|
72
|
+
|
|
73
|
+
- **Zero dependencies.** Do not add imports beyond Python stdlib. This is a hard rule.
|
|
74
|
+
- **Python 3.10+.** Use `|` unions, pattern matching, modern features.
|
|
75
|
+
- **Type hints** on all public functions.
|
|
76
|
+
- No linter is configured in the repo yet, but keep style consistent with the existing code.
|
|
77
|
+
|
|
78
|
+
## Commit messages
|
|
79
|
+
|
|
80
|
+
We use [Conventional Commits](https://www.conventionalcommits.org/):
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
feat: add #071 too-many-returns check
|
|
84
|
+
fix: false positive in #058 with ExitStack
|
|
85
|
+
test: add regression test for elif duplicate findings
|
|
86
|
+
docs: update README with new check
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Scopes are optional but helpful: `feat(detector):`, `fix(cli):`, `test(regression):`.
|
|
90
|
+
|
|
91
|
+
## Pull requests
|
|
92
|
+
|
|
93
|
+
1. Create a feature branch from `main`
|
|
94
|
+
2. Make your changes with tests
|
|
95
|
+
3. Ensure `pytest tests/ -v` passes (all tests, including version parity)
|
|
96
|
+
4. Open a PR against `main`
|
|
97
|
+
|
|
98
|
+
The PR template will guide you through the checklist.
|
|
99
|
+
|
|
100
|
+
## Reporting bugs
|
|
101
|
+
|
|
102
|
+
Use the [bug report template](https://github.com/cheickmec/smellcheck/issues/new?template=bug_report.md). Include:
|
|
103
|
+
|
|
104
|
+
- The Python code that triggered the issue
|
|
105
|
+
- Expected vs. actual output
|
|
106
|
+
- Your Python version
|
|
107
|
+
|
|
108
|
+
## Suggesting new checks
|
|
109
|
+
|
|
110
|
+
Open a [feature request](https://github.com/cheickmec/smellcheck/issues/new?template=feature_request.md). Describe:
|
|
111
|
+
|
|
112
|
+
- What code pattern the check would detect
|
|
113
|
+
- Why it's a smell (reference to Fowler, Contieri, or other literature is a plus)
|
|
114
|
+
- Before/after examples
|
smellcheck-0.2.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cheick Berthe
|
|
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.
|