Skip to content

Conf

conf

Kfactory configuration.

LogFilter pydantic-model

Bases: BaseModel

Filter certain messages by log level or regex.

Filtered messages are not evaluated and discarded.

Fields:

Source code in kfactory/conf.py
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
class LogFilter(BaseModel):
    """Filter certain messages by log level or regex.

    Filtered messages are not evaluated and discarded.
    """

    level: LogLevel = LogLevel.INFO
    regex: str | None = None

    def __call__(self, record: loguru.Record) -> bool:
        """Loguru needs the filter to be callable."""
        levelno = logger.level(self.level).no
        if self.regex is None:
            return record["level"].no >= levelno
        return record["level"].no >= levelno and not bool(
            re.search(self.regex, record["message"])
        )

__call__

__call__(record: Record) -> bool

Loguru needs the filter to be callable.

Source code in kfactory/conf.py
160
161
162
163
164
165
166
167
def __call__(self, record: loguru.Record) -> bool:
    """Loguru needs the filter to be callable."""
    levelno = logger.level(self.level).no
    if self.regex is None:
        return record["level"].no >= levelno
    return record["level"].no >= levelno and not bool(
        re.search(self.regex, record["message"])
    )

LogLevel

Bases: StrEnum

KFactory logger levels.

Source code in kfactory/conf.py
126
127
128
129
130
131
132
133
134
135
class LogLevel(StrEnum):
    """KFactory logger levels."""

    TRACE = "TRACE"
    DEBUG = "DEBUG"
    INFO = "INFO"
    SUCCESS = "SUCCESS"
    WARNING = "WARNING"
    ERROR = "ERROR"
    CRITICAL = "CRITICAL"

PROPID

Bases: IntEnum

Mapping for GDS properties.

Source code in kfactory/conf.py
60
61
62
63
64
65
66
class PROPID(IntEnum):
    """Mapping for GDS properties."""

    NAME = 0
    """Instance name."""
    PURPOSE = 1
    """Instance purpose (e.g. 'routing')."""

NAME class-attribute instance-attribute

NAME = 0

Instance name.

PURPOSE class-attribute instance-attribute

PURPOSE = 1

Instance purpose (e.g. 'routing').

Settings

Bases: BaseSettings

KFactory settings object.

Attrs

n_threads: Number of threads to use for tiling processor, defaults to all available cores. logger: The loguru class to use for logging. Shouldn't be necessary to configure by hand. logfilter: The filter to use. Can be configured to set log level and filter messages by regex. display_type: The type of image to show when calling the jupyter display function. meta_format: Metadata format to use for reading KLayout metadata. If set to 'default', metadata will be read as instances such as Trans/DCplxTrans. If the metadata is in the old string format (there was a bug in how to read metadata in some versions), use 'string'. console: The rich console to use for displaying rich content. max_cellname_length: The maximum length of a cell name.

Source code in kfactory/conf.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
class Settings(BaseSettings):
    """KFactory settings object.

    Attrs:
        n_threads: Number of threads to use for tiling processor, defaults
            to all available cores.
        logger: The loguru class to use for logging. Shouldn't be necessary
            to configure by hand.
        logfilter: The filter to use. Can be configured to set log level and filter
            messages by regex.
        display_type: The type of image to show when calling the jupyter display
            function.
        meta_format: Metadata format to use for reading KLayout metadata.
            If set to 'default', metadata will be read as instances such as
            Trans/DCplxTrans. If the metadata is in the old string format
            (there was a bug in how to read metadata in some versions), use
            'string'.
        console: The rich console to use for displaying rich content.
        max_cellname_length: The maximum length of a cell name.
    """

    model_config = SettingsConfigDict(
        arbitrary_types_allowed=True,
        env_prefix="kfactory_",
        env_nested_delimiter="_",
        extra="allow",
        validate_assignment=True,
        env_file=dotenv_path,
    )

    n_threads: int = get_affinity()
    """Number of threads to use for multithreaded operations."""
    logfilter: LogFilter = Field(default_factory=LogFilter)
    """Can configure the logger to ignore certain levels or by regex."""
    display_type: Literal["widget", "image"] = "image"
    """The default behavior for displaying cells in jupyter."""
    meta_format: Literal["v3", "v2", "v1"] = "v3"
    """The format of the saving of metadata.

    v1: Transformations and other KLayout objects are stored as a string. In
        case of ports they are converted back to KLayout objects on read.
    v2: All objects can be stored in the nativ KLayout format (klayout>=0.28.13)
    """
    # console for printing
    console: rich.console.Console = Field(default_factory=rich.console.Console)

    # cell decorator settings
    allow_width_mismatch: bool = False
    allow_layer_mismatch: bool = False
    allow_type_mismatch: bool = False
    allow_undefined_layers: bool = False
    cell_layout_cache: bool = False
    cell_overwrite_existing: bool = False
    connect_use_angle: bool = True
    connect_use_mirror: bool = True
    check_instances: CheckInstances = CheckInstances.RAISE
    check_unnamed_cells: CheckUnnamedCells = CheckUnnamedCells.WARNING
    max_cellname_length: int = 99
    debug_names: bool = False

    # default write settings
    write_context_info: bool = True
    write_cell_properties: bool = True
    write_file_properties: bool = True
    write_timestamps: bool = False
    write_kfactory_settings: bool = True
    """Write kfactory version into the gds/oasis."""

    show_function: ShowFunction | None = None

    @field_validator("show_function", mode="before")
    @classmethod
    def _validate_show_function(
        cls, show: ShowFunction | str | None
    ) -> ShowFunction | None:
        if isinstance(show, str):
            mod, f = show.rsplit(".", 1)
            loaded_mod = importlib.import_module(mod)
            show_ = getattr(loaded_mod, f)
            if not isinstance(show_, ShowFunction):
                raise ValidationError(f"{show=} is not a ShowFunction.")
            show = show_
        return show

    @field_validator("logfilter")
    @classmethod
    def _validate_logfilter(cls, logfilter: LogFilter) -> LogFilter:
        logger.remove()
        logger.add(
            sys.stdout,
            format=tracing_formatter,
            filter=logfilter,
            enqueue=False,
            backtrace=True,
        )
        logger.debug("LogLevel: {}", logfilter.level)
        logger.patch(add_traceback)

        return logfilter

    @field_validator("cell_overwrite_existing")
    @classmethod
    def _validate_overwrite_and_cache(cls, v: bool) -> bool:
        if v is True:
            logger.warning(
                "'overwrite_existing' has been set to True. This might cause "
                "unintended behavior when overwriting existing cells and delete any "
                "existing instances of them."
            )
        return v

    @field_validator("cell_layout_cache")
    @classmethod
    def _validate_layout_cache(cls, v: bool) -> bool:
        if v is True:
            logger.debug(
                "'cell_layout_cache' has been set to True. This might cause when "
                "as any cell names generated automatically are loaded from the layout"
                " instead of created. This could happen e.g. after reading a gds file"
                " into the layout."
            )
        return v

    @field_validator(
        "allow_width_mismatch", "allow_layer_mismatch", "allow_type_mismatch"
    )
    @classmethod
    def _debug_info_on_global_setting(cls, v: Any, info: ValidationInfo) -> Any:
        logger.bind(with_traceback=True).debug(
            "'{}' set globally to '{}'", info.field_name, v
        )
        return v

    @cached_property
    def project_dir(self) -> Path | None:
        """Find the current working directory's project dir.

        This is the git repo if it exists, cwd otherwise."""
        try:
            repo = pygit2.Repository(Path.cwd())
            return Path(repo.workdir or repo.path).resolve()
        except pygit2.GitError:
            return None

    def __init__(self, **data: Any) -> None:
        """Set log filter and run pydantic."""
        super().__init__(**data)

