flake8-elegant-objects 1.0.0__py3-none-any.whl

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.
@@ -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
+ ```
@@ -0,0 +1,19 @@
1
+ flake8_elegant_objects/__init__.py,sha256=Yw_1bzZhgMwhHwOvrfxEfRvBNkmYNA76r3gCjgHauIY,1129
2
+ flake8_elegant_objects/__main__.py,sha256=fywUb7rQnzevRFKU0dYQ7Mho0k4jD-qF7ODqEPrKlWo,1900
3
+ flake8_elegant_objects/base.py,sha256=xpg3q-Eix7FjyqnyIXYI0LXnBb-28ktXChT0Bs80uk4,5854
4
+ flake8_elegant_objects/no_constructor_code.py,sha256=hksZiqW9UGLkg9GArZN78PHGF-VdmGjXOtJzGH_S73Y,1943
5
+ flake8_elegant_objects/no_er_name.py,sha256=ru6MrWw85Jp5ePGe6jzD7DxbJXLK-hUkFiKiJ12OTqs,8233
6
+ flake8_elegant_objects/no_getters_setters.py,sha256=4bectXt2I37jPgT13JJALEDkVjcrJ1MWYeWZG10aBt0,1771
7
+ flake8_elegant_objects/no_implementation_inheritance.py,sha256=qoUbjXOS5x7CkeHX3cwRagRLi6MXbUCrjVqgbPAnMHo,2714
8
+ flake8_elegant_objects/no_impure_tests.py,sha256=isC_uvt67ScxD5UJ8POZcOaY5ijjBPuBTsWqD5rlTMY,4902
9
+ flake8_elegant_objects/no_mutable_objects.py,sha256=1Tno7WVZxDquxVGQAReEkD02BWzYovxMgrq3-gf2jLE,2687
10
+ flake8_elegant_objects/no_null.py,sha256=pomKBlNcmQZx00N9xBTdmzGGPTVYoI070yEKFq-1cQc,1831
11
+ flake8_elegant_objects/no_orm.py,sha256=dbkSOWL0djrUX4srNOd_NHs4Pv5ElMRdia0x_8cumfY,2655
12
+ flake8_elegant_objects/no_public_methods_without_contracts.py,sha256=hFoSDMT5rgmvROqGm-z3FwSsPymQC_Qatc9mNCcZOMQ,4245
13
+ flake8_elegant_objects/no_static.py,sha256=B1hkb37DNTfJDKRApvmsUIzdIOaviKI9ksMhQqedeGE,995
14
+ flake8_elegant_objects/no_type_discrimination.py,sha256=Gtg9OHtTOqL95hE3ZlSGZ8oTvMPXXY2293ySz3kYsdQ,1042
15
+ flake8_elegant_objects-1.0.0.dist-info/METADATA,sha256=p_mMeZbAJIhQHq6VhG0KSSienJTS1b6IcPgJJhLit18,9875
16
+ flake8_elegant_objects-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
+ flake8_elegant_objects-1.0.0.dist-info/entry_points.txt,sha256=mJESrSTKPS5xBffrQod7Z3F_cCjgpXTHstbm0qXC530,140
18
+ flake8_elegant_objects-1.0.0.dist-info/licenses/LICENSE,sha256=Ugb5MV4aQkMI2kGn9LeRaeDGi95cmYaONptbG2J7QxQ,1071
19
+ flake8_elegant_objects-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ flake8-elegant-objects = flake8_elegant_objects:main
3
+
4
+ [flake8.extension]
5
+ EO = flake8_elegant_objects:ElegantObjectsPlugin
@@ -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.