Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .commitlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"always",
[
"build",
"chore",
"chore",
"ci",
"docs",
"feat",
Expand Down
29 changes: 29 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
exclude: ^mkdocs\.yml$
- id: check-added-large-files
- id: check-toml

- repo: https://github.com/psf/black
rev: 23.12.1
hooks:
- id: black
language_version: python3.12

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.9
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]

- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.11.0
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ['@commitlint/cli@^18.6.1', '@commitlint/config-conventional@^18.6.2']
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,4 @@ For detailed guidelines, see [CONTRIBUTING.md](./CONTRIBUTING.md).

## License

AGPL v3: see [file](./LICENSE)
AGPL v3: see [file](./LICENSE)
40 changes: 20 additions & 20 deletions docs/articles/devs/architecture/ChainOfResponsibilityPattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,17 @@ All component types follow the same abstract base class pattern (replace `Compon
class ComponentBase(ABC):
def __init__(self, next_component: Optional['ComponentBase'] = None):
self.next_component = next_component

@abstractmethod
def can_handle(self, version_or_data) -> bool:
"""Determine if this component can handle the given version/data."""
pass

def set_next(self, component: 'ComponentBase') -> 'ComponentBase':
"""Set the next component in the chain."""
self.next_component = component
return component

# Delegation methods follow the same pattern:
def operation(self, data):
"""Perform operation or delegate to next component."""
Expand All @@ -117,30 +117,30 @@ All component types use identical factory patterns for chain construction (repla
class ComponentFactory:
_component_registry: Dict[str, Type[ComponentBase]] = {}
_version_order: List[str] = [] # Newest to oldest

@classmethod
def create_component_chain(cls, target_version: Optional[str] = None) -> ComponentBase:
"""Create component chain from target version to oldest."""

# Determine target version (latest if not specified)
if target_version is None:
target_version = cls._version_order[0]

# Create chain starting from target version down to oldest
target_index = cls._version_order.index(target_version)
chain_versions = cls._version_order[target_index:]

# Create components in order (newest to oldest)
components = []
for version in chain_versions:
component_class = cls._component_registry[version]
component = component_class()
components.append(component)

# Link components (each points to the next older one)
for i in range(len(components) - 1):
components[i].set_next(components[i + 1])

return components[0] # Return head of chain
```

Expand All @@ -155,15 +155,15 @@ Each component type implements delegation for unchanged concerns:
```python
class V121PackageAccessor(HatchPkgAccessorBase):
"""v1.2.1 accessor - handles dual entry points, delegates dependencies."""

def get_entry_points(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""Handle dual entry point access for v1.2.1."""
# v1.2.1-specific logic for dual entry points
return {
'mcp_server': metadata.get('mcp_server', {}),
'hatch_mcp_server': metadata.get('hatch_mcp_server', {})
}

def get_dependencies(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""Delegate dependency access to v1.2.0."""
if self.next_accessor:
Expand All @@ -172,12 +172,12 @@ class V121PackageAccessor(HatchPkgAccessorBase):

class V120PackageAccessor(HatchPkgAccessorBase):
"""v1.2.0 accessor - handles unified dependencies, delegates basic fields."""

def get_dependencies(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""Handle unified dependency structure for v1.2.0."""
# v1.2.0-specific logic for unified dependencies
return metadata.get('dependencies', {})

def get_name(self, metadata: Dict[str, Any]) -> str:
"""Delegate basic field access to v1.1.0."""
if self.next_accessor:
Expand Down Expand Up @@ -223,11 +223,11 @@ class ValidatorFactory:
# Import and register v1.2.1 validator (newest first)
from hatch_validator.package.v1_2_1.validator import Validator as V121Validator
cls.register_validator('1.2.1', V121Validator)

# Import and register v1.2.0 validator
from hatch_validator.package.v1_2_0.validator import Validator as V120Validator
cls.register_validator('1.2.0', V120Validator)

# Import and register v1.1.0 validator
from hatch_validator.package.v1_1_0.validator import Validator as V110Validator
cls.register_validator('1.1.0', V110Validator)
Expand Down Expand Up @@ -362,14 +362,14 @@ To add support for a new schema version (e.g., v1.3.0):
```python
class V130PackageAccessor(HatchPkgAccessorBase):
"""v1.3.0 accessor - handles new features, delegates unchanged concerns."""

def can_handle(self, schema_version: str) -> bool:
return schema_version == "1.3.0"

def get_new_feature(self, metadata: Dict[str, Any]) -> Any:
"""Handle v1.3.0-specific new feature."""
return metadata.get('new_feature', {})

# All others are automatically delegated from HatchPkgAccessorBase implementation
```

Expand Down Expand Up @@ -423,11 +423,11 @@ def operation(self, data):
return self._handle_operation(data)
except Exception as e:
logger.warning(f"Component {self.__class__.__name__} failed: {e}")

# Delegate to next component
if self.next_component:
return self.next_component.operation(data)

# No component could handle the operation
raise NotImplementedError(f"No component can handle operation for {data}")
```
Expand Down
36 changes: 18 additions & 18 deletions docs/articles/devs/architecture/ComponentTypes.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ Package Accessors provide unified access to package metadata across schema versi
class HatchPkgAccessor(ABC):
def __init__(self, next_accessor: Optional['HatchPkgAccessor'] = None):
self.next_accessor = next_accessor

@abstractmethod
def can_handle(self, schema_version: str) -> bool:
"""Determine if this accessor can handle the schema version."""
pass

def get_dependencies(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""Get dependencies or delegate to next accessor."""
if self.next_accessor:
Expand Down Expand Up @@ -147,12 +147,12 @@ Registry Accessors enable consistent registry data access regardless of registry
class RegistryAccessorBase(ABC):
def __init__(self, successor: Optional['RegistryAccessorBase'] = None):
self._successor = successor

@abstractmethod
def can_handle(self, registry_data: Dict[str, Any]) -> bool:
"""Check if this accessor can handle the registry data."""
pass

def handle_request(self, registry_data: Dict[str, Any]) -> Optional['RegistryAccessorBase']:
"""Handle request using chain of responsibility pattern."""
if self.can_handle(registry_data):
Expand Down Expand Up @@ -189,7 +189,7 @@ class PackageService:
# Create package accessor chain
schema_version = metadata.get("package_schema_version")
self._accessor = HatchPkgAccessorFactory.create_accessor_chain(schema_version)

def get_dependencies(self) -> Dict[str, Any]:
"""Use accessor chain for version-agnostic dependency access."""
return self._accessor.get_dependencies(self._metadata)
Expand All @@ -204,7 +204,7 @@ class RegistryService:
if registry_data:
# Create registry accessor chain
self._accessor = RegistryAccessorFactory.create_accessor_for_data(registry_data)

def package_exists(self, package_name: str) -> bool:
"""Use accessor chain for version-agnostic registry operations."""
return self._accessor.package_exists(self._registry_data, package_name)
Expand All @@ -216,20 +216,20 @@ class RegistryService:
class HatchPackageValidator:
def validate_package(self, package_path: Path) -> Tuple[bool, Dict[str, Any]]:
"""Use validator chain for version-agnostic validation."""

# Load metadata and detect schema version
with open(package_path / "hatch_metadata.json", 'r') as f:
metadata = json.load(f)

schema_version = metadata.get("package_schema_version")

# Create validator chain
validator = ValidatorFactory.create_validator_chain(schema_version)

# Execute validation through chain
context = ValidationContext(registry_data=self.registry_data)
is_valid, errors = validator.validate(metadata, context)

return is_valid, self._format_results(is_valid, errors, metadata)
```

Expand All @@ -246,16 +246,16 @@ class DependencyValidation:
# Use package accessor for version-agnostic dependency access
package_service = PackageService(metadata)
dependencies = package_service.get_dependencies()

# Use registry accessor for dependency existence validation
registry_service = RegistryService(context.registry_data)

errors = []
for dep_type, deps in dependencies.items():
for dep in deps:
if not registry_service.package_exists(dep['name']):
errors.append(f"Dependency {dep['name']} not found in registry")

return len(errors) == 0, errors
```

Expand All @@ -271,18 +271,18 @@ Factory classes coordinate component creation:
# Coordinated factory usage
def create_validation_system(metadata: Dict[str, Any], registry_data: Dict[str, Any]):
"""Create coordinated validation system with all component types."""

schema_version = metadata.get("package_schema_version")

# Create coordinated chains
validator = ValidatorFactory.create_validator_chain(schema_version)
package_accessor = HatchPkgAccessorFactory.create_accessor_chain(schema_version)
registry_accessor = RegistryAccessorFactory.create_accessor_for_data(registry_data)

# Create coordinated services
package_service = PackageService(metadata)
registry_service = RegistryService(registry_data)

return {
'validator': validator,
'package_service': package_service,
Expand Down
Loading
Loading