Skip to content

Commit

Permalink
replace factories with a modified UserDict
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastian-goeldi committed Aug 27, 2023
1 parent 087e4d0 commit ef38488
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 59 deletions.
6 changes: 3 additions & 3 deletions src/kfactory/cells/dbu/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .straight import straight
from .taper import taper
from .straight import Straight, straight
from .taper import Taper, taper

__all__ = ["straight", "taper"]
__all__ = ["straight", "taper", "Straight", "Taper"]
4 changes: 2 additions & 2 deletions src/kfactory/cli/sea.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def upload(
) -> None:
"""Upload a new eda file to gdatasea."""
if name is None:
kcl = KCLayout()
kcl = KCLayout("SeaUpload")
kcl.read(file)
assert len(kcl.top_cells()) > 0, (
"Cannot automatically determine name of gdatasea edafile if"
Expand Down Expand Up @@ -67,7 +67,7 @@ def update(
) -> None:
"""Upload a new eda file to gdatasea."""
if name is None:
kcl = KCLayout()
kcl = KCLayout("SeaUpdate")
kcl.read(file)
assert len(kcl.top_cells()) > 0, (
"Cannot automatically determine name of gdatasea edafile if"
Expand Down
144 changes: 93 additions & 51 deletions src/kfactory/kcell.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import json
import socket
import types
from collections import UserDict
from collections.abc import Callable, Hashable, Iterable, Iterator
from enum import Enum, IntEnum, IntFlag, auto
from hashlib import sha3_512
Expand All @@ -26,7 +27,7 @@
import cachetools.func
import numpy as np
import ruamel.yaml
from pydantic import BaseModel, Field, model_validator
from pydantic import BaseModel, Field, computed_field, model_validator
from pydantic_settings import BaseSettings
from typing_extensions import ParamSpec

Expand All @@ -44,6 +45,8 @@
# from .pdk import Pdk
from types import ModuleType

T = TypeVar("T")

KCellParams = ParamSpec("KCellParams")
AnyTrans = TypeVar(
"AnyTrans", bound=kdb.Trans | kdb.DTrans | kdb.ICplxTrans | kdb.DCplxTrans
Expand Down Expand Up @@ -339,27 +342,27 @@ def port_check(p1: Port, p2: Port, checks: PortCheck = PortCheck.all_opposite) -
assert p1.port_type == p2.port_type, f"Port type mismatch for {p1=} {p2=}"


def get_cells(
modules: Iterable[ModuleType], verbose: bool = False
) -> dict[str, KCellFactory]:
"""Returns KCells (KCell functions) from a module or list of modules.
Args:
modules: module or iterable of modules.
verbose: prints in case any errors occur.
"""
cells = {}
for module in modules:
for t in inspect.getmembers(module):
if callable(t[1]) and t[0] != "partial":
try:
r = inspect.signature(t[1]).return_annotation
if r == KCell or (isinstance(r, str) and r.endswith("KCell")):
cells[t[0]] = KCellFactory(name=t[0], factory=t[1])
except ValueError:
if verbose:
print(f"error in {t[0]}")
return cells
# def get_cells(
# modules: Iterable[ModuleType], verbose: bool = False
# ) -> dict[str, KCellFactory]:
# """Returns KCells (KCell functions) from a module or list of modules.

# Args:
# modules: module or iterable of modules.
# verbose: prints in case any errors occur.
# """
# cells = {}
# for module in modules:
# for t in inspect.getmembers(module):
# if callable(t[1]) and t[0] != "partial":
# try:
# r = inspect.signature(t[1]).return_annotation
# if r == KCell or (isinstance(r, str) and r.endswith("KCell")):
# cells[t[0]] = KCellFactory(name=t[0], factory=t[1])
# except ValueError:
# if verbose:
# print(f"error in {t[0]}")
# return cells


class LayerEnclosureModel(BaseModel):
Expand Down Expand Up @@ -1435,32 +1438,24 @@ def port_filter(num_port: tuple[int, Port]) -> bool:
netlist.add(circ)


class KCellFactory(BaseModel):
name: str
factory: Callable[..., KCell]

def __call__(self, *args: KCellParams.args, **kwargs: KCellParams.kwargs) -> KCell:
return self.factory(*args, **kwargs)
# class KCellFactories(BaseModel):
# factories: dict[str, KCellFactory] = Field(default={})

# def add(self, name: str, factory: KCellFactory) -> None:
# self.factories[name] = factory

class KCellFactories(BaseModel):
factories: dict[str, KCellFactory] = Field(default={})
# # def __getattr__(self, name: str) -> KCellFactory:
# # """If KCLayout doesn't have an attribute, look in the KLayout Cell."""
# # return self.factories[name]

def add(self, name: str, factory: KCellFactory) -> None:
self.factories[name] = factory
# def __getitem__(self, name: str) -> KCellFactory:
# return self.factories[name]

# def __getattr__(self, name: str) -> KCellFactory:
# """If KCLayout doesn't have an attribute, look in the KLayout Cell."""
# return self.factories[name]
# def __setitem__(self, name: str, factory: KCellFactory) -> None:
# self.factories[name] = factory

# def __getitem__(self, name: str) -> KCellFactory:
# return self.factories[name]

# def __setitem__(self, name: str, factory: KCellFactory) -> None:
# self.factories[name] = factory

# def __setattr__(self, name: str, factory: KCellFactory) -> None:
# self.factories[name] = factory
# # def __setattr__(self, name: str, factory: KCellFactory) -> None:
# # self.factories[name] = factory


class Constants(BaseSettings):
Expand Down Expand Up @@ -1507,13 +1502,15 @@ class KCLayout(BaseModel, arbitrary_types_allowed=True, extra="allow"):
"""

name: str | None = None
# name: str
_name: str
layout: kdb.Layout
layer_enclosures: LayerEnclosureModel
enclosure: KCellEnclosure
library: kdb.Library

factories: KCellFactories = KCellFactories()
factories: KCellFactories
# KCellFactories = DictWithAttributes[KCell] # KCellFactories()
kcells: dict[int, KCell]
layers: type[LayerEnum]
sparameters_path: Path | str | None
Expand All @@ -1523,7 +1520,7 @@ class KCLayout(BaseModel, arbitrary_types_allowed=True, extra="allow"):

def __init__(
self,
name: str | None = None,
name: str,
layer_enclosures: dict[str, LayerEnclosure] | LayerEnclosureModel | None = None,
enclosure: KCellEnclosure | None = None,
# factories: dict[str, KCellFactory] | None = None,
Expand Down Expand Up @@ -1561,7 +1558,7 @@ def __init__(
layer_dict = {}

if base_kcl:
name = name or base_kcl.name
name = name
# if layers is None:
if copy_base_kcl_layers:
base_layer_dict = {
Expand Down Expand Up @@ -1607,19 +1604,33 @@ def __init__(
layout = library.layout()

super().__init__(
name=name,
_name=name,
kcells={},
layer_enclosures=layer_enclosures,
enclosure=enclosure,
# cell_factories=cell_factories,
layers=layers,
factories=KCellFactories({}),
sparameters_path=sparameters_path,
interconnect_cml_path=interconnect_cml_path,
constants=_constants,
library=library,
layout=layout,
rename_function=port_rename_function,
)
self._name = name

self.library.register(self.name)

@computed_field # type: ignore[misc]
@property
def name(self) -> str:
return self._name

@name.setter
def name(self, new_name: str) -> None:
self._name = new_name
self.library.register(new_name)

def kcell(self, name: str | None = None, ports: Ports | None = None) -> KCell:
"""Create a new cell based ont he pdk's layout object."""
Expand All @@ -1633,7 +1644,27 @@ def layer_enum(

def __getattr__(self, name): # type: ignore[no-untyped-def]
"""If KCLayout doesn't have an attribute, look in the KLayout Cell."""
return getattr(self.layout, name)
if name != "_name":
return self.layout.__getattribute__(name)

def __setattr__(self, name: str, value: Any) -> None:
if name == "_name":
object.__setattr__(self, name, value)
else:
try:
super().__setattr__(name, value)
except AttributeError:
self.layout.__setattr__(name, value)

# if getattr(self.layout, name):
# setattr(self.layout, name, value)
# else:
# super().__setattr__(name, value)
# if name == "_name":
# super().__setattr__(name, value)
# elif hasattr(self.layout, name):
# setattr(self.layout, name, value)
# else:

def layerenum_from_dict(
self, name: str = "LAYER", *, layers: dict[str, tuple[int, int]]
Expand All @@ -1650,7 +1681,7 @@ def dup(self, init_cells: bool = True) -> KCLayout:
Returns:
Copy of itself
"""
kcl = KCLayout()
kcl = KCLayout(self.name + "_DUPLICATE")
kcl.layout.assign(self.layout.dup())
if init_cells:
for i, kc in self.kcells.items():
Expand Down Expand Up @@ -1853,13 +1884,24 @@ def update_namespace(ns: dict[str, Any]) -> None:
)


class KCellFactories(UserDict[str, Callable[..., KCell]]):
def __init__(self, data: dict[str, Callable[..., KCell]]) -> None:
super().__init__(data)

def __getattr__(self, name: str) -> Any:
if name != "data":
return self.data[name]
else:
self.__getattribute__(name)


KCLayout.model_rebuild()
LayerSection.model_rebuild()
LayerEnclosure.model_rebuild()
KCellEnclosure.model_rebuild()
LayerEnclosureModel.model_rebuild()
LayerEnclosureCollection.model_rebuild()
kcl = KCLayout()
kcl = KCLayout("DEFAULT")
"""Default library object.
Any [KCell][kfactory.kcell.KCell] uses this object unless another one is
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_cells(cell_name: str) -> None:
gds_ref.mkdir(parents=True, exist_ok=True)
run_cell.write(str(ref_file))
raise FileNotFoundError(f"GDS file not found. Saving it to {ref_file}")
kcl_ref = kf.KCLayout()
kcl_ref = kf.KCLayout("TEST")
kcl_ref.read(gds_ref / f"{cell.name}.gds")
ref_cell = kcl_ref[kcl_ref.top_cell().name]

Expand Down
2 changes: 1 addition & 1 deletion tests/test_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def test_metainfo_read(straight):
save.write_context_info = True
straight.kcl.write(t.name)

kcl = kf.KCLayout()
kcl = kf.KCLayout("TEST_META")
kcl.read(t.name)

wg_read = kcl[straight.name]
Expand Down
2 changes: 1 addition & 1 deletion tests/test_rename.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_rename_orientation() -> None:


def test_rename_setter() -> None:
kcl = kf.KCLayout()
kcl = kf.KCLayout("TEST_RENAME")

assert kcl.rename_function == kf.port.rename_clockwise

Expand Down

0 comments on commit ef38488

Please sign in to comment.