
Security News
Bun 1.2.19 Adds Isolated Installs for Better Monorepo Support
Bun 1.2.19 introduces isolated installs for smoother monorepo workflows, along with performance boosts, new tooling, and key compatibility fixes.
flake8-elegant-objects
Advanced tools
Flake8 plugin enforcing Elegant Objects principles: no -er naming, no null, no getters/setters, immutable objects
Detects violations of Elegant Objects principles including the "-er" naming principle, null usage, mutable objects, code in constructors, and getter/setter patterns.
EO001
: Class name violates -er principleEO002
: Method name violates -er principleEO003
: Variable name violates -er principleEO004
: Function name violates -er principleEO005
: Null (None) usage violates EO principleEO006
: Code in constructor violates EO principleEO007
: Getter/setter method violates EO principleEO008
: Mutable dataclass violationEO015
: Mutable class attribute violationEO016
: Mutable instance attribute violationEO017
: Instance attribute mutation violationEO018
: Augmented assignment mutation violationEO019
: Mutating method call violationEO020
: Subscript assignment mutation violationEO021
: Chained mutation violationEO022
: Missing factory methods violationEO023
: Mutable default argument violationEO024
: Missing immutability enforcement violationEO025
: Copy-on-write violationEO026
: Aliasing violation (exposing mutable state)EO027
: Defensive copy violationEO009
: Static method violates EO principle (no static methods allowed)EO010
: isinstance/type casting violates EO principle (avoid type discrimination)EO011
: Public method without contract (Protocol/ABC) violates EO principleEO012
: Test method contains non-assertThat statements (only assertThat allowed)EO013
: ORM/ActiveRecord pattern violates EO principleEO014
: Implementation inheritance violates EO principlepip install flake8-elegant-objects
Standalone:
python -m flake8_elegant_objects path/to/files/*.py
python -m flake8_elegant_objects --show-source path/to/files/*.py
As flake8 plugin:
flake8 --select=EO path/to/files/
The plugin is automatically registered when the package is installed.
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.
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.
class DataProcessor
→ ✅ class ProcessedData
def analyze()
→ ✅ def analysis()
parser = ArgumentParser()
→ ✅ arguments = ArgumentParser()
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.
return None
→ ✅ Return null objects with safe default behaviorif user is None:
→ ✅ Use null object pattern or throw exceptionsWhy? 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.
self.name = name.upper()
→ ✅ self.name = name
(transform on access)self.items = [process(x) for x in data]
→ ✅ self.data = data
(process lazily)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.
def get_value()
/ def set_value()
→ ✅ Objects should expose behavior, not datauser.getName()
→ ✅ user.introduce_yourself()
or user.greet(visitor)
Why? Mutable objects introduce temporal coupling and make reasoning about code difficult. Immutable objects are thread-safe, predictable, and easier to test. This plugin provides comprehensive detection of various mutability patterns.
Basic Mutability Issues:
@dataclass class Data
→ ✅ @dataclass(frozen=True) class Data
(EO008)items = []
(class attribute) → ✅ items: tuple = ()
(EO015)self.data = []
(instance attribute) → ✅ self.data: tuple = ()
(EO016)Mutation Patterns:
self.items.append(x)
→ ✅ self.items = (*self.items, x)
(EO019)self.count += 1
→ ✅ return Counter(self.count + 1)
(EO018)self.data[key] = value
→ ✅ Use immutable data structures (EO020)self.data = new_value
(after init) → ✅ Return new instance (EO017)Advanced Patterns:
def items=[]:
(mutable defaults) → ✅ def items=None:
+ null object (EO023)return self._items
(exposing mutable state) → ✅ return tuple(self._items)
(EO026)self.items = items
(no defensive copy) → ✅ self.items = tuple(items)
(EO027)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.
@staticmethod def process()
→ ✅ Create dedicated objects for behaviorMath.sqrt(x)
→ ✅ SquareRoot(x).value()
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.
isinstance(obj, str)
→ ✅ Design common interfaces and use polymorphismif type(x) == int:
→ ✅ Let objects decide how to behaveWhy? Public methods without explicit contracts (Protocol/ABC) create implicit dependencies and unclear expectations. Contracts make object collaboration explicit and testable.
class Service:
with ad-hoc public methods → ✅ class Service(Protocol):
with defined contractsWhy? 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.
x = 5; y = calculate(x); assert y > 0
→ ✅ assertThat(calculate(5), is_(greater_than(0)))
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.
user.save()
, Model.find()
→ ✅ Separate repository objectsWhy? 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.
class UserList(list):
→ ✅ class UserList:
with compositionThe 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.
The plugin is integrated with flake8. Add to your .flake8
config:
[flake8]
select = E,W,F,EO
per-file-ignores =
tests/*:EO012 # Allow non-assertThat in tests if needed
Run all tests:
python -m pytest tests/ -v
# Type checking
mypy flake8_elegant_objects/
# Linting and formatting
ruff check flake8_elegant_objects/
ruff format flake8_elegant_objects/
flake8_elegant_objects/
├── __init__.py # Main plugin entry point
├── __main__.py # CLI interface
├── base.py # Core types, error codes, and base classes
├── no_constructor_code.py # EO006: No code in constructors
├── no_er_name.py # EO001-EO004: No -er naming violations
├── no_getters_setters.py # EO007: No getter/setter methods
├── no_implementation_inheritance.py # EO014: No implementation inheritance
├── no_impure_tests.py # EO012: Test methods with single assertions
├── no_null.py # EO005: No None/null usage
├── no_orm.py # EO013: No ORM/ActiveRecord patterns
├── no_public_methods_without_contracts.py # EO011: Methods need contracts
├── no_static.py # EO009: No static methods
├── no_type_discrimination.py # EO010: No isinstance/type casting
└── no_mutable_objects/ # EO008, EO015-EO027: Comprehensive mutability detection
├── __init__.py # Package initialization
├── base.py # Shared utilities and state tracking
├── core.py # Main orchestrator for all mutable object checks
├── contract_checker.py # EO024: Immutability contract enforcement
├── copy_on_write_checker.py # EO025: Copy-on-write pattern validation
├── deep_checker.py # Cross-class mutation analysis
├── factory_checker.py # EO022: Factory method pattern validation
├── pattern_detectors.py # EO026-EO027: Aliasing and defensive copy detection
└── shared_state_checker.py # EO023: Shared mutable state detection
FAQs
Flake8 plugin enforcing Elegant Objects principles: no -er naming, no null, no getters/setters, immutable objects
We found that flake8-elegant-objects demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Bun 1.2.19 introduces isolated installs for smoother monorepo workflows, along with performance boosts, new tooling, and key compatibility fixes.
Security News
Popular npm packages like eslint-config-prettier were compromised after a phishing attack stole a maintainer’s token, spreading malicious updates.
Security News
/Research
A phishing attack targeted developers using a typosquatted npm domain (npnjs.com) to steal credentials via fake login pages - watch out for similar scams.