From f0a1bb3ee76dd581bdcf89ad1ecb46607d9cd2d0 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 20 Nov 2025 20:41:49 -0800 Subject: [PATCH 01/15] Update HierarchyBlock.py --- edg/core/HierarchyBlock.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index 98d97eb5e..987911bff 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -100,6 +100,25 @@ def __iter__(self): return iter((tuple(self.blocks), self)) + +BlockPrototypeType = TypeVar('BlockPrototypeType', bound='Block') +class BlockPrototype(Generic[BlockPrototypeType]): + """A block prototype, that contains a type and arguments, but without constructing the entire block + and running its (potentially quite expensive) __init__. + + This class is automatically created on Block instantiations by the BlockMeta metaclass __init__ hook.""" + def __init__(self, tpe: Type[BlockPrototypeType], args: List[Any], kwargs: Dict[str, Any]) -> None: + self._tpe = tpe + self._args = args + self._kwargs = kwargs + + def bind(self) -> BlockPrototypeType: + """Binds the prototype into an actual Block instance.""" + # TODO set / inspect on global binding flag + return self._tpe(*self._args, **self._kwargs) # type: ignore + + + class BlockMeta(ElementMeta): """This provides a hook on __init__ that replaces argument values with empty ConstraintExpr based on the type annotation and stores the supplied argument to the __init__ (if any) in the binding. From d5063e199bb2a5a42cd9e3ace1950700d51ecc8a Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 13:35:54 -0800 Subject: [PATCH 02/15] unit test --- edg/core/HierarchyBlock.py | 12 +++++++++++- edg/core/test_block_prototype.py | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 edg/core/test_block_prototype.py diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index 987911bff..68757a851 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -100,7 +100,6 @@ def __iter__(self): return iter((tuple(self.blocks), self)) - BlockPrototypeType = TypeVar('BlockPrototypeType', bound='Block') class BlockPrototype(Generic[BlockPrototypeType]): """A block prototype, that contains a type and arguments, but without constructing the entire block @@ -117,6 +116,17 @@ def bind(self) -> BlockPrototypeType: # TODO set / inspect on global binding flag return self._tpe(*self._args, **self._kwargs) # type: ignore + def __getattribute__(self, item: str) -> Any: + if item.startswith("_"): + return super().__getattribute__(item) + else: + raise AttributeError(f"BlockPrototype has no attributes, must bind to get a concrete Block instance, tried to get {item}") + + def __setattr__(self, key: str, value: Any) -> None: + if key.startswith("_"): + super().__setattr__(key, value) + else: + raise AttributeError(f"BlockPrototype has no attributes, must bind to get a concrete Block instance, tried to set {key}") class BlockMeta(ElementMeta): diff --git a/edg/core/test_block_prototype.py b/edg/core/test_block_prototype.py new file mode 100644 index 000000000..d83ecb2b6 --- /dev/null +++ b/edg/core/test_block_prototype.py @@ -0,0 +1,21 @@ +import unittest + +from .HierarchyBlock import BlockPrototype, Block + + +class BlockPrototypeTestCase(unittest.TestCase): + def test_args_access(self) -> None: + block = BlockPrototype(Block, ['pos1', 'pos2'], {'k1': 'v1', 'k2': 'v2'}) + assert block._tpe == Block + assert block._args == ['pos1', 'pos2'] + assert block._kwargs == {'k1': 'v1', 'k2': 'v2'} + + + def test_attribute_access(self) -> None: + block = BlockPrototype(Block, [], {}) + + with self.assertRaises(AttributeError): + block.attr + + with self.assertRaises(AttributeError): + block.attr = 2 From d7136db88ed4e623c7d4e6811516d258d1660c80 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 13:41:21 -0800 Subject: [PATCH 03/15] test skeleton --- edg/core/HierarchyBlock.py | 9 +++++++++ edg/core/test_block_prototype.py | 13 ++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index 68757a851..7405a1602 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -166,6 +166,15 @@ class BlockMeta(ElementMeta): "ArrayStringLike": ArrayStringExpr, } + _next_bind: Optional[Type[Block]] = None + + @staticmethod + def _set_next_bind(cls: Type[Block]) -> None: + """Call to set that the next block construction will be of type and should be bound + (concrete block instantiated)""" + assert BlockMeta._next_bind is None + BlockMeta._next_bind = cls + def __new__(cls, *args: Any, **kwargs: Any) -> Any: new_cls = super().__new__(cls, *args, **kwargs) diff --git a/edg/core/test_block_prototype.py b/edg/core/test_block_prototype.py index d83ecb2b6..0ba7b4f48 100644 --- a/edg/core/test_block_prototype.py +++ b/edg/core/test_block_prototype.py @@ -3,6 +3,15 @@ from .HierarchyBlock import BlockPrototype, Block +class TestBlockPrototype(Block): + def __init__(self) -> None: + super().__init__() + block_model = Block() + assert isinstance(block_model, BlockPrototype) + block_model = self.Block(block_model) + assert isinstance(block_model, Block) + + class BlockPrototypeTestCase(unittest.TestCase): def test_args_access(self) -> None: block = BlockPrototype(Block, ['pos1', 'pos2'], {'k1': 'v1', 'k2': 'v2'}) @@ -10,7 +19,6 @@ def test_args_access(self) -> None: assert block._args == ['pos1', 'pos2'] assert block._kwargs == {'k1': 'v1', 'k2': 'v2'} - def test_attribute_access(self) -> None: block = BlockPrototype(Block, [], {}) @@ -19,3 +27,6 @@ def test_attribute_access(self) -> None: with self.assertRaises(AttributeError): block.attr = 2 + + def test_prototype_creation(self) -> None: + TestBlockPrototype() # check that assertions inside fire From ffeef740dcef246239b0b29fd9595963c45ee422 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 14:09:11 -0800 Subject: [PATCH 04/15] Remove ElementMeta from all base classes --- edg/core/Blocks.py | 4 ---- edg/core/Core.py | 20 +------------------- edg/core/HierarchyBlock.py | 27 ++++++++++++++++++++++++--- edg/core/Link.py | 19 ++++++++++++++++--- edg/core/Ports.py | 15 ++++++++++++++- edg/core/__init__.py | 2 +- edg/core/test_block_prototype.py | 6 +++--- 7 files changed, 59 insertions(+), 34 deletions(-) diff --git a/edg/core/Blocks.py b/edg/core/Blocks.py index b922f758e..6a67a9a30 100644 --- a/edg/core/Blocks.py +++ b/edg/core/Blocks.py @@ -276,10 +276,6 @@ def _all_connects_of(self, base: Connection) -> IdentitySet[Connection]: return delegated_connects - def _post_init(self): - assert self._elaboration_state == BlockElaborationState.init - self._elaboration_state = BlockElaborationState.post_init - def name(self) -> StringExpr: return self._name diff --git a/edg/core/Core.py b/edg/core/Core.py index 3a254eec3..af2a2ad1f 100644 --- a/edg/core/Core.py +++ b/edg/core/Core.py @@ -142,20 +142,6 @@ def values(self) -> ValuesView[ElementType]: return self.container.values() -class ElementMeta(type): - """Hook on construction to store some metadata about its creation. - This hooks the top-level __init__ only.""" - def __call__(cls, *args, **kwargs): - block_context = builder.get_enclosing_block() - - obj = type.__call__(cls, *args, **kwargs) - obj._initializer_args = (args, kwargs) # stores args so it is clone-able - obj._block_context = block_context - obj._post_init() - - return obj - - class Refable(): """Object that could be referenced into a edgir.LocalPath""" def __repr__(self) -> str: @@ -185,7 +171,7 @@ def non_library(decorated: NonLibraryType) -> NonLibraryType: @non_library -class LibraryElement(Refable, metaclass=ElementMeta): +class LibraryElement(Refable): """Defines a library element, which optionally contains other library elements.""" _elt_properties: Dict[Tuple[Type[LibraryElement], EltPropertiesBase], Any] = {} @@ -200,10 +186,6 @@ def __init__(self) -> None: self.manager = SubElementManager() self.manager_ignored: Set[str] = set(['_parent']) - """Optionally overloaded to run anything post-__init__""" - def _post_init(self): - pass - def __setattr__(self, name: str, value: Any) -> None: if hasattr(self, 'manager_ignored') and name not in self.manager_ignored: self.manager.add_element(name, value) diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index 7405a1602..fa618813f 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -15,7 +15,7 @@ from .Blocks import BaseBlock, Connection, BlockElaborationState, AbstractBlockProperty from .ConstraintExpr import BoolLike, FloatLike, IntLike, RangeLike, StringLike from .ConstraintExpr import ConstraintExpr, BoolExpr, FloatExpr, IntExpr, RangeExpr, StringExpr -from .Core import Refable, non_library, ElementMeta +from .Core import Refable, non_library from .HdlUserExceptions import * from .IdentityDict import IdentityDict from .IdentitySet import IdentitySet @@ -106,7 +106,7 @@ class BlockPrototype(Generic[BlockPrototypeType]): and running its (potentially quite expensive) __init__. This class is automatically created on Block instantiations by the BlockMeta metaclass __init__ hook.""" - def __init__(self, tpe: Type[BlockPrototypeType], args: List[Any], kwargs: Dict[str, Any]) -> None: + def __init__(self, tpe: Type[BlockPrototypeType], args: Tuple[Any], kwargs: Dict[str, Any]) -> None: self._tpe = tpe self._args = args self._kwargs = kwargs @@ -129,7 +129,7 @@ def __setattr__(self, key: str, value: Any) -> None: raise AttributeError(f"BlockPrototype has no attributes, must bind to get a concrete Block instance, tried to set {key}") -class BlockMeta(ElementMeta): +class BlockMeta(type): """This provides a hook on __init__ that replaces argument values with empty ConstraintExpr based on the type annotation and stores the supplied argument to the __init__ (if any) in the binding. @@ -175,6 +175,19 @@ def _set_next_bind(cls: Type[Block]) -> None: assert BlockMeta._next_bind is None BlockMeta._next_bind = cls + def __call__(cls, *args, **kwargs): + """Hook on construction to store some metadata about its creation. + This hooks the top-level __init__ only.""" + block_context = builder.get_enclosing_block() + + obj = type.__call__(cls, *args, **kwargs) + obj._initializer_args = (args, kwargs) # stores args so it is clone-able + obj._block_context = block_context + assert obj._elaboration_state == BlockElaborationState.init + obj._elaboration_state = BlockElaborationState.post_init + + return obj + def __new__(cls, *args: Any, **kwargs: Any) -> Any: new_cls = super().__new__(cls, *args, **kwargs) @@ -271,6 +284,14 @@ class Block(BaseBlock[edgir.HierarchyBlock], metaclass=BlockMeta): """Part with a statically-defined subcircuit. Relations between contained parameters may only be expressed in the given constraint language. """ + # TODO IMPLEMENT ME + # def __new__(cls, *args: Any, **kwargs: Any) -> Union[Block, BlockPrototype]: + # if BlockMeta._next_bind is cls: + # BlockMeta._next_bind = None + # return super().__new__(cls) + # else: + # return BlockPrototype(cls, args, kwargs) + def __init__(self) -> None: super().__init__() diff --git a/edg/core/Link.py b/edg/core/Link.py index d16223c5c..5bfe8a1ca 100644 --- a/edg/core/Link.py +++ b/edg/core/Link.py @@ -5,15 +5,28 @@ from .. import edgir from .Array import BaseVector, DerivedVector -from .Blocks import BaseBlock, Connection +from .Blocks import BaseBlock, Connection, BlockElaborationState from .Builder import builder -from .Core import Refable, non_library, ElementMeta +from .Core import Refable, non_library from .HdlUserExceptions import UnconnectableError from .IdentityDict import IdentityDict from .Ports import Port -class LinkMeta(ElementMeta): +class LinkMeta(type): + def __call__(cls, *args, **kwargs): + """Hook on construction to store some metadata about its creation. + This hooks the top-level __init__ only.""" + block_context = builder.get_enclosing_block() + + obj = type.__call__(cls, *args, **kwargs) + obj._initializer_args = (args, kwargs) # stores args so it is clone-able + obj._block_context = block_context + assert obj._elaboration_state == BlockElaborationState.init + obj._elaboration_state = BlockElaborationState.post_init + + return obj + def __new__(cls, *args: Any, **kwargs: Any) -> Any: new_cls = super().__new__(cls, *args, **kwargs) diff --git a/edg/core/Ports.py b/edg/core/Ports.py index b8552dad6..62f061c31 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -18,9 +18,22 @@ from .PortBlocks import PortBridge, PortAdapter +class PortMeta(type): + def __call__(cls, *args, **kwargs): + """Hook on construction to store some metadata about its creation. + This hooks the top-level __init__ only.""" + block_context = builder.get_enclosing_block() + + obj = type.__call__(cls, *args, **kwargs) + obj._initializer_args = (args, kwargs) # stores args so it is clone-able + obj._block_context = block_context + + return obj + + PortParentTypes = Union['BaseContainerPort', 'BaseBlock'] @non_library -class BasePort(HasMetadata): +class BasePort(HasMetadata, metaclass=PortMeta): SelfType = TypeVar('SelfType', bound='BasePort') def __init__(self) -> None: diff --git a/edg/core/__init__.py b/edg/core/__init__.py index eff07b942..43139a731 100644 --- a/edg/core/__init__.py +++ b/edg/core/__init__.py @@ -21,7 +21,7 @@ from .MultiBiDict import MultiBiDict # Features for library builders -from .Core import LibraryElement, SubElementDict, ElementDict, ElementMeta, non_library +from .Core import LibraryElement, SubElementDict, ElementDict, non_library from .Blocks import BasePort, BaseBlock from .Categories import InternalBlock from .Builder import builder diff --git a/edg/core/test_block_prototype.py b/edg/core/test_block_prototype.py index 0ba7b4f48..4014f4db3 100644 --- a/edg/core/test_block_prototype.py +++ b/edg/core/test_block_prototype.py @@ -14,13 +14,13 @@ def __init__(self) -> None: class BlockPrototypeTestCase(unittest.TestCase): def test_args_access(self) -> None: - block = BlockPrototype(Block, ['pos1', 'pos2'], {'k1': 'v1', 'k2': 'v2'}) + block = BlockPrototype(Block, ('pos1', 'pos2'), {'k1': 'v1', 'k2': 'v2'}) assert block._tpe == Block - assert block._args == ['pos1', 'pos2'] + assert block._args == ('pos1', 'pos2') assert block._kwargs == {'k1': 'v1', 'k2': 'v2'} def test_attribute_access(self) -> None: - block = BlockPrototype(Block, [], {}) + block = BlockPrototype(Block, (), {}) with self.assertRaises(AttributeError): block.attr From 80f14d2a7f82bcc4fd721d5f05b661f0aae58dfc Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 16:55:21 -0800 Subject: [PATCH 05/15] cleaning --- edg/core/Blocks.py | 18 ++++++++++++++++-- edg/core/Core.py | 14 ++++++++++++-- edg/core/HierarchyBlock.py | 32 +++++++++----------------------- edg/core/Link.py | 17 ++--------------- edg/core/Ports.py | 18 ++++-------------- 5 files changed, 43 insertions(+), 56 deletions(-) diff --git a/edg/core/Blocks.py b/edg/core/Blocks.py index 6a67a9a30..332b21c83 100644 --- a/edg/core/Blocks.py +++ b/edg/core/Blocks.py @@ -10,7 +10,7 @@ from .Array import BaseVector, Vector from .Binding import AssignBinding, NameBinding from .ConstraintExpr import ConstraintExpr, BoolExpr, ParamBinding, AssignExpr, StringExpr, BoolLike -from .Core import Refable, HasMetadata, builder, SubElementDict, non_library, EltPropertiesBase +from .Core import Refable, HasMetadata, builder, SubElementDict, non_library, EltPropertiesBase, InitializerContextMeta from .HdlUserExceptions import * from .IdentityDict import IdentityDict from .IdentitySet import IdentitySet @@ -20,6 +20,18 @@ from .Link import Link +class BaseBlockMeta(InitializerContextMeta): + """Adds a hook to set the post-init elaboration state""" + def __call__(cls, *args, **kwargs): + block_context = builder.get_enclosing_block() + obj = super().__call__(*args, **kwargs) + if isinstance(obj, BaseBlock): # ignore block prototypes + assert obj._elaboration_state == BlockElaborationState.init + obj._elaboration_state = BlockElaborationState.post_init + obj._block_context = block_context + return obj + + class Connection(): """An incremental connection builder, that validates additional ports as they are added so the stack trace can provide the problematic statement.""" @@ -230,12 +242,14 @@ def set_elt_proto(self, pb, ref_map=None): AbstractBlockProperty = EltPropertiesBase() @non_library -class BaseBlock(HasMetadata, Generic[BaseBlockEdgirType]): +class BaseBlock(HasMetadata, Generic[BaseBlockEdgirType], metaclass=BaseBlockMeta): """Base block that has ports (IOs), parameters, and constraints between them. """ # __init__ should initialize the object with structural information (parameters, fields) # as well as optionally initialization (parameter defaults) def __init__(self) -> None: + self._block_context: Optional["Refable"] # set by metaclass, as lexical scope available pre-binding + self._initializer_args: Tuple[Tuple[Any, ...], Dict[str, Any]] # set by metaclass self._parent: Optional[Union[BaseBlock, Port]] # refined from Optional[Refable] in base LibraryElement super().__init__() diff --git a/edg/core/Core.py b/edg/core/Core.py index af2a2ad1f..def17b652 100644 --- a/edg/core/Core.py +++ b/edg/core/Core.py @@ -179,9 +179,7 @@ def __repr__(self) -> str: return "%s@%02x" % (self._get_def_name(), (id(self) // 4) & 0xff) def __init__(self) -> None: - self._block_context: Optional["Refable"] # set by metaclass, as lexical scope available pre-binding self._parent: Optional[LibraryElement] = None # set by binding, None means not bound - self._initializer_args: Tuple[Tuple[Any, ...], Dict[str, Any]] # set by metaclass self.manager = SubElementManager() self.manager_ignored: Set[str] = set(['_parent']) @@ -310,3 +308,15 @@ def _populate_metadata(self, pb: edgir.Metadata, src: Any, else: raise ValueError(f'must overload _metadata_to_proto to handle unknown value {src}') return pb + + +class InitializerContextMeta(type): + def __call__(cls, *args, **kwargs): + """Hook on construction to store some metadata about its creation. + This hooks the top-level __init__ only.""" + # TODO initializer_args should be replaced with the prototype system + + obj = type.__call__(cls, *args, **kwargs) + obj._initializer_args = (args, kwargs) # stores args so it is clone-able + + return obj diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index fa618813f..c459bc912 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -12,7 +12,7 @@ ArrayFloatLike, ArrayRangeLike, ArrayStringLike from .Array import BaseVector, Vector from .Binding import InitParamBinding, AssignBinding -from .Blocks import BaseBlock, Connection, BlockElaborationState, AbstractBlockProperty +from .Blocks import BaseBlock, Connection, BlockElaborationState, AbstractBlockProperty, BaseBlockMeta from .ConstraintExpr import BoolLike, FloatLike, IntLike, RangeLike, StringLike from .ConstraintExpr import ConstraintExpr, BoolExpr, FloatExpr, IntExpr, RangeExpr, StringExpr from .Core import Refable, non_library @@ -106,7 +106,7 @@ class BlockPrototype(Generic[BlockPrototypeType]): and running its (potentially quite expensive) __init__. This class is automatically created on Block instantiations by the BlockMeta metaclass __init__ hook.""" - def __init__(self, tpe: Type[BlockPrototypeType], args: Tuple[Any], kwargs: Dict[str, Any]) -> None: + def __init__(self, tpe: Type[BlockPrototypeType], args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> None: self._tpe = tpe self._args = args self._kwargs = kwargs @@ -129,7 +129,7 @@ def __setattr__(self, key: str, value: Any) -> None: raise AttributeError(f"BlockPrototype has no attributes, must bind to get a concrete Block instance, tried to set {key}") -class BlockMeta(type): +class BlockMeta(BaseBlockMeta): """This provides a hook on __init__ that replaces argument values with empty ConstraintExpr based on the type annotation and stores the supplied argument to the __init__ (if any) in the binding. @@ -175,19 +175,6 @@ def _set_next_bind(cls: Type[Block]) -> None: assert BlockMeta._next_bind is None BlockMeta._next_bind = cls - def __call__(cls, *args, **kwargs): - """Hook on construction to store some metadata about its creation. - This hooks the top-level __init__ only.""" - block_context = builder.get_enclosing_block() - - obj = type.__call__(cls, *args, **kwargs) - obj._initializer_args = (args, kwargs) # stores args so it is clone-able - obj._block_context = block_context - assert obj._elaboration_state == BlockElaborationState.init - obj._elaboration_state = BlockElaborationState.post_init - - return obj - def __new__(cls, *args: Any, **kwargs: Any) -> Any: new_cls = super().__new__(cls, *args, **kwargs) @@ -284,13 +271,12 @@ class Block(BaseBlock[edgir.HierarchyBlock], metaclass=BlockMeta): """Part with a statically-defined subcircuit. Relations between contained parameters may only be expressed in the given constraint language. """ - # TODO IMPLEMENT ME - # def __new__(cls, *args: Any, **kwargs: Any) -> Union[Block, BlockPrototype]: - # if BlockMeta._next_bind is cls: - # BlockMeta._next_bind = None - # return super().__new__(cls) - # else: - # return BlockPrototype(cls, args, kwargs) + def __new__(cls, *args: Any, **kwargs: Any) -> Block: + if BlockMeta._next_bind is cls: + BlockMeta._next_bind = None + return super().__new__(cls) + else: + return BlockPrototype(cls, args, kwargs) # type: ignore def __init__(self) -> None: super().__init__() diff --git a/edg/core/Link.py b/edg/core/Link.py index 5bfe8a1ca..679486b31 100644 --- a/edg/core/Link.py +++ b/edg/core/Link.py @@ -5,7 +5,7 @@ from .. import edgir from .Array import BaseVector, DerivedVector -from .Blocks import BaseBlock, Connection, BlockElaborationState +from .Blocks import BaseBlock, Connection, BaseBlockMeta from .Builder import builder from .Core import Refable, non_library from .HdlUserExceptions import UnconnectableError @@ -13,20 +13,7 @@ from .Ports import Port -class LinkMeta(type): - def __call__(cls, *args, **kwargs): - """Hook on construction to store some metadata about its creation. - This hooks the top-level __init__ only.""" - block_context = builder.get_enclosing_block() - - obj = type.__call__(cls, *args, **kwargs) - obj._initializer_args = (args, kwargs) # stores args so it is clone-able - obj._block_context = block_context - assert obj._elaboration_state == BlockElaborationState.init - obj._elaboration_state = BlockElaborationState.post_init - - return obj - +class LinkMeta(BaseBlockMeta): def __new__(cls, *args: Any, **kwargs: Any) -> Any: new_cls = super().__new__(cls, *args, **kwargs) diff --git a/edg/core/Ports.py b/edg/core/Ports.py index 62f061c31..2757df638 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -8,7 +8,7 @@ from .Binding import ParamBinding, IsConnectedBinding, NameBinding from .Builder import builder from .ConstraintExpr import ConstraintExpr, BoolExpr, StringExpr -from .Core import Refable, HasMetadata, SubElementDict, non_library +from .Core import Refable, HasMetadata, SubElementDict, non_library, InitializerContextMeta from .HdlUserExceptions import * from .IdentityDict import IdentityDict @@ -18,28 +18,18 @@ from .PortBlocks import PortBridge, PortAdapter -class PortMeta(type): - def __call__(cls, *args, **kwargs): - """Hook on construction to store some metadata about its creation. - This hooks the top-level __init__ only.""" - block_context = builder.get_enclosing_block() - - obj = type.__call__(cls, *args, **kwargs) - obj._initializer_args = (args, kwargs) # stores args so it is clone-able - obj._block_context = block_context - - return obj - PortParentTypes = Union['BaseContainerPort', 'BaseBlock'] @non_library -class BasePort(HasMetadata, metaclass=PortMeta): +class BasePort(HasMetadata, metaclass=InitializerContextMeta): SelfType = TypeVar('SelfType', bound='BasePort') def __init__(self) -> None: """Abstract Base Class for ports""" self._parent: Optional[PortParentTypes] # refined from Optional[Refable] in base LibraryElement + self._block_context: Optional["Refable"] # set by metaclass, as lexical scope available pre-binding self._initializer_args: Tuple[Tuple[Any, ...], Dict[str, Any]] # set by metaclass + self._block_context = builder.get_enclosing_block() super().__init__() From c3fe2bac67083c1ec0d17754314741129d9161c4 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 17:07:56 -0800 Subject: [PATCH 06/15] new prototype binding system --- edg/core/HierarchyBlock.py | 45 ++++++++++++++++++-------------- edg/core/test_block_prototype.py | 4 +-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index c459bc912..d24609eeb 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -111,10 +111,12 @@ def __init__(self, tpe: Type[BlockPrototypeType], args: Tuple[Any, ...], kwargs: self._args = args self._kwargs = kwargs - def bind(self) -> BlockPrototypeType: + def _bind(self, parent: Union[BaseBlock, Port]) -> BlockPrototypeType: """Binds the prototype into an actual Block instance.""" - # TODO set / inspect on global binding flag - return self._tpe(*self._args, **self._kwargs) # type: ignore + Block._next_bind = self._tpe + block = self._tpe(*self._args, **self._kwargs) # type: ignore + block._bind_in_place(parent) + return block def __getattribute__(self, item: str) -> Any: if item.startswith("_"): @@ -166,15 +168,6 @@ class BlockMeta(BaseBlockMeta): "ArrayStringLike": ArrayStringExpr, } - _next_bind: Optional[Type[Block]] = None - - @staticmethod - def _set_next_bind(cls: Type[Block]) -> None: - """Call to set that the next block construction will be of type and should be bound - (concrete block instantiated)""" - assert BlockMeta._next_bind is None - BlockMeta._next_bind = cls - def __new__(cls, *args: Any, **kwargs: Any) -> Any: new_cls = super().__new__(cls, *args, **kwargs) @@ -271,9 +264,14 @@ class Block(BaseBlock[edgir.HierarchyBlock], metaclass=BlockMeta): """Part with a statically-defined subcircuit. Relations between contained parameters may only be expressed in the given constraint language. """ + _next_bind: Optional[Type[Block]] = None # set when binding, to avoid creating a prototype + def __new__(cls, *args: Any, **kwargs: Any) -> Block: - if BlockMeta._next_bind is cls: - BlockMeta._next_bind = None + if Block._next_bind is not None: + assert Block._next_bind is cls + Block._next_bind = None + return super().__new__(cls) + elif builder.get_enclosing_block() is None: # always construct if top-level return super().__new__(cls) else: return BlockPrototype(cls, args, kwargs) # type: ignore @@ -614,15 +612,22 @@ def Export(self, port: ExportType, tags: Iterable[PortTag]=[], *, optional: bool def Block(self, tpe: BlockType) -> BlockType: from .BlockInterfaceMixin import BlockInterfaceMixin from .DesignTop import DesignTop - if not isinstance(tpe, Block): + + if self._elaboration_state not in \ + [BlockElaborationState.init, BlockElaborationState.contents, BlockElaborationState.generate]: + raise BlockDefinitionError(self, "can only define blocks in init, contents, or generate") + + if isinstance(tpe, BlockPrototype): + tpe_cls = tpe._tpe + else: + tpe_cls = tpe.__class__ + + if not issubclass(tpe_cls, Block): raise TypeError(f"param to Block(...) must be Block, got {tpe} of type {type(tpe)}") - if isinstance(tpe, BlockInterfaceMixin) and tpe._is_mixin(): + if issubclass(tpe_cls, BlockInterfaceMixin) and tpe_cls._is_mixin(): raise TypeError("param to Block(...) must not be BlockInterfaceMixin") - if isinstance(tpe, DesignTop): + if issubclass(tpe_cls, DesignTop): raise TypeError(f"param to Block(...) may not be DesignTop") - if self._elaboration_state not in \ - [BlockElaborationState.init, BlockElaborationState.contents, BlockElaborationState.generate]: - raise BlockDefinitionError(self, "can only define blocks in init, contents, or generate") elt = tpe._bind(self) self._blocks.register(elt) diff --git a/edg/core/test_block_prototype.py b/edg/core/test_block_prototype.py index 4014f4db3..30689fb71 100644 --- a/edg/core/test_block_prototype.py +++ b/edg/core/test_block_prototype.py @@ -8,8 +8,8 @@ def __init__(self) -> None: super().__init__() block_model = Block() assert isinstance(block_model, BlockPrototype) - block_model = self.Block(block_model) - assert isinstance(block_model, Block) + self.subblock = self.Block(block_model) + assert isinstance(self.subblock, Block) class BlockPrototypeTestCase(unittest.TestCase): From 641e081e8dc62d6cc49aa21343709dd3fb934160 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 17:17:17 -0800 Subject: [PATCH 07/15] wip --- edg/core/HierarchyBlock.py | 10 ++++++++-- edg/core/Ports.py | 3 +++ edg/electronics_model/KiCadSchematicBlock.py | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index d24609eeb..49181d406 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -453,13 +453,19 @@ def _def_to_proto(self) -> edgir.HierarchyBlock: def with_mixin(self, tpe: MixinType) -> MixinType: """Adds an interface mixin for this Block. Mainly useful for abstract blocks, e.g. IoController with HasI2s.""" from .BlockInterfaceMixin import BlockInterfaceMixin - if not (isinstance(tpe, BlockInterfaceMixin) and tpe._is_mixin()): + + if isinstance(tpe, BlockPrototype): + tpe_cls = tpe._tpe + else: + tpe_cls = tpe.__class__ + + if not (issubclass(tpe_cls, BlockInterfaceMixin) and tpe_cls._is_mixin()): raise TypeError("param to with_mixin must be a BlockInterfaceMixin") if isinstance(self, BlockInterfaceMixin) and self._is_mixin(): raise BlockDefinitionError(self, "mixins can not have with_mixin") if (self.__class__, AbstractBlockProperty) not in self._elt_properties: raise BlockDefinitionError(self, "mixins can only be added to abstract classes") - if not isinstance(self, tpe._get_mixin_base()): + if not isinstance(self, tpe_cls._get_mixin_base()): raise TypeError(f"block {self.__class__.__name__} not an instance of mixin base {tpe._get_mixin_base().__name__}") assert self._parent is not None diff --git a/edg/core/Ports.py b/edg/core/Ports.py index 2757df638..8e879cf4a 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -160,12 +160,15 @@ def init_from(self: SelfType, other: SelfType): def _bridge(self) -> Optional[PortBridge]: """Creates a (unbound) bridge and returns it.""" + from .HierarchyBlock import Block + if self.bridge_type is None: return None if self._bridge_instance is not None: return self._bridge_instance assert self._is_bound(), "not bound, can't create bridge" + Block._next_bind = self.bridge_type self._bridge_instance = self.bridge_type() return self._bridge_instance diff --git a/edg/electronics_model/KiCadSchematicBlock.py b/edg/electronics_model/KiCadSchematicBlock.py index 55c2ce36d..af9030d8c 100644 --- a/edg/electronics_model/KiCadSchematicBlock.py +++ b/edg/electronics_model/KiCadSchematicBlock.py @@ -208,9 +208,9 @@ def import_kicad(self, filepath: str, locals: Mapping[str, Any] = {}, # use the caller's globals, since this needs to reflect the caller's imports block_model = eval(inline_code, inspect.stack()[1][0].f_globals, locals) - assert isinstance(block_model, KiCadImportableBlock),\ - f"block {block_model} created by {inline_code} not KicadImportableBlock" block = self.Block(block_model) + assert isinstance(block, KiCadImportableBlock), \ + f"block {block} created by {inline_code} not KicadImportableBlock" block_pinning = block.symbol_pinning(symbol.lib) setattr(self, symbol.refdes, block) elif symbol.lib in SYMBOL_MAP: # sub-block with code to parse the value From 8f19fbea5f8de2a2905b4bb0eaea6920d26ed69a Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 17:22:11 -0800 Subject: [PATCH 08/15] fixes --- edg/core/HierarchyBlock.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index 49181d406..0efd5fc70 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -111,6 +111,9 @@ def __init__(self, tpe: Type[BlockPrototypeType], args: Tuple[Any, ...], kwargs: self._args = args self._kwargs = kwargs + def __repr__(self) -> str: + return f"BlockPrototype({self._tpe}, args={self._args}, kwargs={self._kwargs})" + def _bind(self, parent: Union[BaseBlock, Port]) -> BlockPrototypeType: """Binds the prototype into an actual Block instance.""" Block._next_bind = self._tpe @@ -453,7 +456,6 @@ def _def_to_proto(self) -> edgir.HierarchyBlock: def with_mixin(self, tpe: MixinType) -> MixinType: """Adds an interface mixin for this Block. Mainly useful for abstract blocks, e.g. IoController with HasI2s.""" from .BlockInterfaceMixin import BlockInterfaceMixin - if isinstance(tpe, BlockPrototype): tpe_cls = tpe._tpe else: @@ -466,7 +468,7 @@ def with_mixin(self, tpe: MixinType) -> MixinType: if (self.__class__, AbstractBlockProperty) not in self._elt_properties: raise BlockDefinitionError(self, "mixins can only be added to abstract classes") if not isinstance(self, tpe_cls._get_mixin_base()): - raise TypeError(f"block {self.__class__.__name__} not an instance of mixin base {tpe._get_mixin_base().__name__}") + raise TypeError(f"block {self.__class__.__name__} not an instance of mixin base {tpe_cls._get_mixin_base().__name__}") assert self._parent is not None elt = tpe._bind(self._parent) @@ -629,7 +631,7 @@ def Block(self, tpe: BlockType) -> BlockType: tpe_cls = tpe.__class__ if not issubclass(tpe_cls, Block): - raise TypeError(f"param to Block(...) must be Block, got {tpe} of type {type(tpe)}") + raise TypeError(f"param to Block(...) must be Block, got {tpe_cls}") if issubclass(tpe_cls, BlockInterfaceMixin) and tpe_cls._is_mixin(): raise TypeError("param to Block(...) must not be BlockInterfaceMixin") if issubclass(tpe_cls, DesignTop): From 696bf6b62885023974f0511881fdb6078773c15d Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 17:41:23 -0800 Subject: [PATCH 09/15] less failing tests --- edg/core/MultipackBlock.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/edg/core/MultipackBlock.py b/edg/core/MultipackBlock.py index c55aeb3e6..ee9aa9e87 100644 --- a/edg/core/MultipackBlock.py +++ b/edg/core/MultipackBlock.py @@ -12,7 +12,7 @@ from .Core import non_library, SubElementDict from .ConstraintExpr import ConstraintExpr, BoolExpr, IntExpr, FloatExpr, RangeExpr, StringExpr from .Ports import BasePort, Port -from .HierarchyBlock import Block +from .HierarchyBlock import Block, BlockPrototype class PackedBlockAllocate(NamedTuple): @@ -134,7 +134,12 @@ def __init__(self): def PackedPart(self, tpe: PackedPartType) -> PackedPartType: """Adds a block type that can be packed into this block. The block is a "virtual block" that will not appear in the design tree.""" - if not isinstance(tpe, (Block, PackedBlockArray)): + if isinstance(tpe, BlockPrototype): + tpe_cls = tpe._tpe + else: + tpe_cls = tpe.__class__ + + if not issubclass(tpe_cls, (Block, PackedBlockArray)): raise TypeError(f"param to PackedPart(...) must be Block, got {tpe} of type {type(tpe)}") if self._elaboration_state != BlockElaborationState.init: raise BlockDefinitionError(self, "can only define multipack in init") From 747562efb26f65a7fb32d71562dbf1e731649be0 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 17:54:04 -0800 Subject: [PATCH 10/15] all tests passing --- edg/core/DesignTop.py | 2 +- edg/core/HierarchyBlock.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/edg/core/DesignTop.py b/edg/core/DesignTop.py index 6066df2e9..c80fc55a0 100644 --- a/edg/core/DesignTop.py +++ b/edg/core/DesignTop.py @@ -36,7 +36,7 @@ def make_packing_refinement(multipack_part: Union[Block, PackedBlockAllocate], p if isinstance(multipack_part, Block): return path, type(multipack_part) elif isinstance(multipack_part, PackedBlockAllocate): - return path, type(multipack_part.parent._tpe) + return path, type(multipack_part.parent._elt_sample) else: raise TypeError diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index 0efd5fc70..cf11db19c 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -112,7 +112,7 @@ def __init__(self, tpe: Type[BlockPrototypeType], args: Tuple[Any, ...], kwargs: self._kwargs = kwargs def __repr__(self) -> str: - return f"BlockPrototype({self._tpe}, args={self._args}, kwargs={self._kwargs})" + return f"{self.__class__.__name__}({self._tpe}, args={self._args}, kwargs={self._kwargs})" def _bind(self, parent: Union[BaseBlock, Port]) -> BlockPrototypeType: """Binds the prototype into an actual Block instance.""" From 23cc5bcede2e55f5b29cb46c08efe02319a27758 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 17:55:42 -0800 Subject: [PATCH 11/15] Update DesignTop.py --- edg/core/DesignTop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/edg/core/DesignTop.py b/edg/core/DesignTop.py index c80fc55a0..56124c4c3 100644 --- a/edg/core/DesignTop.py +++ b/edg/core/DesignTop.py @@ -36,6 +36,7 @@ def make_packing_refinement(multipack_part: Union[Block, PackedBlockAllocate], p if isinstance(multipack_part, Block): return path, type(multipack_part) elif isinstance(multipack_part, PackedBlockAllocate): + assert multipack_part.parent._elt_sample is not None return path, type(multipack_part.parent._elt_sample) else: raise TypeError From e8219595f3f63c51bdb450d919055aafd69bdf03 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 18:02:14 -0800 Subject: [PATCH 12/15] remove initializer args from Block --- edg/core/Blocks.py | 14 ++------------ edg/core/Core.py | 12 ------------ edg/core/HierarchyBlock.py | 5 +++++ edg/core/Ports.py | 13 ++++++++++++- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/edg/core/Blocks.py b/edg/core/Blocks.py index 332b21c83..01c42cd6d 100644 --- a/edg/core/Blocks.py +++ b/edg/core/Blocks.py @@ -10,7 +10,7 @@ from .Array import BaseVector, Vector from .Binding import AssignBinding, NameBinding from .ConstraintExpr import ConstraintExpr, BoolExpr, ParamBinding, AssignExpr, StringExpr, BoolLike -from .Core import Refable, HasMetadata, builder, SubElementDict, non_library, EltPropertiesBase, InitializerContextMeta +from .Core import Refable, HasMetadata, builder, SubElementDict, non_library, EltPropertiesBase from .HdlUserExceptions import * from .IdentityDict import IdentityDict from .IdentitySet import IdentitySet @@ -20,7 +20,7 @@ from .Link import Link -class BaseBlockMeta(InitializerContextMeta): +class BaseBlockMeta(type): """Adds a hook to set the post-init elaboration state""" def __call__(cls, *args, **kwargs): block_context = builder.get_enclosing_block() @@ -249,7 +249,6 @@ class BaseBlock(HasMetadata, Generic[BaseBlockEdgirType], metaclass=BaseBlockMet # as well as optionally initialization (parameter defaults) def __init__(self) -> None: self._block_context: Optional["Refable"] # set by metaclass, as lexical scope available pre-binding - self._initializer_args: Tuple[Tuple[Any, ...], Dict[str, Any]] # set by metaclass self._parent: Optional[Union[BaseBlock, Port]] # refined from Optional[Refable] in base LibraryElement super().__init__() @@ -419,15 +418,6 @@ def _get_ref_map(self, prefix: edgir.LocalPath) -> IdentityDict[Refable, edgir.L def _bind_in_place(self, parent: Union[BaseBlock, Port]): self._parent = parent - SelfType = TypeVar('SelfType', bound='BaseBlock') - def _bind(self: SelfType, parent: Union[BaseBlock, Port]) -> SelfType: - """Returns a clone of this object with the specified binding. This object must be unbound.""" - assert self._parent is None, "can't clone bound block" - assert builder.get_enclosing_block() is self._block_context, "can't clone to different context" - clone = type(self)(*self._initializer_args[0], **self._initializer_args[1]) # type: ignore - clone._bind_in_place(parent) - return clone - def _check_constraint(self, constraint: ConstraintExpr) -> None: def check_subexpr(expr: Union[ConstraintExpr, BasePort]) -> None: # TODO rewrite this whole method if isinstance(expr, ConstraintExpr) and isinstance(expr.binding, ParamBinding): diff --git a/edg/core/Core.py b/edg/core/Core.py index def17b652..934b648aa 100644 --- a/edg/core/Core.py +++ b/edg/core/Core.py @@ -308,15 +308,3 @@ def _populate_metadata(self, pb: edgir.Metadata, src: Any, else: raise ValueError(f'must overload _metadata_to_proto to handle unknown value {src}') return pb - - -class InitializerContextMeta(type): - def __call__(cls, *args, **kwargs): - """Hook on construction to store some metadata about its creation. - This hooks the top-level __init__ only.""" - # TODO initializer_args should be replaced with the prototype system - - obj = type.__call__(cls, *args, **kwargs) - obj._initializer_args = (args, kwargs) # stores args so it is clone-able - - return obj diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index cf11db19c..0b0579836 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -279,6 +279,11 @@ def __new__(cls, *args: Any, **kwargs: Any) -> Block: else: return BlockPrototype(cls, args, kwargs) # type: ignore + SelfType = TypeVar('SelfType', bound='BaseBlock') + def _bind(self: SelfType, parent: Union[BaseBlock, Port]) -> SelfType: + # for type checking only + raise TypeError("_bind should be called from BlockPrototype") + def __init__(self) -> None: super().__init__() diff --git a/edg/core/Ports.py b/edg/core/Ports.py index 8e879cf4a..591df6c34 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -8,7 +8,7 @@ from .Binding import ParamBinding, IsConnectedBinding, NameBinding from .Builder import builder from .ConstraintExpr import ConstraintExpr, BoolExpr, StringExpr -from .Core import Refable, HasMetadata, SubElementDict, non_library, InitializerContextMeta +from .Core import Refable, HasMetadata, SubElementDict, non_library from .HdlUserExceptions import * from .IdentityDict import IdentityDict @@ -18,6 +18,17 @@ from .PortBlocks import PortBridge, PortAdapter +class InitializerContextMeta(type): + def __call__(cls, *args, **kwargs): + """Hook on construction to store some metadata about its creation. + This hooks the top-level __init__ only.""" + # TODO initializer_args should be replaced with the prototype system + + obj = type.__call__(cls, *args, **kwargs) + obj._initializer_args = (args, kwargs) # stores args so it is clone-able + + return obj + PortParentTypes = Union['BaseContainerPort', 'BaseBlock'] @non_library From 9857041a8e9c9d54895a6bc57f1dc6e0224c4c3d Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 19:07:48 -0800 Subject: [PATCH 13/15] Update HierarchyBlock.py --- edg/core/HierarchyBlock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index 0b0579836..c6477cedf 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -125,13 +125,13 @@ def __getattribute__(self, item: str) -> Any: if item.startswith("_"): return super().__getattribute__(item) else: - raise AttributeError(f"BlockPrototype has no attributes, must bind to get a concrete Block instance, tried to get {item}") + raise AttributeError(f"{self.__class__.__name__} has no attributes, must bind to get a concrete instance, tried to get {item}") def __setattr__(self, key: str, value: Any) -> None: if key.startswith("_"): super().__setattr__(key, value) else: - raise AttributeError(f"BlockPrototype has no attributes, must bind to get a concrete Block instance, tried to set {key}") + raise AttributeError(f"{self.__class__.__name__} has no attributes, must bind to get a concrete instance, tried to set {key}") class BlockMeta(BaseBlockMeta): From ce689ed71d69c6bd906f1764a733134c93e94e27 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sat, 22 Nov 2025 19:21:30 -0800 Subject: [PATCH 14/15] wip --- edg/core/Blocks.py | 5 +---- edg/core/DesignTop.py | 2 +- edg/core/Generator.py | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/edg/core/Blocks.py b/edg/core/Blocks.py index 01c42cd6d..75f8acbab 100644 --- a/edg/core/Blocks.py +++ b/edg/core/Blocks.py @@ -26,8 +26,6 @@ def __call__(cls, *args, **kwargs): block_context = builder.get_enclosing_block() obj = super().__call__(*args, **kwargs) if isinstance(obj, BaseBlock): # ignore block prototypes - assert obj._elaboration_state == BlockElaborationState.init - obj._elaboration_state = BlockElaborationState.post_init obj._block_context = block_context return obj @@ -198,7 +196,6 @@ def make_connection(self) -> Optional[Union[ConnectedLink, Export]]: class BlockElaborationState(Enum): pre_init = 1 # not sure if this is needed, doesn't actually get used init = 2 - post_init = 3 contents = 4 post_contents = 5 generate = 6 @@ -304,7 +301,7 @@ def _elaborated_def_to_proto(self) -> BaseBlockEdgirType: prev_element = builder.push_element(self) assert prev_element is None try: - assert self._elaboration_state == BlockElaborationState.post_init + assert self._elaboration_state == BlockElaborationState.init self._elaboration_state = BlockElaborationState.contents self.contents() self._elaboration_state = BlockElaborationState.post_contents diff --git a/edg/core/DesignTop.py b/edg/core/DesignTop.py index 56124c4c3..740cac7b3 100644 --- a/edg/core/DesignTop.py +++ b/edg/core/DesignTop.py @@ -56,7 +56,7 @@ def _elaborated_def_to_proto(self) -> edgir.HierarchyBlock: prev_element = builder.push_element(self) assert prev_element is None try: - assert self._elaboration_state == BlockElaborationState.post_init + assert self._elaboration_state == BlockElaborationState.init self._elaboration_state = BlockElaborationState.contents self.contents() self.multipack() diff --git a/edg/core/Generator.py b/edg/core/Generator.py index b4c49cb58..100a2f1dd 100644 --- a/edg/core/Generator.py +++ b/edg/core/Generator.py @@ -111,7 +111,7 @@ def _generated_def_to_proto(self, generate_values: Iterable[Tuple[edgir.LocalPat assert prev_element is None try: - assert self._elaboration_state == BlockElaborationState.post_init # TODO dedup w/ elaborated_def_to_proto + assert self._elaboration_state == BlockElaborationState.init self._elaboration_state = BlockElaborationState.contents self.contents() self._elaboration_state = BlockElaborationState.generate From b01147033d0546a7c1bc5716a6aae65efa213d2b Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 23 Nov 2025 00:49:41 -0800 Subject: [PATCH 15/15] Update Ports.py --- edg/core/Ports.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/edg/core/Ports.py b/edg/core/Ports.py index 591df6c34..fdff6fd6a 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -22,11 +22,8 @@ class InitializerContextMeta(type): def __call__(cls, *args, **kwargs): """Hook on construction to store some metadata about its creation. This hooks the top-level __init__ only.""" - # TODO initializer_args should be replaced with the prototype system - obj = type.__call__(cls, *args, **kwargs) obj._initializer_args = (args, kwargs) # stores args so it is clone-able - return obj