Skip to content

Serialization

serialization

DecoratorDict

Bases: UserDict[Hashable, Any]

Hashable decorator for a dictionary.

Source code in kfactory/serialization.py
40
41
42
43
44
45
46
47
48
class DecoratorDict(UserDict[Hashable, Any]):
    """Hashable decorator for a dictionary."""

    def __hash__(self) -> int:
        """Hash the dictionary."""
        return hash(tuple(sorted(self.data.items())))

    def __reduce__(self) -> tuple[type[DecoratorDict], tuple[dict[Hashable, Any]]]:
        return (DecoratorDict, (self.data,))

__hash__

__hash__() -> int

Hash the dictionary.

Source code in kfactory/serialization.py
43
44
45
def __hash__(self) -> int:
    """Hash the dictionary."""
    return hash(tuple(sorted(self.data.items())))

DecoratorList

Bases: UserList[Any]

Hashable decorator for a list.

Source code in kfactory/serialization.py
29
30
31
32
33
34
35
36
37
class DecoratorList(UserList[Any]):
    """Hashable decorator for a list."""

    def __hash__(self) -> int:
        """Hash the list."""
        return hash(tuple(self.data))

    def __reduce__(self) -> tuple[type[DecoratorList], tuple[list[Any]]]:
        return (DecoratorList, (self.data,))

__hash__

__hash__() -> int

Hash the list.

Source code in kfactory/serialization.py
32
33
34
def __hash__(self) -> int:
    """Hash the list."""
    return hash(tuple(self.data))

cell_name_hash

cell_name_hash(name: str) -> str

Return 8-char hash of a cell name.

Source code in kfactory/serialization.py
84
85
86
def cell_name_hash(name: str) -> str:
    """Return 8-char hash of a cell name."""
    return sha3_512(name.encode()).hexdigest()[:8]

check_metadata_type

check_metadata_type(value: Any) -> MetaData

Recursively check an info value whether it can be stored.

Source code in kfactory/serialization.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
def check_metadata_type(value: Any) -> MetaData:
    """Recursively check an info value whether it can be stored."""
    if value is None:
        return None
    if serializible_value_or_shape_guard(value):
        return value
    if isinstance(value, tuple):
        return tuple(check_metadata_type(tv) for tv in value)
    if isinstance(value, list):
        return [check_metadata_type(tv) for tv in value]
    if isinstance(value, dict):
        return {k: check_metadata_type(v) for k, v in value.items()}
    msg = (
        "MetaData values of the info dict only support int, float, string"
        f", tuple or list. {value=}, {type(value)=}"
    )
    raise ValueError(msg)

clean_dict

clean_dict(d: dict[str, Any]) -> dict[str, Any]

Cleans dictionary recursively.

Source code in kfactory/serialization.py
51
52
53
54
55
56
def clean_dict(d: dict[str, Any]) -> dict[str, Any]:
    """Cleans dictionary recursively."""
    return {
        k: clean_dict(dict(v)) if isinstance(v, dict) else clean_value(v)
        for k, v in d.items()
    }

clean_name

clean_name(name: str) -> str

Ensures that gds cells are composed of [a-zA-Z0-9_-].

only a few characters are currently replaced.

This function has been updated only on case-by-case basis

Source code in kfactory/serialization.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def clean_name(name: str) -> str:
    r"""Ensures that gds cells are composed of [a-zA-Z0-9_\-].

    FIXME: only a few characters are currently replaced.
        This function has been updated only on case-by-case basis
    """
    replace_map = {
        "=": "",
        ",": "_",
        ")": "",
        "(": "",
        "-": "m",
        ".": "p",
        ":": "_",
        "[": "",
        "]": "",
        " ": "_",
        "<": "",
        ">": "",
    }
    for k, v in list(replace_map.items()):
        name = name.replace(k, v)
    return name

clean_value

clean_value(
    value: float
    | float64
    | dict[Any, Any]
    | AnyKCell
    | Callable[..., Any],
) -> str

Makes sure a value is representable in a limited character_space.

Source code in kfactory/serialization.py
 89
 90
 91
 92
 93
 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
