flake8-elegant-objects 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. flake8_elegant_objects-1.0.0/.github/workflows/lint.yml +33 -0
  2. flake8_elegant_objects-1.0.0/.github/workflows/publish.yml +54 -0
  3. flake8_elegant_objects-1.0.0/.github/workflows/test.yml +44 -0
  4. flake8_elegant_objects-1.0.0/.gitignore +161 -0
  5. flake8_elegant_objects-1.0.0/CLAUDE.md +59 -0
  6. flake8_elegant_objects-1.0.0/LICENSE +21 -0
  7. flake8_elegant_objects-1.0.0/PKG-INFO +216 -0
  8. flake8_elegant_objects-1.0.0/README.md +186 -0
  9. flake8_elegant_objects-1.0.0/docs/EO011.md +144 -0
  10. flake8_elegant_objects-1.0.0/flake8_elegant_objects/__init__.py +36 -0
  11. flake8_elegant_objects-1.0.0/flake8_elegant_objects/__main__.py +68 -0
  12. flake8_elegant_objects-1.0.0/flake8_elegant_objects/base.py +180 -0
  13. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_constructor_code.py +45 -0
  14. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_er_name.py +306 -0
  15. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_getters_setters.py +57 -0
  16. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_implementation_inheritance.py +75 -0
  17. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_impure_tests.py +127 -0
  18. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_mutable_objects.py +69 -0
  19. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_null.py +50 -0
  20. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_orm.py +97 -0
  21. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_public_methods_without_contracts.py +125 -0
  22. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_static.py +31 -0
  23. flake8_elegant_objects-1.0.0/flake8_elegant_objects/no_type_discrimination.py +34 -0
  24. flake8_elegant_objects-1.0.0/pyproject.toml +166 -0
  25. flake8_elegant_objects-1.0.0/tests/__init__.py +1 -0
  26. flake8_elegant_objects-1.0.0/tests/test_integration.py +248 -0
  27. flake8_elegant_objects-1.0.0/tests/test_no_constructor_code.py +133 -0
  28. flake8_elegant_objects-1.0.0/tests/test_no_er_name.py +157 -0
  29. flake8_elegant_objects-1.0.0/tests/test_no_getters_setters.py +186 -0
  30. flake8_elegant_objects-1.0.0/tests/test_no_implementation_inheritance.py +78 -0
  31. flake8_elegant_objects-1.0.0/tests/test_no_impure_tests.py +153 -0
  32. flake8_elegant_objects-1.0.0/tests/test_no_mutable_objects.py +167 -0
  33. flake8_elegant_objects-1.0.0/tests/test_no_null.py +126 -0
  34. flake8_elegant_objects-1.0.0/tests/test_no_orm.py +64 -0
  35. flake8_elegant_objects-1.0.0/tests/test_no_public_methods_without_contracts.py +277 -0
  36. flake8_elegant_objects-1.0.0/tests/test_no_static.py +53 -0
  37. flake8_elegant_objects-1.0.0/tests/test_no_type_discrimination.py +67 -0
  38. flake8_elegant_objects-1.0.0/uv.lock +316 -0
