all2md.transforms.metadata
Metadata classes for AST transforms.
This module defines metadata structures for transform registration and discovery. Transform metadata enables CLI argument generation, validation, and plugin discovery through entry points.
Examples
Define a transform with metadata:
>>> from all2md.transforms import TransformMetadata, ParameterSpec
>>> from all2md.ast.transforms import NodeTransformer
>>>
>>> class MyTransform(NodeTransformer):
... def __init__(self, threshold: int = 10):
... self.threshold = threshold
...
>>> METADATA = TransformMetadata(
... name="my-transform",
... description="Example transform",
... transformer_class=MyTransform,
... parameters={
... 'threshold': ParameterSpec(
... type=int,
... default=10,
... help="Threshold value"
... )
... }
... )
- class all2md.transforms.metadata.ParameterSpec
Bases:
objectSpecification for a transform parameter.
This class describes a single parameter accepted by a transform, including type information, default values, and metadata for CLI generation.
- Parameters:
type (type) – Python type of the parameter (e.g., int, str, bool)
default (Any, optional) – Default value if parameter is not provided
help (str, optional) – Help text describing the parameter (used in CLI –help)
cli_flag (str, optional) – Custom CLI flag name (e.g., ‘–my-param’). If None, auto-generated from parameter name
required (bool, default = False) – Whether this parameter is required
choices (list, optional) – List of valid choices for this parameter
validator (callable, optional) – Custom validation function: takes value, returns bool or raises ValueError
element_type (type, optional) – For list parameters, the expected type of list elements (e.g., str, int)
expose (bool, optional) – Whether to expose this parameter on the CLI when no explicit
cli_flagis provided.Nonedefers to global defaults (currentlyFalse).
Examples
- Simple parameter:
>>> param = ParameterSpec(type=int, default=10, help="Threshold value")
- Parameter with choices:
>>> param = ParameterSpec( ... type=str, ... default="auto", ... choices=["auto", "manual", "disabled"], ... help="Processing mode" ... )
- Required parameter with validation:
>>> def validate_positive(value): ... if value <= 0: ... raise ValueError("Must be positive") ... return True >>> param = ParameterSpec( ... type=int, ... required=True, ... validator=validate_positive, ... help="Positive integer" ... )
- List parameter with element type validation:
>>> param = ParameterSpec( ... type=list, ... element_type=str, ... default=["image", "table"], ... help="Node types to remove" ... )
- type: Type
- default: Any = None
- help: str = ''
- cli_flag: str | None = None
- required: bool = False
- choices: list[Any] | None = None
- validator: Callable[[Any], bool] | None = None
- element_type: Type | None = None
- expose: bool | None = None
- DEFAULT_EXPOSE: ClassVar[bool] = False
- validate(value: Any) bool
Validate a parameter value.
- Parameters:
value (Any) – Value to validate. For list types, tuples are accepted and coerced to lists automatically.
- Returns:
True if valid
- Return type:
bool
- Raises:
ValueError – If value is invalid
Notes
When validating list parameters, this method accepts both list and tuple types. Tuples are automatically coerced to lists to accommodate CLI parsers that often yield tuples. The coercion is transparent to the caller.
- get_cli_flag(param_name: str) str
Get CLI flag name for this parameter.
- Parameters:
param_name (str) – Parameter name from the transform
- Returns:
CLI flag (e.g., ‘–threshold’)
- Return type:
str
- should_expose(default: bool | None = None) bool
Determine whether this parameter should surface in the CLI.
- get_dest_name(param_name: str, transform_name: str) str
Get argparse dest name for this parameter.
This provides a consistent naming convention for transform parameters in the argparse namespace, avoiding conflicts between transforms.
- Parameters:
param_name (str) – Parameter name from the transform
transform_name (str) – Name of the transform
- Returns:
Destination name for argparse (e.g., ‘heading_offset_transform_offset’)
- Return type:
str
Notes
The dest name is constructed to avoid collisions: - Format: f’{transform_name}_{param_name}’ - Hyphens converted to underscores for valid Python identifiers - Example: ‘heading-offset’ transform, ‘offset’ param -> ‘heading_offset_offset’
- get_argparse_kwargs(param_name: str, transform_name: str) dict
Generate argparse kwargs for this parameter.
This centralizes the logic for converting ParameterSpec to argparse add_argument() kwargs, ensuring consistency between CLI argument definition and parameter extraction.
- Parameters:
param_name (str) – Parameter name from the transform
transform_name (str) – Name of the transform (for help text)
- Returns:
Keyword arguments for argparse.ArgumentParser.add_argument()
- Return type:
dict
Notes
The returned dict includes: - ‘action’: Tracking action class (TrackingStoreAction, etc.) - ‘type’: Python type for conversion (if applicable) - ‘default’: Default value (if applicable) - ‘help’: Help text - ‘choices’: Valid choices (if specified) - ‘nargs’: Argument count (for list types) - ‘dest’: Destination name in namespace
Examples
>>> param = ParameterSpec(type=int, default=10, help="Threshold") >>> kwargs = param.get_argparse_kwargs('threshold', 'my-transform') >>> # Returns: {'action': TrackingStoreAction, 'type': int, >>> # 'default': 10, 'help': 'Threshold', 'dest': 'my_transform_threshold'}
- extract_value(namespace: Any, dest: str) tuple[Any, bool]
Extract parameter value from parsed argparse namespace.
This handles extracting the value and determining if it was explicitly provided by the user (vs. being a default value).
- Parameters:
namespace (argparse.Namespace) – Parsed command line arguments
dest (str) – Destination name in the namespace (from get_dest_name())
- Returns:
Tuple of (value, was_provided) where: - value: The parameter value (or None if not provided) - was_provided: True if user explicitly provided this value
- Return type:
tuple[Any, bool]
Notes
This method checks the _provided_args set in the namespace to determine if a value was explicitly provided by the user. Only explicitly provided values should be passed to transform constructors.
Examples
>>> namespace = argparse.Namespace( ... my_transform_threshold=20, ... _provided_args={'my_transform_threshold'} ... ) >>> param = ParameterSpec(type=int, default=10) >>> value, provided = param.extract_value(namespace, 'my_transform_threshold') >>> # Returns: (20, True)
- __init__(type: Type, default: Any = None, help: str = '', cli_flag: str | None = None, required: bool = False, choices: list[Any] | None = None, validator: Callable[[Any], bool] | None = None, element_type: Type | None = None, expose: bool | None = None) None
- class all2md.transforms.metadata.TransformMetadata
Bases:
objectMetadata for a transform.
This class describes a transform for registration, discovery, and CLI integration. It follows the same pattern as ConverterMetadata for consistency.
- Parameters:
name (str) – Unique identifier for the transform (e.g., “remove-images”)
description (str) – Human-readable description of what the transform does
transformer_class (type[NodeTransformer]) – The transform class (must inherit from NodeTransformer)
parameters (dict[str, ParameterSpec], default = empty dict) – Parameters accepted by the transform constructor
priority (int, default = 100) – Execution priority (lower runs first). Used for dependency ordering
dependencies (list[str], default = empty list) – Names of transforms that must run before this one
version (str, default = "1.0.0") – Transform version (semantic versioning)
author (str, optional) – Transform author or maintainer
tags (list[str], default = empty list) – Tags for categorization (e.g., [“images”, “cleanup”])
Examples
- Basic transform metadata:
>>> metadata = TransformMetadata( ... name="remove-images", ... description="Remove all image nodes from the AST", ... transformer_class=RemoveImagesTransform ... )
- Transform with parameters:
>>> metadata = TransformMetadata( ... name="heading-offset", ... description="Shift heading levels by an offset", ... transformer_class=HeadingOffsetTransform, ... parameters={ ... 'offset': ParameterSpec( ... type=int, ... default=1, ... help="Number of levels to shift (positive or negative)" ... ) ... } ... )
- Transform with dependencies:
>>> metadata = TransformMetadata( ... name="sanitize-links", ... description="Sanitize and validate all links", ... transformer_class=SanitizeLinksTransform, ... dependencies=["extract-metadata"], ... priority=200 ... )
- name: str
- description: str
- transformer_class: Type[NodeTransformer]
- parameters: dict[str, ParameterSpec]
- priority: int = 100
- dependencies: list[str]
- version: str = '1.0.0'
- author: str | None = None
- tags: list[str]
- create_instance(strict: bool = False, **kwargs: Any) NodeTransformer
Create an instance of the transform with given parameters.
- Parameters:
strict (bool, default = False) – If True, log warnings for unknown parameters to aid debugging
**kwargs – Parameters to pass to the transform constructor
- Returns:
Transform instance
- Return type:
- Raises:
ValueError – If required parameters are missing or validation fails
Examples
>>> metadata = TransformMetadata( ... name="test", ... description="Test transform", ... transformer_class=MyTransform, ... parameters={'threshold': ParameterSpec(type=int, default=10)} ... ) >>> instance = metadata.create_instance(threshold=20)
- get_parameter_names() list[str]
Get list of parameter names.
- Returns:
Parameter names
- Return type:
list[str]
- has_parameter(name: str) bool
Check if transform has a parameter.
- Parameters:
name (str) – Parameter name
- Returns:
True if parameter exists
- Return type:
bool
- __init__(name: str, description: str, transformer_class: ~typing.Type[~all2md.ast.transforms.NodeTransformer], parameters: dict[str, ~all2md.transforms.metadata.ParameterSpec] = <factory>, priority: int = 100, dependencies: list[str] = <factory>, version: str = '1.0.0', author: str | None = None, tags: list[str] = <factory>) None