Skip to content

Factory metadata

factory_metadata

Factory metadata provider system.

Provides a provider-based system for attaching structured metadata (device type, ports, models, tags, display hints) to cell factories without coupling to schematic-driven layout.

FactoryMetadata pydantic-model

Bases: BaseModel

Resolved metadata for a cell factory.

Aggregated from all registered providers for a given factory. Fields are intentionally loosely typed so consumers (gfp, PDKs, simulation packages) can define their own schemas on top.

Fields:

  • device_type (str | None)
  • ports (tuple[PortSpec, ...])
  • models (list)
  • tags (tuple[str, ...])
  • display (dict)
  • info (dict[str, JSONSerializable])
Source code in kfactory/factory_metadata.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class FactoryMetadata(BaseModel):
    """Resolved metadata for a cell factory.

    Aggregated from all registered providers for a given factory.
    Fields are intentionally loosely typed so consumers (gfp, PDKs,
    simulation packages) can define their own schemas on top.
    """

    device_type: str | None = None
    ports: tuple[PortSpec, ...] = ()
    models: list = Field(default_factory=list)
    tags: tuple[str, ...] = ()
    display: dict = Field(default_factory=dict)
    info: dict[str, JSONSerializable] = Field(default_factory=dict)

FactoryMetadataRegistry

Stores metadata provider registrations and resolves them per factory.

Source code in kfactory/factory_metadata.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
class FactoryMetadataRegistry:
    """Stores metadata provider registrations and resolves them per factory."""

    def __init__(self) -> None:
        self._records: list[_FactoryMetadataProviderRecord] = []
        self._counter = 0

    def add(
        self,
        *,
        target_kind: Literal["name", "fqn", "object"],
        target_key: str | int,
        kind: FactoryMetadataProviderKind,
        provider: Callable[..., Any],
        replace: bool = False,
        position: Literal["append", "prepend"] = "append",
    ) -> Callable[..., Any]:
        """Register a metadata provider for a target factory."""
        if position not in ("append", "prepend"):
            raise ValueError(f"Unknown model provider position: {position!r}")
        self._counter += 1
        self._records.append(
            _FactoryMetadataProviderRecord(
                target_kind=target_kind,
                target_key=target_key,
                kind=kind,
                provider=provider,
                replace=replace,
                position=position,
                order=self._counter,
            )
        )
        return provider

    def providers_for(
        self,
        *,
        name: str,
        qualified_name: str,
        obj: object,
    ) -> tuple[_FactoryMetadataProviderRecord, ...]:
        """Return all provider records matching a factory by name, FQN, or identity."""
        object_id = id(obj)
        return tuple(
            record
            for record in sorted(self._records, key=lambda r: r.order)
            if (
                (record.target_kind == "name" and record.target_key == name)
                or (record.target_kind == "fqn" and record.target_key == qualified_name)
                or (record.target_kind == "object" and record.target_key == object_id)
            )
        )

add

add(
    *,
    target_kind: Literal["name", "fqn", "object"],
    target_key: str | int,
    kind: FactoryMetadataProviderKind,
    provider: Callable[..., Any],
    replace: bool = False,
    position: Literal["append", "prepend"] = "append",
) -> Callable[..., Any]

Register a metadata provider for a target factory.

Source code in kfactory/factory_metadata.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def add(
    self,
    *,
    target_kind: Literal["name", "fqn", "object"],
    target_key: str | int,
    kind: FactoryMetadataProviderKind,
    provider: Callable[..., Any],
    replace: bool = False,
    position: Literal["append", "prepend"] = "append",
) -> Callable[..., Any]:
    """Register a metadata provider for a target factory."""
    if position not in ("append", "prepend"):
        raise ValueError(f"Unknown model provider position: {position!r}")
    self._counter += 1
    self._records.append(
        _FactoryMetadataProviderRecord(
            target_kind=target_kind,
            target_key=target_key,
            kind=kind,
            provider=provider,
            replace=replace,
            position=position,
            order=self._counter,
        )
    )
    return provider

providers_for

providers_for(
    *, name: str, qualified_name: str, obj: object
) -> tuple[_FactoryMetadataProviderRecord, ...]

Return all provider records matching a factory by name, FQN, or identity.

Source code in kfactory/factory_metadata.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def providers_for(
    self,
    *,
    name: str,
    qualified_name: str,
    obj: object,
) -> tuple[_FactoryMetadataProviderRecord, ...]:
    """Return all provider records matching a factory by name, FQN, or identity."""
    object_id = id(obj)
    return tuple(
        record
        for record in sorted(self._records, key=lambda r: r.order)
        if (
            (record.target_kind == "name" and record.target_key == name)
            or (record.target_kind == "fqn" and record.target_key == qualified_name)
            or (record.target_kind == "object" and record.target_key == object_id)
        )
    )

PortSpec

Bases: TypedDict

Structured port descriptor for cell metadata.

Declares a logical port with its kind, physical side, and optional cross-section or aliasing information. Used by external tools (simulation runners, schematic editors) to understand a cell's interface without instantiating it.

