vera-syntaxis


Namevera-syntaxis JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryA static analysis tool for detecting architectural code smells in Python
upload_time2025-10-10 21:21:21
maintainerNone
docs_urlNone
authorRH Labs/The SurveyOS Project
requires_python>=3.8
licenseMIT
keywords linter architecture code-quality static-analysis design-patterns
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Vera Syntaxis

A static analysis tool for detecting architectural code smells in Python codebases.

## Overview

Vera Syntaxis is a specialized linter that focuses on architectural issues rather than style or type safety:

- **Tight Coupling Detection**: Identifies instances of tight coupling between classes
- **MVBC Architecture Linter**: Enforces communication rules between Model, View, Business, and Controller layers

## Features

- **AST-based Analysis**: Uses Python's Abstract Syntax Tree for accurate code analysis
- **Call Graph Construction**: Builds a comprehensive call graph of your codebase
- **Configurable Rules**: Customize architectural rules via TOML configuration
- **Python API**: Use as a library in your own tools
- **GUI Interface**: Optional GUI plugin for Extensio Pulchra framework

## Requirements

- Python 3.8+
- Extensive use of type hints in target codebase for accurate analysis

## Installation

```bash
pip install vera-syntaxis
```

For development:
```bash
pip install vera-syntaxis[dev]
```

For GUI support:
```bash
pip install vera-syntaxis[gui]
```

## Quick Start

### Command Line

```bash
# Parse and analyze a project
vera-syntaxis analyze /path/to/project

# Parse only (for debugging)
vera-syntaxis parse /path/to/project
```

### Python API

```python
from vera_syntaxis import run_analysis

violations = run_analysis("/path/to/project")
for violation in violations:
    print(f"{violation.file_path}:{violation.line_number} - {violation.message}")
```

## Configuration

Create a `pyproject.toml` in your project root:

```toml
[tool.vera_syntaxis]
model_paths = ["my_app/models/"]
view_paths = ["my_app/views/"]
business_paths = ["my_app/services/"]
controller_paths = ["my_app/controllers/"]
module_filter = ["my_app.*"]

[tool.vera_syntaxis.coupling]
max_inter_class_calls = 5
max_demeter_chain = 3

[tool.vera_syntaxis.circular]
allow_self_cycles = false
max_cycle_length = 10

[tool.vera_syntaxis.god_object]
max_methods = 20
max_attributes = 15
max_lines = 300

[tool.vera_syntaxis.feature_envy]
envy_threshold = 0.5
min_accesses = 3

[tool.vera_syntaxis.data_clump]
min_clump_size = 3
min_occurrences = 2
```

## Architectural Rules

### Tight Coupling Linter

#### TC001: Direct Instantiation
Flags direct class instantiations that create tight coupling.

**Example Violation:**
```python
class UserService:
    def create_user(self):
        # Bad: Direct instantiation creates tight coupling
        db = Database()
        return db.save(User())
```

**Fix:** Use dependency injection instead.

#### TC002: Law of Demeter (Method Chaining)
Detects excessive method chaining that violates the Law of Demeter.

**Configuration:**
- `max_demeter_chain`: Maximum allowed chain length (default: 5)

**Example Violation:**
```python
# Bad: Chain length of 4 exceeds configured maximum of 3
country = user.address.city.postal_code.country
```

**Fix:** Break the chain using intermediate variables or add helper methods:
```python
# Good: Use intermediate variables
address = user.address
city = address.city
postal_code = city.postal_code
country = postal_code.country

# Or: Add a helper method
country = user.get_country()
```

#### TC003: Excessive Interaction
Identifies classes with too many inter-class calls using call graph analysis.

**Configuration:**
- `max_inter_class_calls`: Maximum allowed unique method calls to other classes (default: 10)

**Example Violation:**
```python
class Client:
    def __init__(self):
        self.service_a = ServiceA()
        self.service_b = ServiceB()
        self.service_c = ServiceC()
    
    def do_work(self):
        # Bad: Calls 12 unique methods from other classes (exceeds limit of 10)
        self.service_a.method1()
        self.service_a.method2()
        self.service_a.method3()
        self.service_a.method4()
        self.service_b.method1()
        self.service_b.method2()
        self.service_b.method3()
        self.service_b.method4()
        self.service_c.method1()
        self.service_c.method2()
        self.service_c.method3()
        self.service_c.method4()
```