def clean_value(
    value: float | np.float64 | dict[Any, Any] | AnyKCell | Callable[..., Any],
) -> str:
    """Makes sure a value is representable in a limited character_space."""
    if isinstance(value, int):  # integer
        return str(value)
    if isinstance(value, float | np.float64):  # float
        return f"{value}".replace(".", "p").rstrip("0").rstrip("p")
    if isinstance(value, kdb.LayerInfo):
        return f"{value.name or str(value.layer) + '_' + str(value.datatype)}"
    if isinstance(value, list | tuple):
        return "_".join(clean_value(v) for v in value)  # ty:ignore[invalid-argument-type]
    if isinstance(value, dict):
        try:
            return dict2name(**value)  # ty:ignore[invalid-argument-type]
        except TypeError as e:
            raise CellNameError(
                "Dictionaries passed to functions as args/kwargs"
                " must be of type dict[str, ...] to be properly serialized"
                " for Cell/Component names or similar."
            ) from e
    if hasattr(value, "name"):
        return clean_name(value.name)  # ty:ignore[invalid-argument-type]
    if callable(value):
        if isinstance(value, FunctionType) and value.__name__ == "<lambda>":
            msg = "Unable to serialize lambda function. Use a named function instead."
            raise ValueError(msg)
        if isinstance(value, functools.partial):
            sig = inspect.signature(value.func)
            args_as_kwargs = dict(zip(sig.parameters.keys(), value.args, strict=False))
            args_as_kwargs.update(**value.keywords)
            args_as_kwargs = clean_dict(args_as_kwargs)
            func = value.func
            while hasattr(func, "func"):
                func = func.func
            v = {
                "function": get_function_name(func),  # ty:ignore[invalid-argument-type]
                "module": func.__module__,
                "settings": args_as_kwargs,
            }
            return clean_value(v)
        if isinstance(value, toolz.functoolz.Compose):
            return "_".join(
                [clean_value(value.first)] + [clean_value(func) for func in value.funcs]
            )
        return getattr(value, "__name__", value.__class__.__name__)
    return clean_name(str(value))

convert_metadata_type

convert_metadata_type(value: Any) -> MetaData

Recursively clean up a MetaData for KCellSettings.

Source code in kfactory/serialization.py
218
219
220
221
222
223
224
225
226
227
228
229
230
def convert_metadata_type(value: Any) -> MetaData:
    """Recursively clean up a MetaData for KCellSettings."""
    if value is None:
        return None
    if serializible_value_or_shape_guard(value):
        return value
    if isinstance(value, tuple):
        return tuple(convert_metadata_type(tv) for tv in value)
    if isinstance(value, list):
        return [convert_metadata_type(tv) for tv in value]
    if isinstance(value, dict):
        return {k: convert_metadata_type(v) for k, v in value.items()}
    return clean_value(value)

deserialize_setting

deserialize_setting(setting: JSONSerializable) -> MetaData

Deserialize a setting.

Source code in kfactory/serialization.py
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
def deserialize_setting(setting: JSONSerializable) -> MetaData:
    """Deserialize a setting."""
    if isinstance(setting, dict):
        return {
            name: deserialize_setting(_setting)  # ty:ignore[invalid-argument-type]
            for name, _setting in setting.items()
        }  # ty:ignore[invalid-return-type]
    if isinstance(setting, list):
        return [deserialize_setting(s) for s in setting]
    if isinstance(setting, tuple):
        return tuple(deserialize_setting(s) for s in setting)
    if isinstance(setting, str) and setting.startswith("!#"):
        cls_name, value = setting.removeprefix("!#").split(" ", 1)
        match cls_name:
            case "LayerInfo":
                return getattr(kdb, cls_name).from_string(value)
            case _:
                return getattr(kdb, cls_name).from_s(value)
    return setting

dict2name

dict2name(
    prefix: str | None = None, **kwargs: dict[str, Any]
) -> str

Returns name from a dict.

Source code in kfactory/serialization.py
207
208
209
210
211
212
213
214
215
def dict2name(prefix: str | None = None, **kwargs: dict[str, Any]) -> str:
    """Returns name from a dict."""
    kwargs.pop("self", None)
    label = [prefix] if prefix else []
    for key, value in kwargs.items():
        key_ = join_first_letters(key)
        label += [f"{key_.upper()}{clean_value(value)}"]
    label_ = "_".join(label)
    return clean_name(label_)