display_type class-attribute instance-attribute

display_type: Literal['widget', 'image'] = 'image'

The default behavior for displaying cells in jupyter.

logfilter class-attribute instance-attribute

logfilter: LogFilter = Field(default_factory=LogFilter)

Can configure the logger to ignore certain levels or by regex.

meta_format class-attribute instance-attribute

meta_format: Literal['v3', 'v2', 'v1'] = 'v3'

The format of the saving of metadata.

Transformations and other KLayout objects are stored as a string. In

case of ports they are converted back to KLayout objects on read.

v2: All objects can be stored in the nativ KLayout format (klayout>=0.28.13)

n_threads class-attribute instance-attribute

n_threads: int = get_affinity()

Number of threads to use for multithreaded operations.

project_dir cached property

project_dir: Path | None

Find the current working directory's project dir.

This is the git repo if it exists, cwd otherwise.

write_kfactory_settings class-attribute instance-attribute

write_kfactory_settings: bool = True

Write kfactory version into the gds/oasis.

__init__

__init__(**data: Any) -> None

Set log filter and run pydantic.

Source code in kfactory/conf.py
342
343
344
def __init__(self, **data: Any) -> None:
    """Set log filter and run pydantic."""
    super().__init__(**data)

add_traceback

add_traceback(record: Record) -> None

Add a traceback to the logger.

Source code in kfactory/conf.py
93
94
95
96
97
98
99
def add_traceback(record: loguru.Record) -> None:
    """Add a traceback to the logger."""
    extra = record["extra"]
    if extra.get("with_traceback", False):
        extra["traceback"] = "\n" + "".join(traceback.format_stack())
    else:
        extra["traceback"] = ""

get_affinity

get_affinity() -> int

Get number of cores/threads available.

On (most) linux we can get it through the scheduling affinity. Otherwise, fall back to the multiprocessing cpu count.

Source code in kfactory/conf.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def get_affinity() -> int:
    """Get number of cores/threads available.

    On (most) linux we can get it through the scheduling affinity. Otherwise,
    fall back to the multiprocessing cpu count.
    """
    try:
        return len(os.sched_getaffinity(0))
    except AttributeError:
        try:
            import multiprocessing

            return multiprocessing.cpu_count()
        except ModuleNotFoundError:
            return 1

tracing_formatter

tracing_formatter(record: Record) -> str

Traceback filtering.

Filter out frames coming from Loguru internals.

Source code in kfactory/conf.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
def tracing_formatter(record: loguru.Record) -> str:
    """Traceback filtering.

    Filter out frames coming from Loguru internals.
    """
    frames = takewhile(
        lambda f: "/loguru/" not in f.filename, traceback.extract_stack()
    )
    stack = " > ".join(f"{f.filename}:{f.name}:{f.lineno}" for f in frames)
    record["extra"]["stack"] = stack

    if record["extra"].get("with_backtrace", False):
        return (
            "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level>"
            " | <cyan>{extra[stack]}</cyan> - <level>{message}</level>\n{exception}"
        )

    return (
        "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}"
        "</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan>"
        " - <level>{message}</level>\n{exception}"
    )