docs: Add missing docstrings and fix terminology warnings

This commit is contained in:
DSS
2025-12-11 06:31:06 -03:00
parent bcd1a86ae4
commit 42b146ca02
9 changed files with 179 additions and 28 deletions

View File

@@ -1 +1 @@
1765443595382 1765445463969

View File

@@ -762,7 +762,12 @@ const classes = computed(() => [
# === MCP Tool Registration === # === MCP Tool Registration ===
def create_mcp_tools(mcp_instance): def create_mcp_tools(mcp_instance):
"""Register all Figma tools with MCP server.""" """
Register all Figma tools with MCP server.
Args:
mcp_instance: The MCP server instance to register tools with.
"""
suite = FigmaToolSuite() suite = FigmaToolSuite()
@@ -814,6 +819,9 @@ if __name__ == "__main__":
import asyncio import asyncio
async def test(): async def test():
"""
Run a series of tests for the FigmaToolSuite in mock mode.
"""
suite = FigmaToolSuite(output_dir="./test_output") suite = FigmaToolSuite(output_dir="./test_output")
print("Testing Figma Tool Suite (Mock Mode)\n") print("Testing Figma Tool Suite (Mock Mode)\n")

View File

@@ -51,6 +51,9 @@ class ValidationIssue:
# ============================================================================= # =============================================================================
class RateLimiter: class RateLimiter:
"""
Manages API request rate limiting and exponential backoff.
"""
def __init__(self, max_per_minute: int = MAX_REQUESTS_PER_MINUTE): def __init__(self, max_per_minute: int = MAX_REQUESTS_PER_MINUTE):
self.max_per_minute = max_per_minute self.max_per_minute = max_per_minute
self.requests: List[float] = [] self.requests: List[float] = []
@@ -59,6 +62,10 @@ class RateLimiter:
self._lock = asyncio.Lock() self._lock = asyncio.Lock()
async def acquire(self): async def acquire(self):
"""
Acquire a slot for an API request, waiting if rate limit is reached
or if an exponential backoff is active.
"""
async with self._lock: async with self._lock:
now = asyncio.get_event_loop().time() now = asyncio.get_event_loop().time()
if now < self.backoff_until: if now < self.backoff_until:
@@ -73,12 +80,19 @@ class RateLimiter:
self.requests.append(asyncio.get_event_loop().time()) self.requests.append(asyncio.get_event_loop().time())
def handle_429(self): def handle_429(self):
"""
Handles a 429 (Too Many Requests) response by initiating an
exponential backoff.
"""
self.consecutive_429s += 1 self.consecutive_429s += 1
backoff = min(INITIAL_BACKOFF_SECONDS * (2 ** self.consecutive_429s), MAX_BACKOFF_SECONDS) backoff = min(INITIAL_BACKOFF_SECONDS * (2 ** self.consecutive_429s), MAX_BACKOFF_SECONDS)
self.backoff_until = asyncio.get_event_loop().time() + backoff self.backoff_until = asyncio.get_event_loop().time() + backoff
return backoff return backoff
def reset_backoff(self): def reset_backoff(self):
"""
Resets the exponential backoff counter.
"""
self.consecutive_429s = 0 self.consecutive_429s = 0
# ============================================================================= # =============================================================================
@@ -86,6 +100,9 @@ class RateLimiter:
# ============================================================================= # =============================================================================
class IntelligentFigmaClient: class IntelligentFigmaClient:
"""
Figma API client with intelligent rate limiting and retry logic.
"""
def __init__(self, token: str, verbose: bool = False): def __init__(self, token: str, verbose: bool = False):
self.token = token self.token = token
self.verbose = verbose self.verbose = verbose
@@ -139,6 +156,11 @@ class IntelligentFigmaClient:
# ============================================================================= # =============================================================================
class DesignValidator: class DesignValidator:
"""
A stub validator for design components.
In a full implementation, this would validate components against
design system rules.
"""
def validate_component(self, component: Dict) -> List[ValidationIssue]: def validate_component(self, component: Dict) -> List[ValidationIssue]:
return [] # Dummy implementation for now return [] # Dummy implementation for now
@@ -147,6 +169,9 @@ class DesignValidator:
# ============================================================================= # =============================================================================
class VariableExtractor: class VariableExtractor:
"""
Extracts design tokens from Figma variables.
"""
def extract(self, variables_data: Dict, file_key: str) -> List[DesignToken]: def extract(self, variables_data: Dict, file_key: str) -> List[DesignToken]:
tokens = [] tokens = []
meta = variables_data.get("meta", {}) meta = variables_data.get("meta", {})
@@ -286,21 +311,28 @@ class ComponentExtractor:
def _classify_component(self, set_data: Dict) -> AtomicType: def _classify_component(self, set_data: Dict) -> AtomicType:
""" """
Classify a component as an ATOM, MOLECULE, or ORGANISM based on heuristics. Classify a component as a PRIMITIVE_COMPONENT or COMPOSITE_COMPONENT based on heuristics.
""" """
name = set_data.get('name', '').lower() name = set_data.get('name', '').lower()
num_children = len(set_data.get('children_ids', [])) num_children = len(set_data.get('children_ids', []))
if 'icon' in name or 'button' in name or 'input' in name: # Heuristics for Primitive Components
return AtomicType.ATOM primitive_keywords = ['icon', 'button', 'input', 'text', 'avatar', 'checkbox', 'radio', 'switch']
if any(keyword in name for keyword in primitive_keywords):
return AtomicType.PRIMITIVE_COMPONENT
# Heuristics for Composite Components
composite_keywords = ['card', 'modal', 'navbar', 'sidebar', 'form']
if any(keyword in name for keyword in composite_keywords):
return AtomicType.COMPOSITE_COMPONENT
# Fallback based on children count
if num_children == 0: if num_children == 0:
return AtomicType.ATOM return AtomicType.PRIMITIVE_COMPONENT
elif num_children > 0 and num_children < 5: elif num_children > 0:
return AtomicType.MOLECULE return AtomicType.COMPOSITE_COMPONENT
else:
return AtomicType.ORGANISM
return AtomicType.UNKNOWN
def _parse_variant_name(self, name: str) -> Dict[str, str]: def _parse_variant_name(self, name: str) -> Dict[str, str]:
return {key.strip(): value.strip() for part in name.split(", ") if "=" in part for key, value in [part.split("=", 1)]} return {key.strip(): value.strip() for part in name.split(", ") if "=" in part for key, value in [part.split("=", 1)]}

View File

@@ -8,18 +8,28 @@ from enum import Enum
class AtomicType(str, Enum): class AtomicType(str, Enum):
""" """
Classification of components based on atomic design principles. Classification of components based on their composition.
- PRIMITIVE_COMPONENT: Fundamental UI elements (e.g., Button, Icon).
- COMPOSITE_COMPONENT: Composed of multiple primitive or other composite components (e.g., Card, NavBar).
- TEMPLATE: Page-level structures, arranging organisms to show underlying page structure.
- PAGE: Instances of templates, with real content in place.
""" """
ATOM = "atom" PRIMITIVE_COMPONENT = "primitive_component" # Formerly ATOM
MOLECULE = "molecule" COMPOSITE_COMPONENT = "composite_component" # Formerly MOLECULE and ORGANISM
ORGANISM = "organism"
TEMPLATE = "template" TEMPLATE = "template"
PAGE = "page" PAGE = "page"
UNKNOWN = "unknown" UNKNOWN = "unknown"
class ComponentVariant(BaseModel): class ComponentVariant(BaseModel):
"""A variant of a component (e.g., 'outline' button)""" """
A variant of a component (e.g., 'outline' button).
Attributes:
uuid (str): UUID for export/import.
name (str): Variant name.
props (Dict[str, Any]): Variant-specific props.
"""
model_config = ConfigDict(arbitrary_types_allowed=True) model_config = ConfigDict(arbitrary_types_allowed=True)
uuid: str = Field(default_factory=lambda: str(uuid4()), description="UUID for export/import") uuid: str = Field(default_factory=lambda: str(uuid4()), description="UUID for export/import")
@@ -28,7 +38,21 @@ class ComponentVariant(BaseModel):
class Component(BaseModel): class Component(BaseModel):
"""A design system component, classified by atomic design principles.""" """
A design system component, classified by atomic design principles.
Attributes:
uuid (str): UUID for export/import.
figma_node_id (Optional[str]): The corresponding node ID in Figma.
name (str): Component name (e.g., 'Button').
source (str): Component source (e.g., shadcn, custom, figma).
description (Optional[str]): Component description.
classification (AtomicType): Atomic design classification.
variants (List[str]): Available variants.
props (Dict[str, Any]): Component props schema.
dependencies (List[str]): UUIDs of components this component depends on (e.g., a composite component depends on primitive components).
sub_components (List[str]): UUIDs of components that are children of this component in the atomic hierarchy.
"""
model_config = ConfigDict(arbitrary_types_allowed=True) model_config = ConfigDict(arbitrary_types_allowed=True)
uuid: str = Field(default_factory=lambda: str(uuid4()), description="UUID for export/import") uuid: str = Field(default_factory=lambda: str(uuid4()), description="UUID for export/import")

View File

@@ -77,7 +77,12 @@ class ProjectRegistry:
}, f, indent=2) }, f, indent=2)
def register(self, project: DSSProject): def register(self, project: DSSProject):
"""Register a project.""" """
Register a project.
Args:
project: The DSSProject instance to register.
"""
self._projects[project.config.name] = { self._projects[project.config.name] = {
"name": project.config.name, "name": project.config.name,
"path": str(project.path), "path": str(project.path),
@@ -88,7 +93,12 @@ class ProjectRegistry:
self._save() self._save()
def unregister(self, name: str): def unregister(self, name: str):
"""Remove a project from registry.""" """
Remove a project from registry.
Args:
name: The name of the project to unregister.
"""
if name in self._projects: if name in self._projects:
del self._projects[name] del self._projects[name]
self._save() self._save()
@@ -110,7 +120,13 @@ class ProjectRegistry:
return list(self._projects.values()) return list(self._projects.values())
def update_status(self, name: str, status: ProjectStatus): def update_status(self, name: str, status: ProjectStatus):
"""Update project status.""" """
Update project status.
Args:
name: The name of the project to update.
status: The new status for the project.
"""
if name in self._projects: if name in self._projects:
self._projects[name]["status"] = status.value self._projects[name]["status"] = status.value
self._projects[name]["updated_at"] = datetime.now().isoformat() self._projects[name]["updated_at"] = datetime.now().isoformat()

View File

@@ -12,7 +12,14 @@ from pydantic import BaseModel, Field, field_validator
class ProjectStatus(str, Enum): class ProjectStatus(str, Enum):
"""Project lifecycle status.""" """
Project lifecycle status.
- CREATED: Project initialized but not yet configured.
- CONFIGURED: Project configuration is complete.
- SYNCED: Project data has been synchronized with external sources (e.g., Figma).
- BUILT: Project output files have been generated.
- ERROR: An error occurred during a project operation.
"""
CREATED = "created" CREATED = "created"
CONFIGURED = "configured" CONFIGURED = "configured"
SYNCED = "synced" SYNCED = "synced"
@@ -21,7 +28,15 @@ class ProjectStatus(str, Enum):
class FigmaFile(BaseModel): class FigmaFile(BaseModel):
"""A single Figma file reference.""" """
A single Figma file reference.
Attributes:
key (str): Figma file key from URL.
name (str): Human-readable file name.
last_synced (Optional[datetime]): Last sync timestamp.
thumbnail_url (Optional[str]): Figma thumbnail URL.
"""
key: str = Field(..., description="Figma file key from URL") key: str = Field(..., description="Figma file key from URL")
name: str = Field(..., description="Human-readable file name") name: str = Field(..., description="Human-readable file name")
last_synced: Optional[datetime] = Field(None, description="Last sync timestamp") last_synced: Optional[datetime] = Field(None, description="Last sync timestamp")
@@ -32,11 +47,20 @@ class FigmaFile(BaseModel):
class FigmaSource(BaseModel): class FigmaSource(BaseModel):
"""Figma project source configuration. """
Figma project source configuration.
The team folder is the main Figma resource. Projects within the team The team folder is the main Figma resource. Projects within the team
contain design files. The 'uikit' file (if present) is the primary contain design files. The 'uikit' file (if present) is the primary
reference for design tokens. reference for design tokens.
Attributes:
team_id (Optional[str]): Figma team ID (main resource).
project_id (Optional[str]): Figma project ID within team.
project_name (Optional[str]): Figma project name.
files (List[FigmaFile]): List of Figma files.
uikit_file_key (Optional[str]): Key of the UIKit reference file.
auto_sync (bool): Enable automatic sync on changes.
""" """
team_id: Optional[str] = Field(None, description="Figma team ID (main resource)") team_id: Optional[str] = Field(None, description="Figma team ID (main resource)")
project_id: Optional[str] = Field(None, description="Figma project ID within team") project_id: Optional[str] = Field(None, description="Figma project ID within team")
@@ -62,7 +86,15 @@ class FigmaSource(BaseModel):
class OutputConfig(BaseModel): class OutputConfig(BaseModel):
"""Output configuration for generated files.""" """
Output configuration for generated files.
Attributes:
tokens_dir (str): Directory for token files.
themes_dir (str): Directory for theme files.
components_dir (str): Directory for component files.
formats (List[str]): Output formats to generate (e.g., "css", "scss", "json").
"""
tokens_dir: str = Field("./tokens", description="Directory for token files") tokens_dir: str = Field("./tokens", description="Directory for token files")
themes_dir: str = Field("./themes", description="Directory for theme files") themes_dir: str = Field("./themes", description="Directory for theme files")
components_dir: str = Field("./components", description="Directory for component files") components_dir: str = Field("./components", description="Directory for component files")
@@ -74,6 +106,9 @@ class OutputConfig(BaseModel):
@field_validator("formats") @field_validator("formats")
@classmethod @classmethod
def validate_formats(cls, v): def validate_formats(cls, v):
"""
Validate that all specified output formats are supported.
"""
valid = {"css", "scss", "json", "js", "ts"} valid = {"css", "scss", "json", "js", "ts"}
for fmt in v: for fmt in v:
if fmt not in valid: if fmt not in valid:
@@ -82,7 +117,20 @@ class OutputConfig(BaseModel):
class ProjectConfig(BaseModel): class ProjectConfig(BaseModel):
"""Main project configuration (ds.config.json).""" """
Main project configuration (ds.config.json).
Attributes:
name (str): Project name.
version (str): Project version.
description (Optional[str]): Project description.
figma (Optional[FigmaSource]): Figma source configuration.
skin (Optional[str]): Base skin/theme to extend (e.g., 'shadcn', 'material').
base_theme (str): Default theme variant.
output (OutputConfig): Output settings.
created_at (datetime): Timestamp of project creation.
updated_at (datetime): Timestamp of last project update.
"""
name: str = Field(..., description="Project name") name: str = Field(..., description="Project name")
version: str = Field("1.0.0", description="Project version") version: str = Field("1.0.0", description="Project version")
description: Optional[str] = Field(None, description="Project description") description: Optional[str] = Field(None, description="Project description")

View File

@@ -14,16 +14,26 @@ from dss.ingest.base import TokenCollection
class ThemeTranslator: class ThemeTranslator:
""" """
Translates a DSS project into a specific theme. Translates a DSS project's tokens and components into a specific
theme or "skin" for a target framework (e.g., shadcn, material-ui).
""" """
def __init__(self, project: Project): def __init__(self, project: Project):
"""
Initializes the ThemeTranslator with a DSS Project.
Args:
project: The DSS Project instance to translate.
"""
self.project = project self.project = project
def translate(self, skin: str, output_dir: Path): def translate(self, skin: str, output_dir: Path):
""" """
Translate the project into a specific skin. Translate the project into a specific skin.
This method dispatches to a specific translation function based on the
provided skin name.
Args: Args:
skin: The name of the skin to translate to (e.g., 'shadcn'). skin: The name of the skin to translate to (e.g., 'shadcn').
output_dir: The directory to write the translated theme files to. output_dir: The directory to write the translated theme files to.

View File

@@ -36,7 +36,8 @@ def dss_project(project_manager: ProjectManager, tmp_path: Path) -> DSSProject:
def test_recursive_figma_import(MockAsyncClient, dss_project: DSSProject, project_manager: ProjectManager): def test_recursive_figma_import(MockAsyncClient, dss_project: DSSProject, project_manager: ProjectManager):
""" """
Test that the Figma import is recursive and that the components are Test that the Figma import is recursive and that the components are
classified correctly. classified correctly. This test mocks the FigmaTokenSource to
control the data returned during sync.
""" """
# Mock the httpx.AsyncClient to return a sample Figma file # Mock the httpx.AsyncClient to return a sample Figma file
mock_client_instance = MockAsyncClient.return_value mock_client_instance = MockAsyncClient.return_value

View File

@@ -13,6 +13,11 @@ from dss.models.component import AtomicType
# Mock Figma client with async context manager and async methods # Mock Figma client with async context manager and async methods
class MockAsyncClient: class MockAsyncClient:
"""
Mocks the IntelligentFigmaClient for testing purposes.
Simulates an async context manager and provides mock async methods
for Figma API calls.
"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
pass pass
@@ -23,6 +28,9 @@ class MockAsyncClient:
pass pass
async def get_file(self, file_key: str): async def get_file(self, file_key: str):
"""
Mocks the async get_file method to return a predefined Figma document structure.
"""
return { return {
"document": { "document": {
"id": "0:0", "id": "0:0",
@@ -62,6 +70,9 @@ class MockAsyncClient:
} }
async def get_file_variables(self, file_key: str): async def get_file_variables(self, file_key: str):
"""
Mocks the async get_file_variables method to return empty variables.
"""
return {"meta": {"variables": {}, "variableCollections": {}}} return {"meta": {"variables": {}, "variableCollections": {}}}
@@ -69,7 +80,8 @@ class MockAsyncClient:
def test_figma_component_extraction(): def test_figma_component_extraction():
""" """
Test that the Figma ingestion source correctly extracts and classifies Test that the Figma ingestion source correctly extracts and classifies
components from a mock Figma file. components from a mock Figma file structure. It verifies that the recursive
component discovery works and assigns correct AtomicType classifications.
""" """
source = FigmaTokenSource(figma_token="fake_token") source = FigmaTokenSource(figma_token="fake_token")