get_cell_name

get_cell_name(
    cell_type: str,
    max_cellname_length: int | None = None,
    **kwargs: dict[str, Any],
) -> str

Convert a cell to a string.

Source code in kfactory/serialization.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
def get_cell_name(
    cell_type: str, max_cellname_length: int | None = None, **kwargs: dict[str, Any]
) -> str:
    """Convert a cell to a string."""
    name = cell_type
    max_cellname_length = max_cellname_length or config.max_cellname_length

    if kwargs:
        name += f"_{dict2name(None, **kwargs)}"

    if len(name) > max_cellname_length:
        name_hash = cell_name_hash(name)
        name = f"{name[: (max_cellname_length - 9)]}_{name_hash}"

    return name

hashable_to_original

hashable_to_original(
    udl: DecoratorDict,
) -> dict[Hashable, Any]
hashable_to_original(udl: DecoratorList) -> list[Hashable]
hashable_to_original(udl: Any) -> Any
hashable_to_original(
    udl: DecoratorDict | DecoratorList | Any,
) -> dict[str, Any] | list[Any] | Any

Convert DecoratorDict to dict.

Source code in kfactory/serialization.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
def hashable_to_original(
    udl: DecoratorDict | DecoratorList | Any,
) -> dict[str, Any] | list[Any] | Any:
    """Convert `DecoratorDict` to `dict`."""
    if isinstance(udl, DecoratorDict):
        for item, value in udl.items():
            udl[item] = hashable_to_original(value)
        return udl.data
    if isinstance(udl, DecoratorList):
        list_: list[Any] = []
        for v in udl:
            if isinstance(v, DecoratorDict | DecoratorList):
                list_.append(hashable_to_original(v))
            else:
                list_.append(v)
        return list_
    return udl

join_first_letters

join_first_letters(name: str) -> str

Join the first letter of a name separated with underscores.

Example::

"TL" == join_first_letters("taper_length")
Source code in kfactory/serialization.py
197
198
199
200
201
202
203
204
def join_first_letters(name: str) -> str:
    """Join the first letter of a name separated with underscores.

    Example::

        "TL" == join_first_letters("taper_length")
    """
    return "".join([x[0] for x in name.split("_") if x])

serialize_setting

serialize_setting(setting: MetaData) -> JSONSerializable

Serialize a setting.

Source code in kfactory/serialization.py
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
def serialize_setting(setting: MetaData) -> JSONSerializable:
    """Serialize a setting."""
    if setting is None:
        return None
    if isinstance(setting, dict):
        return {
            str(name): serialize_setting(_setting)  # ty:ignore[invalid-argument-type]
            for name, _setting in setting.items()
        }
    if isinstance(setting, list):
        return [serialize_setting(s) for s in setting]  # ty:ignore[invalid-argument-type]
    if isinstance(setting, tuple):
        return tuple(serialize_setting(s) for s in setting)  # ty:ignore[invalid-argument-type]
    if serializible_shape_guard(setting):
        return f"!#{setting.__class__.__name__} {setting!s}"
    return setting  # ty:ignore[invalid-return-type]

to_hashable

to_hashable(d: dict[Hashable, Any]) -> DecoratorDict
to_hashable(d: list[Any]) -> DecoratorList
to_hashable(
    d: dict[Hashable, Any] | list[Any],
) -> DecoratorDict | DecoratorList

Convert a dict to a DecoratorDict.

Source code in kfactory/serialization.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def to_hashable(
    d: dict[Hashable, Any] | list[Any],
) -> DecoratorDict | DecoratorList:
    """Convert a `dict` to a `DecoratorDict`."""
    if isinstance(d, dict):
        ud = DecoratorDict()
        for item, value in sorted(d.items()):
            if isinstance(value, dict | list):
                value_: Any = to_hashable(value)
            else:
                value_ = value
            ud[item] = value_
        return ud
    ul = DecoratorList([])
    for _index, value in enumerate(d):
        value_ = to_hashable(value) if isinstance(value, dict | list) else value
        ul.append(value_)
    return ul