**Fix:** Refactor to reduce coupling:
```python
# Good: Introduce a facade or coordinator
class ServiceCoordinator:
    def __init__(self):
        self.service_a = ServiceA()
        self.service_b = ServiceB()
        self.service_c = ServiceC()
    
    def execute_workflow(self):
        # Encapsulates the complex interactions
        self.service_a.do_work()
        self.service_b.do_work()
        self.service_c.do_work()

class Client:
    def __init__(self):
        self.coordinator = ServiceCoordinator()
    
    def do_work(self):
        # Now only 1 inter-class call
        self.coordinator.execute_workflow()
```

### MVBC Linter

#### W2902: Illegal Layer Dependency
Enforces proper layer communication in Model-View-Business-Controller architecture.

**Allowed Dependencies:**
- **Model**: Can be used by anyone (no dependencies on other layers)
- **View**: Can use Model only
- **Business**: Can use Model only  
- **Controller**: Can use View, Business, and Model

**Forbidden Dependencies:**
- View → Business (must go through Controller)
- Business → View (business logic shouldn't know about presentation)
- View → Controller (creates circular dependency)
- Business → Controller (creates circular dependency)

**Configuration:**
Supports both single-level and nested directory patterns:
```toml
[tool.vera_syntaxis.mvbc]
model_paths = ["models/*.py"]          # Matches models/user.py and models/subdir/user.py
view_paths = ["views/**/*.py"]         # Explicit nested pattern
business_paths = ["business/*.py"]
controller_paths = ["controllers/*.py"]
```

**Example Violations:**

*View → Business:*
```python
# In views/user_view.py
from business.user_logic import UserLogic  # Bad: View importing Business

class UserView:
    def __init__(self):
        self.logic = UserLogic()
```
**Fix:** Use a Controller to mediate between View and Business.

*Business → View:*
```python
# In business/user_logic.py
from views.user_view import UserView  # Bad: Business importing View

class UserLogic:
    def __init__(self):
        self.view = UserView()
```
**Fix:** Keep business logic independent of presentation. Controllers should manage Views.

### Circular Dependency Linter

#### CD001: Circular Dependency
Detects circular dependencies between modules.

**Configuration:**
- `allow_self_cycles`: Allow methods to call themselves (default: false)
- `max_cycle_length`: Maximum cycle length to report (default: 10)

**Example Violation:**
```python
# module_a.py
from module_b import ClassB

class ClassA:
    def method(self):
        b = ClassB()

# module_b.py
from module_a import ClassA  # Circular dependency!

class ClassB:
    def method(self):
        a = ClassA()
```

**Fix:** Break the cycle using dependency inversion:
```python
# interface.py
from abc import ABC, abstractmethod

class IProcessor(ABC):
    @abstractmethod
    def process(self): pass

# module_a.py
from interface import IProcessor

class ClassA(IProcessor):
    def process(self):
        pass

# module_b.py
from interface import IProcessor

class ClassB:
    def __init__(self, processor: IProcessor):
        self.processor = processor
```

### God Object Linter

#### GO001: God Object
Detects classes with too many responsibilities (God Objects).

**Configuration:**
- `max_methods`: Maximum number of methods allowed (default: 20)
- `max_attributes`: Maximum number of attributes allowed (default: 15)
- `max_lines`: Maximum number of lines allowed (default: 300)

**Example Violation:**
```python
class UserManager:  # God Object!
    def __init__(self):
        self.db = Database()
        self.cache = Cache()
        self.logger = Logger()
        self.validator = Validator()
        self.emailer = Emailer()
        # ... 15+ attributes
    
    def create_user(self): pass
    def update_user(self): pass
    def delete_user(self): pass
    def validate_email(self): pass
    def send_welcome_email(self): pass
    def log_activity(self): pass
    # ... 20+ methods
```

**Fix:** Split into focused classes:
```python
# Separate concerns into focused classes
class UserRepository:
    def __init__(self, db):
        self.db = db
    
    def create(self, user): pass
    def update(self, user): pass
    def delete(self, user_id): pass

class UserValidator:
    def validate_email(self, email): pass
    def validate_password(self, password): pass

class UserNotifier:
    def __init__(self, emailer):
        self.emailer = emailer
    
    def send_welcome_email(self, user): pass
    def send_password_reset(self, user): pass

class UserService:
    def __init__(self, repo, validator, notifier):
        self.repo = repo
        self.validator = validator
        self.notifier = notifier
    
    def create_user(self, user_data):
        if self.validator.validate_email(user_data['email']):
            user = self.repo.create(user_data)
            self.notifier.send_welcome_email(user)
            return user
```

### Feature Envy Linter

#### FE001: Feature Envy
Detects methods that use more features from another class than their own.

**Configuration:**
- `envy_threshold`: Ratio of external to total accesses that triggers envy (default: 0.5)
- `min_accesses`: Minimum total accesses before checking (default: 3)

**Example Violation:**
```python
class Order:
    def __init__(self):
        self.customer = Customer()
    
    def validate_customer_email(self):  # Feature Envy!
        # Uses customer features more than own features
        email = self.customer.email
        email = self.customer.email.strip()
        email = self.customer.email.lower()
        is_valid = '@' in self.customer.email
        return is_valid
```

**Fix:** Move the method to the appropriate class:
```python
class Customer:
    def __init__(self):
        self.email = ""
    
    def validate_email(self):
        # Now uses own features
        email = self.email.strip().lower()
        return '@' in email

class Order:
    def __init__(self):
        self.customer = Customer()
    
    def process(self):
        # Delegate to the appropriate class
        if self.customer.validate_email():
            # Process order
            pass
```

### Data Clump Linter

#### DC001: Data Clump
Detects groups of parameters that frequently appear together.

**Configuration:**
- `min_clump_size`: Minimum number of parameters in a clump (default: 3)
- `min_occurrences`: Minimum number of methods with the clump (default: 2)

**Example Violation:**
```python
class OrderProcessor:
    def calculate_total(self, price, quantity, discount):  # Data Clump!
        return price * quantity * (1 - discount)
    
    def validate_order(self, price, quantity, discount):  # Same parameters
        return price > 0 and quantity > 0
    
    def format_order(self, price, quantity, discount):  # Same parameters
        return f"{quantity} items at ${price}"
```

**Fix:** Extract data clump into a class:
```python
class OrderDetails:
    def __init__(self, price: float, quantity: int, discount: float):
        self.price = price
        self.quantity = quantity
        self.discount = discount
    
    def calculate_total(self) -> float:
        return self.price * self.quantity * (1 - self.discount)
    
    def validate(self) -> bool:
        return self.price > 0 and self.quantity > 0
    
    def format(self) -> str:
        return f"{self.quantity} items at ${self.price}"

class OrderProcessor:
    def process_order(self, details: OrderDetails):
        if details.validate():
            total = details.calculate_total()
            print(details.format())
            return total
```

## Limitations

- Requires extensive type hints for accurate analysis
- Dynamic attributes (via `__getattr__`) may not be detected
- Forward references must be properly annotated

## Development Status

Version 0.1.0 - Alpha (Reporting Only)

## License

MIT License

## Contributing

Contributions welcome! Please see CONTRIBUTING.md for guidelines.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "vera-syntaxis",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "linter, architecture, code-quality, static-analysis, design-patterns",
    "author": "RH Labs/The SurveyOS Project",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/6d/89/58406268bb24e4b8c7befd1e2451f3263449c9a7cff5e75a734173baf4fb/vera_syntaxis-0.1.0.tar.gz",
    "platform": null,
    "description": "# Vera Syntaxis\r\n\r\nA static analysis tool for detecting architectural code smells in Python codebases.\r\n\r\n## Overview\r\n\r\nVera Syntaxis is a specialized linter that focuses on architectural issues rather than style or type safety:\r\n\r\n- **Tight Coupling Detection**: Identifies instances of tight coupling between classes\r\n- **MVBC Architecture Linter**: Enforces communication rules between Model, View, Business, and Controller layers\r\n\r\n## Features\r\n\r\n- **AST-based Analysis**: Uses Python's Abstract Syntax Tree for accurate code analysis\r\n- **Call Graph Construction**: Builds a comprehensive call graph of your codebase\r\n- **Configurable Rules**: Customize architectural rules via TOML configuration\r\n- **Python API**: Use as a library in your own tools\r\n- **GUI Interface**: Optional GUI plugin for Extensio Pulchra framework\r\n\r\n## Requirements\r\n\r\n- Python 3.8+\r\n- Extensive use of type hints in target codebase for accurate analysis\r\n\r\n## Installation\r\n\r\n```bash\r\npip install vera-syntaxis\r\n```\r\n\r\nFor development:\r\n```bash\r\npip install vera-syntaxis[dev]\r\n```\r\n\r\nFor GUI support:\r\n```bash\r\npip install vera-syntaxis[gui]\r\n```\r\n\r\n## Quick Start\r\n\r\n### Command Line\r\n\r\n```bash\r\n# Parse and analyze a project\r\nvera-syntaxis analyze /path/to/project\r\n\r\n# Parse only (for debugging)\r\nvera-syntaxis parse /path/to/project\r\n```\r\n\r\n### Python API\r\n\r\n```python\r\nfrom vera_syntaxis import run_analysis\r\n\r\nviolations = run_analysis(\"/path/to/project\")\r\nfor violation in violations:\r\n    print(f\"{violation.file_path}:{violation.line_number} - {violation.message}\")\r\n```\r\n\r\n## Configuration\r\n\r\nCreate a `pyproject.toml` in your project root:\r\n\r\n```toml\r\n[tool.vera_syntaxis]\r\nmodel_paths = [\"my_app/models/\"]\r\nview_paths = [\"my_app/views/\"]\r\nbusiness_paths = [\"my_app/services/\"]\r\ncontroller_paths = [\"my_app/controllers/\"]\r\nmodule_filter = [\"my_app.*\"]\r\n\r\n[tool.vera_syntaxis.coupling]\r\nmax_inter_class_calls = 5\r\nmax_demeter_chain = 3\r\n\r\n[tool.vera_syntaxis.circular]\r\nallow_self_cycles = false\r\nmax_cycle_length = 10\r\n\r\n[tool.vera_syntaxis.god_object]\r\nmax_methods = 20\r\nmax_attributes = 15\r\nmax_lines = 300\r\n\r\n[tool.vera_syntaxis.feature_envy]\r\nenvy_threshold = 0.5\r\nmin_accesses = 3\r\n\r\n[tool.vera_syntaxis.data_clump]\r\nmin_clump_size = 3\r\nmin_occurrences = 2\r\n```\r\n\r\n## Architectural Rules\r\n\r\n### Tight Coupling Linter\r\n\r\n#### TC001: Direct Instantiation\r\nFlags direct class instantiations that create tight coupling.\r\n\r\n**Example Violation:**\r\n```python\r\nclass UserService:\r\n    def create_user(self):\r\n        # Bad: Direct instantiation creates tight coupling\r\n        db = Database()\r\n        return db.save(User())\r\n```\r\n\r\n**Fix:** Use dependency injection instead.\r\n\r\n#### TC002: Law of Demeter (Method Chaining)\r\nDetects excessive method chaining that violates the Law of Demeter.\r\n\r\n**Configuration:**\r\n- `max_demeter_chain`: Maximum allowed chain length (default: 5)\r\n\r\n**Example Violation:**\r\n```python\r\n# Bad: Chain length of 4 exceeds configured maximum of 3\r\ncountry = user.address.city.postal_code.country\r\n```\r\n\r\n**Fix:** Break the chain using intermediate variables or add helper methods:\r\n```python\r\n# Good: Use intermediate variables\r\naddress = user.address\r\ncity = address.city\r\npostal_code = city.postal_code\r\ncountry = postal_code.country\r\n\r\n# Or: Add a helper method\r\ncountry = user.get_country()\r\n```\r\n\r\n#### TC003: Excessive Interaction\r\nIdentifies classes with too many inter-class calls using call graph analysis.\r\n\r\n**Configuration:**\r\n- `max_inter_class_calls`: Maximum allowed unique method calls to other classes (default: 10)\r\n\r\n**Example Violation:**\r\n```python\r\nclass Client:\r\n    def __init__(self):\r\n        self.service_a = ServiceA()\r\n        self.service_b = ServiceB()\r\n        self.service_c = ServiceC()\r\n    \r\n    def do_work(self):\r\n        # Bad: Calls 12 unique methods from other classes (exceeds limit of 10)\r\n        self.service_a.method1()\r\n        self.service_a.method2()\r\n        self.service_a.method3()\r\n        self.service_a.method4()\r\n        self.service_b.method1()\r\n        self.service_b.method2()\r\n        self.service_b.method3()\r\n        self.service_b.method4()\r\n        self.service_c.method1()\r\n        self.service_c.method2()\r\n        self.service_c.method3()\r\n        self.service_c.method4()\r\n```\r\n\r\n**Fix:** Refactor to reduce coupling:\r\n```python\r\n# Good: Introduce a facade or coordinator\r\nclass ServiceCoordinator:\r\n    def __init__(self):\r\n        self.service_a = ServiceA()\r\n        self.service_b = ServiceB()\r\n        self.service_c = ServiceC()\r\n    \r\n    def execute_workflow(self):\r\n        # Encapsulates the complex interactions\r\n        self.service_a.do_work()\r\n        self.service_b.do_work()\r\n        self.service_c.do_work()\r\n\r\nclass Client:\r\n    def __init__(self):\r\n        self.coordinator = ServiceCoordinator()\r\n    \r\n    def do_work(self):\r\n        # Now only 1 inter-class call\r\n        self.coordinator.execute_workflow()\r\n```\r\n\r\n### MVBC Linter\r\n\r\n#### W2902: Illegal Layer Dependency\r\nEnforces proper layer communication in Model-View-Business-Controller architecture.\r\n\r\n**Allowed Dependencies:**\r\n- **Model**: Can be used by anyone (no dependencies on other layers)\r\n- **View**: Can use Model only\r\n- **Business**: Can use Model only  \r\n- **Controller**: Can use View, Business, and Model\r\n\r\n**Forbidden Dependencies:**\r\n- View \u2192 Business (must go through Controller)\r\n- Business \u2192 View (business logic shouldn't know about presentation)\r\n- View \u2192 Controller (creates circular dependency)\r\n- Business \u2192 Controller (creates circular dependency)\r\n\r\n**Configuration:**\r\nSupports both single-level and nested directory patterns:\r\n```toml\r\n[tool.vera_syntaxis.mvbc]\r\nmodel_paths = [\"models/*.py\"]          # Matches models/user.py and models/subdir/user.py\r\nview_paths = [\"views/**/*.py\"]         # Explicit nested pattern\r\nbusiness_paths = [\"business/*.py\"]\r\ncontroller_paths = [\"controllers/*.py\"]\r\n```\r\n\r\n**Example Violations:**\r\n\r\n*View \u2192 Business:*\r\n```python\r\n# In views/user_view.py\r\nfrom business.user_logic import UserLogic  # Bad: View importing Business\r\n\r\nclass UserView:\r\n    def __init__(self):\r\n        self.logic = UserLogic()\r\n```\r\n**Fix:** Use a Controller to mediate between View and Business.\r\n\r\n*Business \u2192 View:*\r\n```python\r\n# In business/user_logic.py\r\nfrom views.user_view import UserView  # Bad: Business importing View\r\n\r\nclass UserLogic:\r\n    def __init__(self):\r\n        self.view = UserView()\r\n```\r\n**Fix:** Keep business logic independent of presentation. Controllers should manage Views.\r\n\r\n### Circular Dependency Linter\r\n\r\n#### CD001: Circular Dependency\r\nDetects circular dependencies between modules.\r\n\r\n**Configuration:**\r\n- `allow_self_cycles`: Allow methods to call themselves (default: false)\r\n- `max_cycle_length`: Maximum cycle length to report (default: 10)\r\n\r\n**Example Violation:**\r\n```python\r\n# module_a.py\r\nfrom module_b import ClassB\r\n\r\nclass ClassA:\r\n    def method(self):\r\n        b = ClassB()\r\n\r\n# module_b.py\r\nfrom module_a import ClassA  # Circular dependency!\r\n\r\nclass ClassB:\r\n    def method(self):\r\n        a = ClassA()\r\n```\r\n\r\n**Fix:** Break the cycle using dependency inversion:\r\n```python\r\n# interface.py\r\nfrom abc import ABC, abstractmethod\r\n\r\nclass IProcessor(ABC):\r\n    @abstractmethod\r\n    def process(self): pass\r\n\r\n# module_a.py\r\nfrom interface import IProcessor\r\n\r\nclass ClassA(IProcessor):\r\n    def process(self):\r\n        pass\r\n\r\n# module_b.py\r\nfrom interface import IProcessor\r\n\r\nclass ClassB:\r\n    def __init__(self, processor: IProcessor):\r\n        self.processor = processor\r\n```\r\n\r\n### God Object Linter\r\n\r\n#### GO001: God Object\r\nDetects classes with too many responsibilities (God Objects).\r\n\r\n**Configuration:**\r\n- `max_methods`: Maximum number of methods allowed (default: 20)\r\n- `max_attributes`: Maximum number of attributes allowed (default: 15)\r\n- `max_lines`: Maximum number of lines allowed (default: 300)\r\n\r\n**Example Violation:**\r\n```python\r\nclass UserManager:  # God Object!\r\n    def __init__(self):\r\n        self.db = Database()\r\n        self.cache = Cache()\r\n        self.logger = Logger()\r\n        self.validator = Validator()\r\n        self.emailer = Emailer()\r\n        # ... 15+ attributes\r\n    \r\n    def create_user(self): pass\r\n    def update_user(self): pass\r\n    def delete_user(self): pass\r\n    def validate_email(self): pass\r\n    def send_welcome_email(self): pass\r\n    def log_activity(self): pass\r\n    # ... 20+ methods\r\n```\r\n\r\n**Fix:** Split into focused classes:\r\n```python\r\n# Separate concerns into focused classes\r\nclass UserRepository:\r\n    def __init__(self, db):\r\n        self.db = db\r\n    \r\n    def create(self, user): pass\r\n    def update(self, user): pass\r\n    def delete(self, user_id): pass\r\n\r\nclass UserValidator:\r\n    def validate_email(self, email): pass\r\n    def validate_password(self, password): pass\r\n\r\nclass UserNotifier:\r\n    def __init__(self, emailer):\r\n        self.emailer = emailer\r\n    \r\n    def send_welcome_email(self, user): pass\r\n    def send_password_reset(self, user): pass\r\n\r\nclass UserService:\r\n    def __init__(self, repo, validator, notifier):\r\n        self.repo = repo\r\n        self.validator = validator\r\n        self.notifier = notifier\r\n    \r\n    def create_user(self, user_data):\r\n        if self.validator.validate_email(user_data['email']):\r\n            user = self.repo.create(user_data)\r\n            self.notifier.send_welcome_email(user)\r\n            return user\r\n```\r\n\r\n### Feature Envy Linter\r\n\r\n#### FE001: Feature Envy\r\nDetects methods that use more features from another class than their own.\r\n\r\n**Configuration:**\r\n- `envy_threshold`: Ratio of external to total accesses that triggers envy (default: 0.5)\r\n- `min_accesses`: Minimum total accesses before checking (default: 3)\r\n\r\n**Example Violation:**\r\n```python\r\nclass Order:\r\n    def __init__(self):\r\n        self.customer = Customer()\r\n    \r\n    def validate_customer_email(self):  # Feature Envy!\r\n        # Uses customer features more than own features\r\n        email = self.customer.email\r\n        email = self.customer.email.strip()\r\n        email = self.customer.email.lower()\r\n        is_valid = '@' in self.customer.email\r\n        return is_valid\r\n```\r\n\r\n**Fix:** Move the method to the appropriate class:\r\n```python\r\nclass Customer:\r\n    def __init__(self):\r\n        self.email = \"\"\r\n    \r\n    def validate_email(self):\r\n        # Now uses own features\r\n        email = self.email.strip().lower()\r\n        return '@' in email\r\n\r\nclass Order:\r\n    def __init__(self):\r\n        self.customer = Customer()\r\n    \r\n    def process(self):\r\n        # Delegate to the appropriate class\r\n        if self.customer.validate_email():\r\n            # Process order\r\n            pass\r\n```\r\n\r\n### Data Clump Linter\r\n\r\n#### DC001: Data Clump\r\nDetects groups of parameters that frequently appear together.\r\n\r\n**Configuration:**\r\n- `min_clump_size`: Minimum number of parameters in a clump (default: 3)\r\n- `min_occurrences`: Minimum number of methods with the clump (default: 2)\r\n\r\n**Example Violation:**\r\n```python\r\nclass OrderProcessor:\r\n    def calculate_total(self, price, quantity, discount):  # Data Clump!\r\n        return price * quantity * (1 - discount)\r\n    \r\n    def validate_order(self, price, quantity, discount):  # Same parameters\r\n        return price > 0 and quantity > 0\r\n    \r\n    def format_order(self, price, quantity, discount):  # Same parameters\r\n        return f\"{quantity} items at ${price}\"\r\n```\r\n\r\n**Fix:** Extract data clump into a class:\r\n```python\r\nclass OrderDetails:\r\n    def __init__(self, price: float, quantity: int, discount: float):\r\n        self.price = price\r\n        self.quantity = quantity\r\n        self.discount = discount\r\n    \r\n    def calculate_total(self) -> float:\r\n        return self.price * self.quantity * (1 - self.discount)\r\n    \r\n    def validate(self) -> bool:\r\n        return self.price > 0 and self.quantity > 0\r\n    \r\n    def format(self) -> str:\r\n        return f\"{self.quantity} items at ${self.price}\"\r\n\r\nclass OrderProcessor:\r\n    def process_order(self, details: OrderDetails):\r\n        if details.validate():\r\n            total = details.calculate_total()\r\n            print(details.format())\r\n            return total\r\n```\r\n\r\n## Limitations\r\n\r\n- Requires extensive type hints for accurate analysis\r\n- Dynamic attributes (via `__getattr__`) may not be detected\r\n- Forward references must be properly annotated\r\n\r\n## Development Status\r\n\r\nVersion 0.1.0 - Alpha (Reporting Only)\r\n\r\n## License\r\n\r\nMIT License\r\n\r\n## Contributing\r\n\r\nContributions welcome! Please see CONTRIBUTING.md for guidelines.\r\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A static analysis tool for detecting architectural code smells in Python",
    "version": "0.1.0",
    "project_urls": {
        "Bug Tracker": "https://github.com/yourusername/vera-syntaxis/issues",
        "Homepage": "https://github.com/yourusername/vera-syntaxis"
    },
    "split_keywords": [
        "linter",
        " architecture",
        " code-quality",
        " static-analysis",
        " design-patterns"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "233d549f7989e8d807a9cf5abbbbff7cba2e76efdefb22899e1c16f966a2755a",
                "md5": "3c8c67255b04b24f3850fd32466896d6",
                "sha256": "f4b656399e50f18e68cea43b487fd6971ff9a6f8fd94860f1298775454817e19"
            },
            "downloads": -1,
            "filename": "vera_syntaxis-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "3c8c67255b04b24f3850fd32466896d6",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 74682,
            "upload_time": "2025-10-10T21:21:20",
            "upload_time_iso_8601": "2025-10-10T21:21:20.231506Z",
            "url": "https://files.pythonhosted.org/packages/23/3d/549f7989e8d807a9cf5abbbbff7cba2e76efdefb22899e1c16f966a2755a/vera_syntaxis-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "6d8958406268bb24e4b8c7befd1e2451f3263449c9a7cff5e75a734173baf4fb",
                "md5": "2a0681a6d75d8bd75103aaa4edf7dd02",
                "sha256": "aa2b25b8fbd67ccad8dfdc943f4e4efea6b8e3c41ca1601763128fc73e5aad8e"
            },
            "downloads": -1,
            "filename": "vera_syntaxis-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "2a0681a6d75d8bd75103aaa4edf7dd02",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 78417,
            "upload_time": "2025-10-10T21:21:21",
            "upload_time_iso_8601": "2025-10-10T21:21:21.640158Z",
            "url": "https://files.pythonhosted.org/packages/6d/89/58406268bb24e4b8c7befd1e2451f3263449c9a7cff5e75a734173baf4fb/vera_syntaxis-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-10 21:21:21",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "yourusername",
    "github_project": "vera-syntaxis",
    "github_not_found": true,
    "lcname": "vera-syntaxis"
}
        
Elapsed time: 3.32249s