@@ -0,0 +1,33 @@
1
+ name: Lint
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v4
17
+ with:
18
+ enable-cache: true
19
+
20
+ - name: Set up Python
21
+ run: uv python install 3.10
22
+
23
+ - name: Install dependencies
24
+ run: uv sync
25
+
26
+ - name: Run ruff check
27
+ run: uv run ruff check
28
+
29
+ - name: Run ruff format check
30
+ run: uv run ruff format --check
31
+
32
+ - name: Run type checking
33
+ run: uv run mypy .
@@ -0,0 +1,54 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch: # Allow manual triggering
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: publish # Use environment for additional security
12
+ permissions:
13
+ id-token: write # For trusted publishing
14
+ contents: read
15
+
16
+ steps:
17
+ - name: Checkout code
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v3
22
+ with:
23
+ version: "latest"
24
+
25
+ - name: Set up Python
26
+ run: uv python install 3.12
27
+
28
+ - name: Install dependencies
29
+ run: < /dev/null |
30
+ uv sync --dev
31
+ uv add --dev twine
32
+
33
+ - name: Run tests
34
+ run: |
35
+ uv run python -m pytest tests/ -v
36
+ uv run mypy flake8_elegant_objects/
37
+ uv run ruff check flake8_elegant_objects/
38
+
39
+ - name: Build package
40
+ run: uv build
41
+
42
+ - name: Publish to TestPyPI
43
+ env:
44
+ TWINE_USERNAME: __token__
45
+ TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
46
+ run: |
47
+ uv run twine upload --repository testpypi dist/* --skip-existing
48
+
49
+ - name: Publish to PyPI
50
+ env:
51
+ TWINE_USERNAME: __token__
52
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
53
+ run: |
54
+ uv run twine upload dist/*
@@ -0,0 +1,44 @@
1
+ name: Build and Test
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ['3.10', '3.11', '3.12', '3.13']
15
+
16
+ steps:
17
+ - name: Checkout code
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v4
22
+ with:
23
+ version: 'latest'
24
+
25
+ - name: Set up Python ${{ matrix.python-version }}
26
+ run: uv python install ${{ matrix.python-version }}
27
+
28
+ - name: Install dependencies
29
+ run: uv sync
30
+
31
+ - name: Run linting
32
+ run: uv run ruff check .
33
+
34
+ - name: Run formatting check
35
+ run: uv run ruff format --check .
36
+
37
+ - name: Run type checking
38
+ run: uv run mypy .
39
+
40
+ - name: Run unit tests
41
+ run: uv run pytest tests/ -v
42
+
43
+ - name: Build package
44
+ run: uv build
@@ -0,0 +1,161 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+
52
+ # Translations
53
+ *.mo
54
+ *.pot
55
+
56
+ # Django stuff:
57
+ *.log
58
+ local_settings.py
59
+ db.sqlite3
60
+ db.sqlite3-journal
61
+
62
+ # Flask stuff:
63
+ instance/
64
+ .webassets-cache
65
+
66
+ # Scrapy stuff:
67
+ .scrapy
68
+
69
+ # Sphinx documentation
70
+ docs/_build/
71
+
72
+ # PyBuilder
73
+ .pybuilder/
74
+ target/
75
+
76
+ # Jupyter Notebook
77
+ .ipynb_checkpoints
78
+
79
+ # IPython
80
+ profile_default/
81
+ ipython_config.py
82
+
83
+ # pyenv
84
+ .python-version
85
+
86
+ # pipenv
87
+ Pipfile.lock
88
+
89
+ # poetry
90
+ poetry.lock
91
+
92
+ # pdm
93
+ .pdm.toml
94
+
95
+ # PEP 582
96
+ __pypackages__/
97
+
98
+ # Celery stuff
99
+ celerybeat-schedule
100
+ celerybeat.pid
101
+
102
+ # SageMath parsed files
103
+ *.sage.py
104
+
105
+ # Environments
106
+ .env
107
+ .venv
108
+ env/
109
+ venv/
110
+ ENV/
111
+ env.bak/
112
+ venv.bak/
113
+
114
+ # Spyder project settings
115
+ .spyderproject
116
+ .spyproject
117
+
118
+ # Rope project settings
119
+ .ropeproject
120
+
121
+ # mkdocs documentation
122
+ /site
123
+
124
+ # mypy
125
+ .mypy_cache/
126
+ .dmypy.json
127
+ dmypy.json
128
+
129
+ # Pyre type checker
130
+ .pyre/
131
+
132
+ # pytype static type analyzer
133
+ .pytype/
134
+
135
+ # Cython debug symbols
136
+ cython_debug/
137
+
138
+ # Ruff
139
+ .ruff_cache/
140
+
141
+ # IDE / Editor files
142
+ .vscode/
143
+ .idea/
144
+ *.swp
145
+ *.swo
146
+ *~
147
+
148
+ # OS generated files
149
+ .DS_Store
150
+ .DS_Store?
151
+ ._*
152
+ .Spotlight-V100
153
+ .Trashes
154
+ ehthumbs.db
155
+ Thumbs.db
156
+
157
+ # Temporary files
158
+ *.tmp
159
+ *.temp
160
+ *.bak
161
+ *.orig
@@ -0,0 +1,59 @@
1
+ # CLAUDE.md
2
+
3
+ ## Development Commands
4
+
5
+ ```bash
6
+ # Testing
7
+ python -m pytest tests/ -v
8
+
9
+ # Code Quality
10
+ mypy flake8_elegant_objects/
11
+ ruff check flake8_elegant_objects/
12
+ ruff format flake8_elegant_objects/
13
+
14
+ # Plugin Usage
15
+ python -m flake8_elegant_objects --show-source path/to/files/*.py
16
+ flake8 --select=EO path/to/files/
17
+ ```
18
+
19
+ ## Architecture
20
+
21
+ Flake8 plugin enforcing Elegant Objects principles (14 error codes EO001-EO014).
22
+
23
+ **Core Components:**
24
+ - `__init__.py`: ElegantObjectsPlugin orchestrating analysis
25
+ - `base.py`: Principles base class, Violations type, ErrorCodes
26
+ - `naming.py`: NoErNamePrinciple (EO001-EO004)
27
+ - `core.py`: CorePrinciples (EO005-EO008)
28
+ - `advanced.py`: AdvancedPrinciples (EO009-EO014)
29
+
30
+ **Pattern:** Plugin uses Principles which provides Violations (no None)
31
+
32
+ ## Elegant Objects Principles
33
+
34
+ **MUST follow:**
35
+ - No null (None) - use empty lists instead
36
+ - No code in constructors
37
+ - No getters/setters
38
+ - No mutable objects
39
+ - NO "-ER" NAMES: Manager, Controller, Helper, Handler, Parser, etc
40
+ - No static methods
41
+ - No instanceof/type casting
42
+ - No public methods without contracts
43
+ - No statements in test methods except assertThat
44
+ - No ORM/ActiveRecord
45
+ - No implementation inheritance
46
+
47
+ **Philosophy:**
48
+ - Objects are living partners, not data containers
49
+ - Declarative over imperative (`new Sorted(apples)` not `new Sorter().sort(apples)`)
50
+ - Fail fast with exceptions, not null checks
51
+ - Composition over inheritance
52
+ - Immutability implied by design, not keywords
53
+
54
+ ## Code Style
55
+
56
+ - No useless comments
57
+ - Types: `Violations = list[Violation]`
58
+ - Return empty lists `[]` instead of `None`
59
+ - Extend lists instead of checking for None
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Anton Prokopyev
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.
@@ -0,0 +1,216 @@
1
+ Metadata-Version: 2.4
2
+ Name: flake8-elegant-objects
3
+ Version: 1.0.0
4
+ Summary: Flake8 plugin enforcing Elegant Objects principles: no -er naming, no null, no getters/setters, immutable objects
5
+ Project-URL: Homepage, https://github.com/AntonProkopyev/flake8-elegant-objects
6
+ Project-URL: Repository, https://github.com/AntonProkopyev/flake8-elegant-objects.git
7
+ Project-URL: Issues, https://github.com/AntonProkopyev/flake8-elegant-objects/issues
8
+ Project-URL: Documentation, https://github.com/AntonProkopyev/flake8-elegant-objects#readme
9
+ Author: Flake8 Elegant Objects Contributors
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: code-quality,elegant-objects,flake8,linting,object-oriented,python,static-analysis
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Framework :: Flake8
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Topic :: Software Development :: Quality Assurance
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.10
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Flake8 ElegantObjects Plugin
32
+
33
+ [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](https://github.com/AntonProkopyev/flake8-elegant-objects)
34
+ [![Coverage](https://img.shields.io/badge/coverage-86%25-brightgreen.svg)](https://github.com/AntonProkopyev/flake8-elegant-objects)
35
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://python.org)
36
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
37
+ [![Code style: ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
38
+ [![Type checked: mypy](https://img.shields.io/badge/type_checked-mypy-blue.svg)](https://mypy.readthedocs.io/)
39
+ [![Flake8](https://img.shields.io/badge/flake8-plugin-orange.svg)](https://flake8.pycqa.org/)
40
+
41
+ Detects violations of core Elegant Objects principles including the "-er" naming principle, null usage, mutable objects, code in constructors, and getter/setter patterns.
42
+
43
+ ## Error Codes
44
+
45
+ - `EO001`: Class name violates -er principle
46
+ - `EO002`: Method name violates -er principle
47
+ - `EO003`: Variable name violates -er principle
48
+ - `EO004`: Function name violates -er principle
49
+ - `EO005`: Null (None) usage violates EO principle
50
+ - `EO006`: Code in constructor violates EO principle
51
+ - `EO007`: Getter/setter method violates EO principle
52
+ - `EO008`: Mutable object violation
53
+ - `EO009`: Static method violates EO principle (no static methods allowed)
54
+ - `EO010`: isinstance/type casting violates EO principle (avoid type discrimination)
55
+ - `EO011`: Public method without contract (Protocol/ABC) violates EO principle
56
+ - `EO012`: Test method contains non-assertThat statements (only assertThat allowed)
57
+ - `EO013`: ORM/ActiveRecord pattern violates EO principle
58
+ - `EO014`: Implementation inheritance violates EO principle
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install flake8-elegant-objects
64
+ ```
65
+
66
+ ## Usage
67
+
68
+ **Standalone:**
69
+
70
+ ```bash
71
+ python -m flake8_elegant_objects path/to/files/*.py
72
+ python -m flake8_elegant_objects --show-source path/to/files/*.py
73
+ ```
74
+
75
+ **As flake8 plugin:**
76
+
77
+ ```bash
78
+ flake8 --select=EO path/to/files/
79
+ ```
80
+
81
+ The plugin is automatically registered when the package is installed.
82
+
83
+ ## Philosophy
84
+
85
+ Based on Yegor Bugayenko's Elegant Objects principles, this plugin enforces object-oriented design that treats objects as living, thinking entities rather than data containers or procedure executors.
86
+
87
+ ### 1. No "-er" Entities (EO001-EO004)
88
+
89
+ **Why?** Names ending in "-er" describe what objects _do_ rather than what they _are_, reducing them to mechanical task performers instead of equal partners in your design.
90
+
91
+ - ❌ `class DataProcessor` → ✅ `class ProcessedData`
92
+ - ❌ `def analyze()` → ✅ `def analysis()`
93
+ - ❌ `parser = ArgumentParser()` → ✅ `arguments = ArgumentParser()`
94
+
95
+ ### 2. No Null/None (EO005)
96
+
97
+ **Why?** Null references break object-oriented thinking by representing "absence of object" - but absence cannot participate in object interactions. They lead to defensive programming and unclear contracts.
98
+
99
+ - ❌ `return None` → ✅ Return null objects with safe default behavior
100
+ - ❌ `if user is None:` → ✅ Use null object pattern or throw exceptions
101
+
102
+ ### 3. No Code in Constructors (EO006)
103
+
104
+ **Why?** Constructors should be about object assembly, not computation. Complex logic in constructors violates the principle that objects should be lazy and do work only when asked.
105
+
106
+ - ❌ `self.name = name.upper()` → ✅ `self.name = name` (transform on access)
107
+ - ❌ `self.items = [process(x) for x in data]` → ✅ `self.data = data` (process lazily)
108
+
109
+ ### 4. No Getters/Setters (EO007)
110
+
111
+ **Why?** Getters and setters expose internal state, breaking encapsulation. They encourage "tell, don't ask" violations and treat objects as data containers rather than behavioral entities.
112
+
113
+ - ❌ `def get_value()` / `def set_value()` → ✅ Objects should expose behavior, not data
114
+ - ❌ `user.getName()` → ✅ `user.introduce_yourself()` or `user.greet(visitor)`
115
+
116
+ ### 5. No Mutable Objects (EO008)
117
+
118
+ **Why?** Mutable objects introduce temporal coupling and make reasoning about code difficult. Immutable objects are thread-safe, predictable, and easier to test.
119
+
120
+ - ❌ `@dataclass class Data` → ✅ `@dataclass(frozen=True) class Data`
121
+ - ❌ `items = []` → ✅ `items: tuple = ()`
122
+
123
+ ### 6. No Static Methods (EO009)
124
+
125
+ **Why?** Static methods belong to classes, not objects, breaking object-oriented design. They can't be overridden, can't be mocked easily, and promote procedural thinking. Every static method is a candidate for a new class.
126
+
127
+ - ❌ `@staticmethod def process()` → ✅ Create dedicated objects for behavior
128
+ - ❌ `Math.sqrt(x)` → ✅ `SquareRoot(x).value()`
129
+
130
+ ### 7. No Type Discrimination (EO010)
131
+
132
+ **Why?** Using `isinstance`, type casting, or reflection is a form of object discrimination. It violates polymorphism by treating objects unequally based on their type rather than their behavior contracts.
133
+
134
+ - ❌ `isinstance(obj, str)` → ✅ Design common interfaces and use polymorphism
135
+ - ❌ `if type(x) == int:` → ✅ Let objects decide how to behave
136
+
137
+ ### 8. No Public Methods Without Contracts (EO011)
138
+
139
+ **Why?** Public methods without explicit contracts (Protocol/ABC) create implicit dependencies and unclear expectations. Contracts make object collaboration explicit and testable.
140
+
141
+ - ❌ `class Service:` with ad-hoc public methods → ✅ `class Service(Protocol):` with defined contracts
142
+ - ❌ Implicit interfaces → ✅ Explicit protocols that can be tested and verified
143
+
144
+ ### 9. Test Methods: Only assertThat (EO012)
145
+
146
+ **Why?** Test methods should contain only one assertion statement (preferably `assertThat`). Multiple statements create complex tests that are hard to understand and maintain. Each test should verify one specific behavior.
147
+
148
+ - ❌ `x = 5; y = calculate(x); assert y > 0` → ✅ `assertThat(calculate(5), is_(greater_than(0)))`
149
+ - ❌ Multiple assertions per test → ✅ One focused assertion per test
150
+
151
+ ### 10. No ORM/ActiveRecord (EO013)
152
+
153
+ **Why?** ORM and ActiveRecord patterns mix data persistence concerns with business logic, violating single responsibility. They create anemic domain models and tight coupling to databases.
154
+
155
+ - ❌ `user.save()`, `Model.find()` → ✅ Separate repository objects
156
+ - ❌ Mixing persistence with business logic → ✅ Clean separation of concerns
157
+
158
+ ### 11. No Implementation Inheritance (EO014)
159
+
160
+ **Why?** Implementation inheritance creates tight coupling between parent and child classes, making code fragile and hard to test. It violates composition over inheritance and creates deep hierarchies that are difficult to understand.
161
+
162
+ - ❌ `class UserList(list):` → ✅ `class UserList:` with composition
163
+ - ❌ Inheriting concrete implementations → ✅ Inherit only from abstractions (ABC/Protocol)
164
+
165
+ The plugin detects the "hall of shame" naming patterns: Manager, Controller, Helper, Handler, Writer, Reader, Converter, Validator, Router, Dispatcher, Observer, Listener, Sorter, Encoder, Decoder, Analyzer, etc.
166
+
167
+ ## Configuration
168
+
169
+ The plugin is integrated with flake8. Add to your `.flake8` config:
170
+
171
+ ```ini
172
+ [flake8]
173
+ select = E,W,F,EO
174
+ per-file-ignores =
175
+ tests/*:EO012 # Allow non-assertThat in tests if needed
176
+ ```
177
+
178
+ ## Development
179
+
180
+ ### Testing
181
+
182
+ Run all tests:
183
+
184
+ ```bash
185
+ python -m pytest tests/ -v
186
+ ```
187
+
188
+ ### Code Quality
189
+
190
+ ```bash
191
+ # Type checking
192
+ mypy flake8_elegant_objects/
193
+
194
+ # Linting and formatting
195
+ ruff check flake8_elegant_objects/
196
+ ruff format flake8_elegant_objects/
197
+ ```
198
+
199
+ ### Project Structure
200
+
201
+ ```
202
+ flake8_elegant_objects/
203
+ ├── __init__.py # Main plugin entry point
204
+ ├── base.py # Base classes and utilities
205
+ ├── no_er_name.py # EO001-EO004: No "-er" names
206
+ ├── no_null.py # EO005: No None usage
207
+ ├── no_constructor_code.py # EO006: No code in constructors
208
+ ├── no_getters_setters.py # EO007: No getters/setters
209
+ ├── no_mutable_objects.py # EO008: No mutable objects
210
+ ├── no_static.py # EO009: No static methods
211
+ ├── no_type_discrimination.py # EO010: No isinstance/type casting
212
+ ├── no_public_methods_without_contracts.py # EO011: Contracts required
213
+ ├── no_impure_tests.py # EO012: Only assertThat in tests
214
+ ├── no_orm.py # EO013: No ORM/ActiveRecord
215
+ └── no_implementation_inheritance.py # EO014: No implementation inheritance
216
+ ```