Source code in kfactory/factory_metadata.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class PortSpec(TypedDict):
    """Structured port descriptor for cell metadata.

    Declares a logical port with its kind, physical side, and optional
    cross-section or aliasing information. Used by external tools
    (simulation runners, schematic editors) to understand a cell's
    interface without instantiating it.
    """

    name: str
    kind: str
    side: NotRequired[Literal["left", "right", "top", "bottom"] | None]
    orientation: NotRequired[Literal[0, 90, 180, 270] | None]
    order: NotRequired[int | None]
    cross_section: NotRequired[str | None]
    role: NotRequired[str | None]
    aliases: NotRequired[tuple[str, ...]]

call_provider

call_provider(
    provider: Callable[..., Any], params: dict[str, Any]
) -> Any

Invoke a provider with the subset of cell params its signature accepts.

Source code in kfactory/factory_metadata.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def call_provider(provider: Callable[..., Any], params: dict[str, Any]) -> Any:
    """Invoke a provider with the subset of cell params its signature accepts."""
    sig = inspect.signature(provider)
    accepts_kwargs = any(
        p.kind is inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()
    )
    if accepts_kwargs:
        return provider(**params)

    provider_kwargs: dict[str, Any] = {}
    missing: list[str] = []
    for name, param in sig.parameters.items():
        if param.kind in (
            inspect.Parameter.POSITIONAL_ONLY,
            inspect.Parameter.VAR_POSITIONAL,
        ):
            raise TypeError(
                f"Metadata provider {provider!r} has unsupported parameter {name!r}."
            )
        if name in params:
            provider_kwargs[name] = params[name]
        elif param.default is inspect.Parameter.empty:
            missing.append(name)
    if missing:
        raise TypeError(
            f"Metadata provider {provider!r} requires parameters unavailable "
            f"from the cell factory: {missing!r}."
        )
    return provider(**provider_kwargs)

provider_result_to_patch

provider_result_to_patch(
    record: _FactoryMetadataProviderRecord, result: Any
) -> _FactoryMetadataPatch

Convert a provider's return value into a metadata patch.

Source code in kfactory/factory_metadata.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
def provider_result_to_patch(
    record: _FactoryMetadataProviderRecord, result: Any
) -> _FactoryMetadataPatch:
    """Convert a provider's return value into a metadata patch."""
    match record.kind:
        case "metadata":
            return _metadata_result_to_patch(result)
        case "model":
            models = _coerce_models(result)
            return _FactoryMetadataPatch(
                models=models,
                models_position=record.position,
            )
        case "device_type":
            return _FactoryMetadataPatch(
                device_type=None if result is None else str(result)
            )
        case "ports":
            return _FactoryMetadataPatch(ports=_coerce_ports(result))
        case "tags":
            return _FactoryMetadataPatch(tags=_coerce_tags(result))
        case "display":
            return _FactoryMetadataPatch(
                display=None if result is None else _coerce_display(result)
            )
        case "info":
            if result is None:
                return _FactoryMetadataPatch()
            if not isinstance(result, Mapping):
                raise TypeError("info provider must return a mapping.")
            return _FactoryMetadataPatch(info={str(k): v for k, v in result.items()})

resolve_metadata

resolve_metadata(
    providers: Iterable[_FactoryMetadataProviderRecord],
    params: dict[str, Any],
) -> FactoryMetadata

Resolve all providers for a factory into a single FactoryMetadata.

Source code in kfactory/factory_metadata.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
def resolve_metadata(
    providers: Iterable[_FactoryMetadataProviderRecord],
    params: dict[str, Any],
) -> FactoryMetadata:
    """Resolve all providers for a factory into a single FactoryMetadata."""
    device_type: str | None = None
    ports: tuple[PortSpec, ...] | None = None
    models: list = []
    tags: tuple[str, ...] = ()
    display: dict = {}
    info: dict[str, JSONSerializable] = {}

    for record in providers:
        result = call_provider(record.provider, params)
        patch = provider_result_to_patch(record, result)

        if patch.device_type is not None:
            if (
                device_type is not None
                and device_type != patch.device_type
                and not record.replace
            ):
                raise ValueError(
                    "Conflicting metadata device_type values: "
                    f"{device_type!r} and {patch.device_type!r}."
                )
            device_type = patch.device_type

        if patch.ports is not None:
            if ports is not None and ports != patch.ports and not record.replace:
                raise ValueError("Conflicting metadata port declarations.")
            ports = patch.ports

        if patch.models is not None:
            if patch.models_position == "prepend":
                models = patch.models + models
            else:
                models = models + patch.models

        if patch.tags:
            tags = _merge_tags(tags, patch.tags)

        if patch.display:
            if display and patch.display != display and not record.replace:
                raise ValueError("Conflicting metadata display declarations.")
            display = patch.display

        if patch.info:
            for key, value in patch.info.items():
                if key in info and info[key] != value:
                    raise ValueError(f"Conflicting metadata info key: {key!r}.")
                info[key] = value

    return FactoryMetadata(
        device_type=device_type,
        ports=ports or (),
        models=models,
        tags=tags,
        display=display,
        info=info,
    )