From 200154549a725308f3bf7609cf6b095dea0e8a31 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 12:25:57 -0800 Subject: [PATCH 01/21] wip --- edg/abstract_parts/PartsTable.py | 2 +- edg/core/ConstraintExpr.py | 4 ++-- edg/core/Ports.py | 2 +- edg/electronics_model/CircuitBlock.py | 2 +- pyproject.toml | 1 - 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/edg/abstract_parts/PartsTable.py b/edg/abstract_parts/PartsTable.py index 9808d2602..dee001cba 100644 --- a/edg/abstract_parts/PartsTable.py +++ b/edg/abstract_parts/PartsTable.py @@ -16,7 +16,7 @@ def __eq__(self, other: Any) -> bool: ... def __lt__(self, other: Any) -> bool: ... -PartsTableColumnType = TypeVar('PartsTableColumnType') +PartsTableColumnType = TypeVar('PartsTableColumnType', default=Any) class PartsTableColumn(Generic[PartsTableColumnType]): """A column header for a parts table, that allows indexing by an object (instead of a string) that also checks the value type. diff --git a/edg/core/ConstraintExpr.py b/edg/core/ConstraintExpr.py index 53da8710f..e473c8506 100644 --- a/edg/core/ConstraintExpr.py +++ b/edg/core/ConstraintExpr.py @@ -21,8 +21,8 @@ SelfType = TypeVar('SelfType', bound='ConstraintExpr') -WrappedType = TypeVar('WrappedType', covariant=True) -CastableType = TypeVar('CastableType', contravariant=True) +WrappedType = TypeVar('WrappedType', covariant=True, default=Any) +CastableType = TypeVar('CastableType', contravariant=True, default=Any) class ConstraintExpr(Refable, Generic[WrappedType, CastableType]): """Base class for constraint expressions. Basically a container for operations. Actual meaning is held in the Binding. diff --git a/edg/core/Ports.py b/edg/core/Ports.py index c83cd5376..c1fb689c0 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -110,7 +110,7 @@ class BaseContainerPort(BasePort): # TODO can this be removed? pass -PortLinkType = TypeVar('PortLinkType', bound='Link', covariant=True) # TODO: this breaks w/ selftypes +PortLinkType = TypeVar('PortLinkType', bound='Link', covariant=True, default='Link') # TODO: this breaks w/ selftypes @non_library class Port(BasePort, Generic[PortLinkType]): """Abstract Base Class for ports""" diff --git a/edg/electronics_model/CircuitBlock.py b/edg/electronics_model/CircuitBlock.py index c2760aa72..a26bb10aa 100644 --- a/edg/electronics_model/CircuitBlock.py +++ b/edg/electronics_model/CircuitBlock.py @@ -9,7 +9,7 @@ from .KiCadImportableBlock import KiCadImportableBlock from ..core.HdlUserExceptions import EdgTypeError -CircuitLinkType = TypeVar('CircuitLinkType', bound=Link, covariant=True) +CircuitLinkType = TypeVar('CircuitLinkType', bound=Link, covariant=True, default=Link) class CircuitPort(Port[CircuitLinkType], Generic[CircuitLinkType]): """Electrical connection that represents a single port into a single copper net""" pass diff --git a/pyproject.toml b/pyproject.toml index 0defa0066..f571cafc3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,6 @@ edg = ["core/resources/edg-compiler-precompiled.jar", "electronics_model/resourc strict = true implicit_reexport = true -disallow_any_generics = false [project.urls] Homepage = "https://github.com/BerkeleyHCI/PolymorphicBlocks" From 4a6bd7661cb70463e76d9f67f4c461cfc40eea5e Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 12:33:28 -0800 Subject: [PATCH 02/21] eliminate edgir type generic --- edg/core/Blocks.py | 18 ++++++++---------- edg/core/HierarchyBlock.py | 5 ++++- edg/core/Link.py | 5 ++++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/edg/core/Blocks.py b/edg/core/Blocks.py index 19c92acfa..ad86b7495 100644 --- a/edg/core/Blocks.py +++ b/edg/core/Blocks.py @@ -207,8 +207,6 @@ class BlockElaborationState(Enum): post_generate = 7 -BaseBlockEdgirType = TypeVar('BaseBlockEdgirType', bound=edgir.BlockLikeTypes) - class DescriptionStringElts(): @abstractmethod def set_elt_proto(self, pb: edgir.BlockLikeTypes, ref_map: Refable.RefMapType) -> None: @@ -243,7 +241,7 @@ def set_elt_proto(self, pb: edgir.BlockLikeTypes, ref_map: Refable.RefMapType) - AbstractBlockProperty = EltPropertiesBase() @non_library -class BaseBlock(HasMetadata, Generic[BaseBlockEdgirType], metaclass=BaseBlockMeta): +class BaseBlock(HasMetadata, metaclass=BaseBlockMeta): """Base block that has ports (IOs), parameters, and constraints between them. """ # __init__ should initialize the object with structural information (parameters, fields) @@ -297,10 +295,10 @@ def contents(self) -> None: pass @abstractmethod - def _def_to_proto(self) -> BaseBlockEdgirType: + def _def_to_proto(self) -> edgir.BlockLikeTypes: raise NotImplementedError - def _elaborated_def_to_proto(self) -> BaseBlockEdgirType: + def _elaborated_def_to_proto(self) -> edgir.BlockLikeTypes: prev_element = builder.push_element(self) assert prev_element is None try: @@ -313,7 +311,7 @@ def _elaborated_def_to_proto(self) -> BaseBlockEdgirType: return self._def_to_proto() - def _populate_def_proto_block_base(self, pb: BaseBlockEdgirType) -> None: + def _populate_def_proto_block_base(self, pb: edgir.BlockLikeTypes) -> None: """Populates the structural parts of a block proto: parameters, ports, superclasses""" assert self._elaboration_state == BlockElaborationState.post_contents or \ self._elaboration_state == BlockElaborationState.post_generate @@ -364,21 +362,21 @@ def _populate_def_proto_block_base(self, pb: BaseBlockEdgirType) -> None: self._populate_metadata(pb.meta, self._metadata, ref_map) - def _populate_def_proto_port_init(self, pb: BaseBlockEdgirType, ref_map: Refable.RefMapType) -> None: + def _populate_def_proto_port_init(self, pb: edgir.BlockLikeTypes, ref_map: Refable.RefMapType) -> None: for (name, port) in self._ports.items(): for (param, path, initializer) in port._get_initializers([name]): edgir.add_pair(pb.constraints, f"(init){'.'.join(path)}").CopyFrom( AssignBinding.make_assign(param, param._to_expr_type(initializer), ref_map) ) - def _populate_def_proto_param_init(self, pb: BaseBlockEdgirType, ref_map: Refable.RefMapType) -> None: + def _populate_def_proto_param_init(self, pb: edgir.BlockLikeTypes, ref_map: Refable.RefMapType) -> None: for (name, param) in self._parameters.items(): if param.initializer is not None: edgir.add_pair(pb.constraints, f'(init){name}').CopyFrom( AssignBinding.make_assign(param, param.initializer, ref_map) ) - def _populate_def_proto_block_contents(self, pb: BaseBlockEdgirType, ref_map: Refable.RefMapType) -> None: + def _populate_def_proto_block_contents(self, pb: edgir.BlockLikeTypes, ref_map: Refable.RefMapType) -> None: """Populates the contents of a block proto: constraints""" assert self._elaboration_state == BlockElaborationState.post_contents or \ self._elaboration_state == BlockElaborationState.post_generate @@ -388,7 +386,7 @@ def _populate_def_proto_block_contents(self, pb: BaseBlockEdgirType, ref_map: Re for (name, constraint) in self._constraints.items(): edgir.add_pair(pb.constraints, name).CopyFrom(constraint._expr_to_proto(ref_map)) - def _populate_def_proto_description(self, pb: BaseBlockEdgirType, ref_map: Refable.RefMapType) -> None: + def _populate_def_proto_description(self, pb: edgir.BlockLikeTypes, ref_map: Refable.RefMapType) -> None: description = self.description assert(description is None or isinstance(description, DescriptionString)) if isinstance(description, DescriptionString): diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index ccf8533bb..91256479f 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -263,7 +263,7 @@ def remap_arg(arg_name: str, arg_type: Type[ConstraintExpr], arg_value: Any) -> @non_library -class Block(BaseBlock[edgir.HierarchyBlock], metaclass=BlockMeta): +class Block(BaseBlock, metaclass=BlockMeta): """Part with a statically-defined subcircuit. Relations between contained parameters may only be expressed in the given constraint language. """ @@ -451,6 +451,9 @@ def _def_to_proto(self) -> edgir.HierarchyBlock: return pb + def _elaborated_def_to_proto(self) -> edgir.HierarchyBlock: + return cast(edgir.HierarchyBlock, super()._elaborated_def_to_proto()) + MixinType = TypeVar('MixinType', bound='BlockInterfaceMixin') def with_mixin(self, tpe: MixinType) -> MixinType: """Adds an interface mixin for this Block. Mainly useful for abstract blocks, e.g. IoController with HasI2s.""" diff --git a/edg/core/Link.py b/edg/core/Link.py index b9727cb5b..f74a7a159 100644 --- a/edg/core/Link.py +++ b/edg/core/Link.py @@ -32,7 +32,7 @@ def wrapped_init(self: Any, *args: Any, **kwargs: Any) -> None: @non_library -class Link(BaseBlock[edgir.Link], metaclass=LinkMeta): +class Link(BaseBlock, metaclass=LinkMeta): def __init__(self) -> None: super().__init__() self.parent: Optional[Port] = None @@ -85,3 +85,6 @@ def _def_to_proto(self) -> edgir.Link: ) return pb + + def _elaborated_def_to_proto(self) -> edgir.HierarchyBlock: + return cast(edgir.Link, super()._elaborated_def_to_proto()) From 9b22a2a19376cbc13a1dab1b5715f200b61302f4 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 12:40:31 -0800 Subject: [PATCH 03/21] wip moles being whacked --- edg/core/BlockInterfaceMixin.py | 2 +- edg/core/HierarchyBlock.py | 4 ++-- edg/core/MultipackBlock.py | 4 ++-- edg/core/PortTag.py | 5 ++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/edg/core/BlockInterfaceMixin.py b/edg/core/BlockInterfaceMixin.py index 7dc900582..77287f185 100644 --- a/edg/core/BlockInterfaceMixin.py +++ b/edg/core/BlockInterfaceMixin.py @@ -5,7 +5,7 @@ from .HdlUserExceptions import BlockDefinitionError from .HierarchyBlock import Block -MixinBaseType = TypeVar('MixinBaseType', bound='Block') +MixinBaseType = TypeVar('MixinBaseType', covariant=True, bound=Block, default=Block) @non_library diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index 91256479f..b1041c586 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -297,13 +297,13 @@ def __init__(self) -> None: self._blocks = self.manager.new_dict(Block) self._chains = self.manager.new_dict(ChainConnect, anon_prefix='anon_chain') - self._port_tags = IdentityDict[BasePort, Set[PortTag[Any]]]() + self._port_tags = IdentityDict[BasePort, Set[PortTag]]() def _get_ports_by_tag(self, tags: Set[PortTag]) -> List[BasePort]: out = [] for block_port_name, block_port in self._ports.items(): assert isinstance(block_port, BasePort) - port_tags: Set[PortTag[Any]] = self._port_tags.get(block_port, set()) + port_tags: Set[PortTag] = self._port_tags.get(block_port, set()) if port_tags.issuperset(tags): out.append(block_port) return out diff --git a/edg/core/MultipackBlock.py b/edg/core/MultipackBlock.py index bfe6d98fa..7ded6ddac 100644 --- a/edg/core/MultipackBlock.py +++ b/edg/core/MultipackBlock.py @@ -39,7 +39,7 @@ class PackedBlockParam(NamedTuple): # a parameter replicated from an array of b param: ConstraintExpr -PackedBlockElementType = TypeVar('PackedBlockElementType', bound=Block) +PackedBlockElementType = TypeVar('PackedBlockElementType', bound=Block, default=Block) class PackedBlockArray(Generic[PackedBlockElementType]): """A container "block" (for multipack packing only) for an arbitrary-length array of Blocks. This is meant to be analogous to Vector (port arrays), though there isn't an use case for this in general @@ -50,7 +50,7 @@ def __init__(self, tpe: PackedBlockElementType): self._parent: Optional[Block] = None self._allocates: List[Tuple[Optional[str], Block]] = [] # outer facing only, to track allocate for ref_map - def _bind(self, parent: Block) -> PackedBlockArray: + def _bind(self, parent: Block) -> PackedBlockArray[PackedBlockElementType]: clone = PackedBlockArray(self._tpe) clone._parent = parent clone._elt_sample = self._tpe._bind(parent) diff --git a/edg/core/PortTag.py b/edg/core/PortTag.py index bdae04a63..b4333ec57 100644 --- a/edg/core/PortTag.py +++ b/edg/core/PortTag.py @@ -4,9 +4,8 @@ from .Ports import BasePort, Port -PortType = TypeVar('PortType', bound=BasePort, covariant=True) # TODO this should just be Port, but current needed to work w/ BaseBlock.Port type hierarchy -class PortTag(Generic[PortType]): - def __init__(self, tpe: Type[PortType]): +class PortTag: + def __init__(self, tpe: Type[BasePort]) -> None: self.port_tpe = tpe From 6b9d53cdc73658245b3e0ec21727b7167f5a9d57 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 12:42:04 -0800 Subject: [PATCH 04/21] wip --- edg/core/DesignTop.py | 2 +- edg/core/HierarchyBlock.py | 2 +- edg/core/Link.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/edg/core/DesignTop.py b/edg/core/DesignTop.py index b94ff39b4..ee09b2010 100644 --- a/edg/core/DesignTop.py +++ b/edg/core/DesignTop.py @@ -66,7 +66,7 @@ def _elaborated_def_to_proto(self) -> edgir.HierarchyBlock: builder.pop_to(prev_element) return self._def_to_proto() - def _populate_def_proto_block_contents(self, pb: edgir.HierarchyBlock, ref_map: Refable.RefMapType) -> None: + def _populate_def_proto_block_contents(self, pb: edgir.BlockLikeTypes, ref_map: Refable.RefMapType) -> None: """Add multipack constraints""" super()._populate_def_proto_block_contents(pb, ref_map) diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index b1041c586..a85aaa5f6 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -318,7 +318,7 @@ def _build_ref_map(self, ref_map: Refable.RefMapType, prefix: edgir.LocalPath, * assert isinstance(block, Block) block._build_ref_map(ref_map, edgir.localpath_concat(prefix, name), interface_only=True) - def _populate_def_proto_block_base(self, pb: edgir.HierarchyBlock) -> None: + def _populate_def_proto_block_base(self, pb: edgir.BlockLikeTypes) -> None: super()._populate_def_proto_block_base(pb) # generate param defaults diff --git a/edg/core/Link.py b/edg/core/Link.py index f74a7a159..20df5bf31 100644 --- a/edg/core/Link.py +++ b/edg/core/Link.py @@ -86,5 +86,5 @@ def _def_to_proto(self) -> edgir.Link: return pb - def _elaborated_def_to_proto(self) -> edgir.HierarchyBlock: + def _elaborated_def_to_proto(self) -> edgir.Link: return cast(edgir.Link, super()._elaborated_def_to_proto()) From b3fdb063d223bf05b2fd152a66a310ac25a13597 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 12:45:21 -0800 Subject: [PATCH 05/21] Update MultipackBlock.py --- edg/core/MultipackBlock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edg/core/MultipackBlock.py b/edg/core/MultipackBlock.py index 7ded6ddac..7f8d8eee4 100644 --- a/edg/core/MultipackBlock.py +++ b/edg/core/MultipackBlock.py @@ -39,7 +39,7 @@ class PackedBlockParam(NamedTuple): # a parameter replicated from an array of b param: ConstraintExpr -PackedBlockElementType = TypeVar('PackedBlockElementType', bound=Block, default=Block) +PackedBlockElementType = TypeVar('PackedBlockElementType', covariant=True, bound=Block, default=Block) class PackedBlockArray(Generic[PackedBlockElementType]): """A container "block" (for multipack packing only) for an arbitrary-length array of Blocks. This is meant to be analogous to Vector (port arrays), though there isn't an use case for this in general From 9e1f3ae28b85de30d6b0b435187f0d237fb739ca Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 12:48:44 -0800 Subject: [PATCH 06/21] cleanup, remove abcmeta --- edg/abstract_parts/ESeriesUtil.py | 6 +++--- edg/abstract_parts/PinMappable.py | 3 +-- edg/core/BaseBackend.py | 4 ++-- edg/core/BaseRefinementPass.py | 4 ++-- edg/electronics_model/KicadFootprintData.py | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/edg/abstract_parts/ESeriesUtil.py b/edg/abstract_parts/ESeriesUtil.py index aa2123fb9..25c3eb19a 100644 --- a/edg/abstract_parts/ESeriesUtil.py +++ b/edg/abstract_parts/ESeriesUtil.py @@ -1,6 +1,6 @@ import itertools import math -from abc import ABCMeta, abstractmethod +from abc import abstractmethod from collections import deque from typing import Sequence, Optional, TypeVar, Tuple, List, Generic, Type, Union, overload @@ -123,7 +123,7 @@ def series_of(cls, value: float, *, default: Optional[SeriesDefaultType] = None) ESeriesRatioValueType = TypeVar('ESeriesRatioValueType', bound='ESeriesRatioValue') -class ESeriesRatioValue(Generic[ESeriesRatioValueType], metaclass=ABCMeta): +class ESeriesRatioValue(Generic[ESeriesRatioValueType]): """Abstract base class for the calculated output value for a resistor ... thing. Yes, not too descriptive, but example applications are: - resistive divider: ratio and impedance @@ -154,7 +154,7 @@ def intersects(self, spec: ESeriesRatioValueType) -> bool: ... -class ESeriesRatioUtil(Generic[ESeriesRatioValueType], metaclass=ABCMeta): +class ESeriesRatioUtil(Generic[ESeriesRatioValueType]): """Base class for an algorithm that searches pairs of E-series numbers to get some desired output (eg, ratio and impedance for a resistive divider). The output calculations are determined by the value_type class. diff --git a/edg/abstract_parts/PinMappable.py b/edg/abstract_parts/PinMappable.py index cd048e78f..fc36eac65 100644 --- a/edg/abstract_parts/PinMappable.py +++ b/edg/abstract_parts/PinMappable.py @@ -1,5 +1,4 @@ import itertools -from abc import ABCMeta from typing import List, Type, Tuple, Optional, Union, NamedTuple, Callable, Dict, Set, Any from ..electronics_model import * @@ -37,7 +36,7 @@ def generator_set_allocation(self, allocations: List['AllocatedResource']) -> No self.assign(self.actual_pin_assigns, allocation_strs) -class BasePinMapResource(metaclass=ABCMeta): +class BasePinMapResource: """Abstract base class for a resource definition for the pin mapping utility. Because these are so intertwined with the actual pin mapper, these are just named data structures - the logic for handling these is in the pin mapper.""" diff --git a/edg/core/BaseBackend.py b/edg/core/BaseBackend.py index b6e7bf76b..182877293 100644 --- a/edg/core/BaseBackend.py +++ b/edg/core/BaseBackend.py @@ -1,11 +1,11 @@ -from abc import ABCMeta, abstractmethod +from abc import abstractmethod from typing import List, Tuple, Dict from .. import edgir from .ScalaCompilerInterface import CompiledDesign -class BaseBackend(metaclass=ABCMeta): +class BaseBackend: """Abstract base class for a backend, which takes a compiled design, and returns a list of outputs associated with paths.""" # to be implemented per backend diff --git a/edg/core/BaseRefinementPass.py b/edg/core/BaseRefinementPass.py index 1f6cd5f93..3767e4549 100644 --- a/edg/core/BaseRefinementPass.py +++ b/edg/core/BaseRefinementPass.py @@ -1,11 +1,11 @@ -from abc import ABCMeta, abstractmethod +from abc import abstractmethod from typing import List, Tuple from .. import edgir from .ScalaCompilerInterface import CompiledDesign -class BaseRefinementPass(metaclass=ABCMeta): +class BaseRefinementPass: """Abstract base class for a refinement pass, which takes a compiled design, and returns a list of additional solved values to be added.""" # to be implemented per backend diff --git a/edg/electronics_model/KicadFootprintData.py b/edg/electronics_model/KicadFootprintData.py index f4c549245..8aafa5c51 100644 --- a/edg/electronics_model/KicadFootprintData.py +++ b/edg/electronics_model/KicadFootprintData.py @@ -1,4 +1,4 @@ -from typing import Optional, List, Tuple +from typing import Optional, List, Tuple, Dict from pydantic import RootModel, BaseModel import os @@ -9,7 +9,7 @@ class FootprintData(BaseModel): bbox: Tuple[float, float, float, float] # [x_min, y_min, x_max, y_max] -class FootprintJson(RootModel): # script relpath imports are weird so this is duplicated here +class FootprintJson(RootModel[Dict[str, FootprintData]]): # script relpath imports are weird so this is duplicated here root: dict[str, FootprintData] # footprint name -> data From c83253aba44b68a577da49a6702fc1c727546d3b Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 12:51:44 -0800 Subject: [PATCH 07/21] wip --- edg/core/ArrayExpr.py | 6 +++--- .../resources/build_kicad_footprint_table.py | 2 +- edg/jlcparts/JlcPartsBase.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/edg/core/ArrayExpr.py b/edg/core/ArrayExpr.py index 1880fc6ff..f2c4a9f6a 100644 --- a/edg/core/ArrayExpr.py +++ b/edg/core/ArrayExpr.py @@ -25,9 +25,9 @@ def expr_to_proto(self, expr: ConstraintExpr, ref_map: Refable.RefMapType) -> ed SelfType = TypeVar('SelfType', bound='ArrayExpr') -ArrayEltType = TypeVar('ArrayEltType', bound=ConstraintExpr, covariant=True) -ArrayWrappedType = TypeVar('ArrayWrappedType', covariant=True) -ArrayCastableType = TypeVar('ArrayCastableType', contravariant=True) +ArrayEltType = TypeVar('ArrayEltType', bound=ConstraintExpr, covariant=True, default=ConstraintExpr) +ArrayWrappedType = TypeVar('ArrayWrappedType', covariant=True, default=Any) +ArrayCastableType = TypeVar('ArrayCastableType', contravariant=True, default=Any) class ArrayExpr(ConstraintExpr[ArrayWrappedType, ArrayCastableType], Generic[ArrayEltType, ArrayWrappedType, ArrayCastableType]): """An Array-valued ConstraintExpr (a variable-sized list of ConstraintExpr). diff --git a/edg/electronics_model/resources/build_kicad_footprint_table.py b/edg/electronics_model/resources/build_kicad_footprint_table.py index 09d22e0e4..7a5067c9e 100644 --- a/edg/electronics_model/resources/build_kicad_footprint_table.py +++ b/edg/electronics_model/resources/build_kicad_footprint_table.py @@ -123,7 +123,7 @@ class FootprintData(BaseModel): bbox: Tuple[float, float, float, float] # [x_min, y_min, x_max, y_max] -class FootprintJson(RootModel): # script relpath imports are weird so this is duplicated here +class FootprintJson(RootModel[Dict[str, FootprintData]]): # script relpath imports are weird so this is duplicated here root: dict[str, FootprintData] # footprint name -> data diff --git a/edg/jlcparts/JlcPartsBase.py b/edg/jlcparts/JlcPartsBase.py index 09329b1a2..20a64e51c 100644 --- a/edg/jlcparts/JlcPartsBase.py +++ b/edg/jlcparts/JlcPartsBase.py @@ -26,7 +26,7 @@ class JlcPartsAttributeEntry(BaseModel): ParsedType = TypeVar('ParsedType') # can't be inside the class or it gets confused as a pydantic model entry -class JlcPartsAttributes(RootModel): +class JlcPartsAttributes(RootModel[Dict[str, JlcPartsAttributeEntry]]): root: dict[str, JlcPartsAttributeEntry] def get(self, key: str, expected_type: Type[ParsedType], default: Optional[ParsedType] = None, sub: str = 'default') -> ParsedType: @@ -55,7 +55,7 @@ class JlcPartsPriceEntry(BaseModel): qTo: Optional[int] # None = top bucket -class JlcPartsPrice(RootModel): +class JlcPartsPrice(RootModel[List[JlcPartsPriceEntry]]): root: list[JlcPartsPriceEntry] def for_min_qty(self) -> float: @@ -69,7 +69,7 @@ def for_min_qty(self) -> float: return min_seen_price[1] -class JlcPartsStockFile(RootModel): +class JlcPartsStockFile(RootModel[Dict[str, int]]): root: dict[str, int] # LCSC to stock level From 152e8b45cde3adb99ee7050dca5d8fd23144ea86 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 12:53:05 -0800 Subject: [PATCH 08/21] Update Array.py --- edg/core/Array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edg/core/Array.py b/edg/core/Array.py index 5292e38de..2a4d2961c 100644 --- a/edg/core/Array.py +++ b/edg/core/Array.py @@ -56,7 +56,7 @@ def _get_elt_sample(self) -> BasePort: # A 'fake'/'intermediate'/'view' vector object used as a return in map_extract operations. -VectorType = TypeVar('VectorType', bound='Port') +VectorType = TypeVar('VectorType', covariant=True, bound=Port, default=Port) @non_library class DerivedVector(BaseVector, Generic[VectorType]): # TODO: Library types need to be removed from the type hierarchy, because this does not generate into a library elt From 391e009f1f92fa51aa2ff3f938d62d8572a54f54 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 13:09:04 -0800 Subject: [PATCH 09/21] wip --- edg/abstract_parts/ESeriesUtil.py | 4 ++-- edg/abstract_parts/OpampCircuits.py | 4 ++-- edg/abstract_parts/PartsTable.py | 16 ++++++++-------- edg/abstract_parts/PinMappable.py | 8 ++++---- edg/core/Array.py | 6 +++--- edg/core/MultipackBlock.py | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/edg/abstract_parts/ESeriesUtil.py b/edg/abstract_parts/ESeriesUtil.py index 25c3eb19a..68cd6d53f 100644 --- a/edg/abstract_parts/ESeriesUtil.py +++ b/edg/abstract_parts/ESeriesUtil.py @@ -2,7 +2,7 @@ import math from abc import abstractmethod from collections import deque -from typing import Sequence, Optional, TypeVar, Tuple, List, Generic, Type, Union, overload +from typing import Any, Sequence, Optional, TypeVar, Tuple, List, Generic, Type, Union, overload from ..electronics_model import * @@ -122,7 +122,7 @@ def series_of(cls, value: float, *, default: Optional[SeriesDefaultType] = None) return cls.VALUE_SERIES.get(cls.round_sig(normalized_value, cls.ROUND_DIGITS), default) -ESeriesRatioValueType = TypeVar('ESeriesRatioValueType', bound='ESeriesRatioValue') +ESeriesRatioValueType = TypeVar('ESeriesRatioValueType', bound='ESeriesRatioValue[Any]') class ESeriesRatioValue(Generic[ESeriesRatioValueType]): """Abstract base class for the calculated output value for a resistor ... thing. Yes, not too descriptive, but example applications are: diff --git a/edg/abstract_parts/OpampCircuits.py b/edg/abstract_parts/OpampCircuits.py index 8ae0236f6..3e899f7c8 100644 --- a/edg/abstract_parts/OpampCircuits.py +++ b/edg/abstract_parts/OpampCircuits.py @@ -36,7 +36,7 @@ def contents(self) -> None: self.import_kicad(self.file_path("resources", f"{self.__class__.__name__}.kicad_sch")) -class AmplifierValues(ESeriesRatioValue): +class AmplifierValues(ESeriesRatioValue['AmplifierValues']): def __init__(self, amplification: Range, parallel_impedance: Range): self.amplification = amplification # amplification factor from in to out self.parallel_impedance = parallel_impedance # parallel impedance into the opamp negative pin @@ -158,7 +158,7 @@ def generate(self) -> None: self.assign(self.actual_amplification, 1 + (self.r1.actual_resistance / self.r2.actual_resistance)) -class DifferentialValues(ESeriesRatioValue): +class DifferentialValues(ESeriesRatioValue['DifferentialValues']): def __init__(self, ratio: Range, input_impedance: Range): self.ratio = ratio # ratio from difference between inputs to output self.input_impedance = input_impedance # resistance of the input resistor diff --git a/edg/abstract_parts/PartsTable.py b/edg/abstract_parts/PartsTable.py index dee001cba..4a22a5188 100644 --- a/edg/abstract_parts/PartsTable.py +++ b/edg/abstract_parts/PartsTable.py @@ -109,14 +109,14 @@ def filter(self, fn: Callable[[PartsTableRow], bool]) -> PartsTable: if fn(row)] return PartsTable(new_rows) - def map_new_columns(self, fn: Callable[[PartsTableRow], Optional[Dict[PartsTableColumn[Any], Any]]], + def map_new_columns(self, fn: Callable[[PartsTableRow], Optional[Dict[PartsTableColumn, Any]]], overwrite: bool = False) -> PartsTable: """Creates a new table (deep copy) with additional rows. All entries must have the same keys. Specify overwrite=True to overwrite an existing row. To preserve the existing value (no-op), return it. Return None to drop the row.""" new_rows: List[PartsTableRow] = [] - first_keys: Optional[KeysView] = None + first_keys: Optional[KeysView[PartsTableColumn]] = None for row in self.rows: new_columns = fn(row) if new_columns is None: @@ -164,7 +164,7 @@ def first(self, err: str="no elements in list") -> PartsTableRow: UserFnMetaParams = ParamSpec('UserFnMetaParams') -UserFnType = TypeVar('UserFnType', bound=Callable, covariant=True) +UserFnType = TypeVar('UserFnType', bound=Callable[..., Any], covariant=True) class UserFnSerialiable(Protocol[UserFnMetaParams, UserFnType]): """A protocol that marks functions as usable in deserialize, that they have been registered.""" _is_serializable: None # guard attribute @@ -188,11 +188,11 @@ class ExperimentalUserFnPartsTable(PartsTable): _FN_SERIALIZATION_SEPARATOR = ";" - _user_fns: Dict[str, Tuple[Callable, Sequence[Type]]] = {} # name -> fn, [arg types] - _fn_name_dict: Dict[Callable, str] = {} + _user_fns: Dict[str, Tuple[Callable[..., Any], Sequence[Type[Any]]]] = {} # name -> fn, [arg types] + _fn_name_dict: Dict[Callable[..., Any], str] = {} @staticmethod - def user_fn(param_types: Sequence[Type] = []) -> Callable[[Callable[UserFnMetaParams, UserFnType]], + def user_fn(param_types: Sequence[Type[Any]] = []) -> Callable[[Callable[UserFnMetaParams, UserFnType]], UserFnSerialiable[UserFnMetaParams, UserFnType]]: def decorator(fn: Callable[UserFnMetaParams, UserFnType]) -> UserFnSerialiable[UserFnMetaParams, UserFnType]: """Decorator to register a user function that can be used in ExperimentalUserFnPartsTable.""" @@ -211,7 +211,7 @@ def serialize_fn(cls, fn: UserFnSerialiable[UserFnMetaParams, UserFnType], if fn not in cls._fn_name_dict: raise ValueError(f"Function {fn} not registered.") fn_ctor, fn_argtypes = cls._user_fns[fn.__name__] - def serialize_arg(tpe: Type, val: Any) -> str: + def serialize_arg(tpe: Type[Any], val: Any) -> str: assert isinstance(val, tpe), f"in serialize {val}, expected {tpe}, got {type(val)}" if tpe is bool: return str(val) @@ -234,7 +234,7 @@ def deserialize_fn(cls, serialized: str) -> Any: raise ValueError(f"Function {serialized} not registered.") fn_ctor, fn_argtypes = cls._user_fns[split[0]] assert len(split) == len(fn_argtypes) + 1 - def deserialize_arg(tpe: Type, val: str) -> Any: + def deserialize_arg(tpe: Type[Any], val: str) -> Any: if tpe is bool: return val == 'True' elif tpe is int: diff --git a/edg/abstract_parts/PinMappable.py b/edg/abstract_parts/PinMappable.py index fc36eac65..0fb59c226 100644 --- a/edg/abstract_parts/PinMappable.py +++ b/edg/abstract_parts/PinMappable.py @@ -1,5 +1,5 @@ import itertools -from typing import List, Type, Tuple, Optional, Union, NamedTuple, Callable, Dict, Set, Any +from typing import List, Type, Tuple, Optional, Union, NamedTuple, Callable, Dict, Set, Any, Mapping from ..electronics_model import * @@ -141,10 +141,10 @@ def __eq__(self, other: Any) -> bool: # Defines a way to convert port models into related types for use in bundles, for example from the # DigitalBidir in pin definitions to the DigitalSource/Sink used by the Uart bundle. # Specified as entries of target port type: (source port type, conversion function) -PortTransformsType = Dict[Type, Tuple[Type, Callable]] +PortTransformsType = Mapping[Type[Port], Tuple[Type[Port], Callable[[Port], Port]]] DefaultPortTransforms: PortTransformsType = { - DigitalSource: (DigitalBidir, DigitalSource.from_bidir), - DigitalSink: (DigitalBidir, DigitalSink.from_bidir), + DigitalSource: (DigitalBidir, DigitalSource.from_bidir), # type: ignore + DigitalSink: (DigitalBidir, DigitalSink.from_bidir), # type: ignore } diff --git a/edg/core/Array.py b/edg/core/Array.py index 2a4d2961c..3beef3136 100644 --- a/edg/core/Array.py +++ b/edg/core/Array.py @@ -310,14 +310,14 @@ def map_extract(self, selector: Callable[[VectorType], StringExpr]) -> ArrayStri @overload def map_extract(self, selector: Callable[[VectorType], ExtractPortType]) -> DerivedVector[ExtractPortType]: ... - def map_extract(self, selector: Callable[[VectorType], Union[ConstraintExpr, BasePort]]) -> Union[ArrayExpr, DerivedVector]: + def map_extract(self, selector: Callable[[VectorType], Union[ConstraintExpr, Port]]) -> Union[ArrayExpr, DerivedVector]: param = selector(self._elt_sample) if isinstance(param, ConstraintExpr): # TODO check that returned type is child return ArrayExpr.array_of_elt(param)._bind(MapExtractBinding(self, param)) - elif isinstance(param, BasePort): + elif isinstance(param, Port): return DerivedVector(self, param) else: - raise EdgTypeError(f"selector return", param, (ConstraintExpr, BasePort)) + raise EdgTypeError(f"selector return", param, (ConstraintExpr, Port)) def any_connected(self) -> BoolExpr: return self.any(lambda port: port.is_connected()) diff --git a/edg/core/MultipackBlock.py b/edg/core/MultipackBlock.py index 7f8d8eee4..1a7b3abb1 100644 --- a/edg/core/MultipackBlock.py +++ b/edg/core/MultipackBlock.py @@ -20,14 +20,14 @@ class PackedBlockAllocate(NamedTuple): suggested_name: Optional[str] -ArrayPortType = TypeVar('ArrayPortType', bound=Port) +ArrayPortType = TypeVar('ArrayPortType', covariant=True, bound=Port, default=Port) class PackedBlockPortArray(Generic[ArrayPortType]): def __init__(self, parent: PackedBlockArray, port: ArrayPortType): self.parent = parent self.port = port -ArrayParamType = TypeVar('ArrayParamType', bound=ConstraintExpr) +ArrayParamType = TypeVar('ArrayParamType', covariant=True, bound=ConstraintExpr, default=ConstraintExpr) class PackedBlockParamArray(Generic[ArrayParamType]): # an array of parameters from an array of parts def __init__(self, parent: PackedBlockArray, param: ArrayParamType): self.parent = parent From b4f041fed0d9febd75b78712f2704f51fb20829a Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 13:22:02 -0800 Subject: [PATCH 10/21] proof of concept for strongly typed standard footprints --- edg/abstract_parts/AbstractBjt.py | 42 ++++++++++++------------- edg/abstract_parts/StandardFootprint.py | 12 +++---- edg/core/ConstraintExpr.py | 2 +- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/edg/abstract_parts/AbstractBjt.py b/edg/abstract_parts/AbstractBjt.py index 74c82d126..75141986f 100644 --- a/edg/abstract_parts/AbstractBjt.py +++ b/edg/abstract_parts/AbstractBjt.py @@ -7,28 +7,8 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class BjtStandardFootprint(StandardFootprint['Bjt']): - REFDES_PREFIX = 'Q' - - FOOTPRINT_PINNING_MAP = { - ( - 'Package_TO_SOT_SMD:SOT-23', - 'Package_TO_SOT_SMD:SOT-323_SC-70', - ): lambda block: { - '1': block.base, - '2': block.emitter, - '3': block.collector, - }, - 'Package_TO_SOT_SMD:SOT-89-3': lambda block: { - '1': block.base, - '2': block.collector, - '3': block.emitter, - }, - } - - @abstract_block -class Bjt(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint): +class Bjt(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint['Bjt']): """Base class for untyped BJTs """ _STANDARD_FOOTPRINT = BjtStandardFootprint @@ -83,6 +63,26 @@ def contents(self) -> None: ) +class BjtStandardFootprint(StandardFootprint[Bjt]): + REFDES_PREFIX = 'Q' + + FOOTPRINT_PINNING_MAP = { + ( + 'Package_TO_SOT_SMD:SOT-23', + 'Package_TO_SOT_SMD:SOT-323_SC-70', + ): lambda block: { + '1': block.base, + '2': block.emitter, + '3': block.collector, + }, + 'Package_TO_SOT_SMD:SOT-89-3': lambda block: { + '1': block.base, + '2': block.collector, + '3': block.emitter, + }, + } + + class TableBjt(PartsTableSelector, Bjt): VCE_RATING = PartsTableColumn(Range) ICE_RATING = PartsTableColumn(Range) diff --git a/edg/abstract_parts/StandardFootprint.py b/edg/abstract_parts/StandardFootprint.py index cf9171b52..aa6dc3b95 100644 --- a/edg/abstract_parts/StandardFootprint.py +++ b/edg/abstract_parts/StandardFootprint.py @@ -8,11 +8,11 @@ class StandardFootprint(Generic[StandardPinningType]): """A shared helper class that provides a table to provide standard pin mapping from footprints.""" REFDES_PREFIX: ClassVar[str] - FOOTPRINT_PINNING_MAP: ClassVar[Dict[Union[str, Tuple[str, ...]], PinningFunction]] # user-specified - _EXPANDED_FOOTPRINT_PINNING_MAP: Optional[Dict[str, PinningFunction]] = None # automatically-generated from above + FOOTPRINT_PINNING_MAP: ClassVar[Dict[Union[str, Tuple[str, ...]], PinningFunction[StandardPinningType]]] # user-specified + _EXPANDED_FOOTPRINT_PINNING_MAP: Optional[Dict[str, PinningFunction[StandardPinningType]]] = None # automatically-generated from above @classmethod - def _footprint_pinning_map(cls) -> Dict[str, PinningFunction]: + def _footprint_pinning_map(cls) -> Dict[str, PinningFunction[StandardPinningType]]: """Returns the footprint pinning map as a dict of footprint name -> pinning fn, generating and caching the expanded table as needed""" if cls._EXPANDED_FOOTPRINT_PINNING_MAP is None: @@ -36,8 +36,6 @@ def _make_pinning(cls, block: StandardPinningType, footprint: str) -> Dict[str, return cls._footprint_pinning_map()[footprint](block) -class HasStandardFootprint: +class HasStandardFootprint(Generic[StandardPinningType]): """Base class that defines that a class supports a StandardFootprint""" - # TODO: this should be typed on the StandardFootprint type, but type vars in ClassVar are disallowed - # https://github.com/python/mypy/issues/5144 - _STANDARD_FOOTPRINT: ClassVar[Type[StandardFootprint]] + _STANDARD_FOOTPRINT: ClassVar[Type[StandardFootprint[StandardPinningType]]] diff --git a/edg/core/ConstraintExpr.py b/edg/core/ConstraintExpr.py index e473c8506..efcb8c978 100644 --- a/edg/core/ConstraintExpr.py +++ b/edg/core/ConstraintExpr.py @@ -187,7 +187,7 @@ def then_else(self, then_val: Any, else_val: Any) -> ConstraintExpr: NumLikeSelfType = TypeVar('NumLikeSelfType', bound='NumLikeExpr') -NumLikeCastable = TypeVar('NumLikeCastable') # should include the self type +NumLikeCastable = TypeVar('NumLikeCastable', default=Any) # should include the self type class NumLikeExpr(ConstraintExpr[WrappedType, NumLikeCastable], Generic[WrappedType, NumLikeCastable]): """Trait for numeric-like expressions, providing common arithmetic operations""" From 764e6308f17f72d0003025e9214ab9603cc9837b Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 13:29:31 -0800 Subject: [PATCH 11/21] backports to TypeVar w/ default --- edg/abstract_parts/PartsTable.py | 4 ++-- edg/core/Array.py | 1 + edg/core/ArrayExpr.py | 2 +- edg/core/BlockInterfaceMixin.py | 1 + edg/core/ConstraintExpr.py | 2 +- edg/core/MultipackBlock.py | 3 ++- edg/core/Ports.py | 1 + edg/electronics_model/CircuitBlock.py | 4 +--- pyproject.toml | 2 +- 9 files changed, 11 insertions(+), 9 deletions(-) diff --git a/edg/abstract_parts/PartsTable.py b/edg/abstract_parts/PartsTable.py index 4a22a5188..8202c4b2b 100644 --- a/edg/abstract_parts/PartsTable.py +++ b/edg/abstract_parts/PartsTable.py @@ -2,10 +2,10 @@ import csv import itertools -from typing import TypeVar, Generic, Type, overload, Union, Callable, List, Dict, Any, KeysView, Optional, OrderedDict, \ +from typing import Generic, Type, overload, Union, Callable, List, Dict, Any, KeysView, Optional, OrderedDict, \ cast, Tuple, Sequence, Protocol -from typing_extensions import ParamSpec +from typing_extensions import ParamSpec, TypeVar from ..core.Range import Range diff --git a/edg/core/Array.py b/edg/core/Array.py index 3beef3136..efc15fe2d 100644 --- a/edg/core/Array.py +++ b/edg/core/Array.py @@ -3,6 +3,7 @@ import itertools from abc import abstractmethod from typing import * +from typing_extensions import TypeVar from deprecated import deprecated from .HdlUserExceptions import EdgTypeError diff --git a/edg/core/ArrayExpr.py b/edg/core/ArrayExpr.py index f2c4a9f6a..cee8926f4 100644 --- a/edg/core/ArrayExpr.py +++ b/edg/core/ArrayExpr.py @@ -1,6 +1,7 @@ from __future__ import annotations from typing import * +from typing_extensions import TypeVar from .. import edgir from .Binding import EqOp, ArrayBinding, UnarySetOpBinding, BinarySetOpBinding @@ -8,7 +9,6 @@ BoolExpr, BoolLike, StringLike, \ NumericOp, BoolOp, RangeSetOp, Binding, StringExpr, IntExpr from .Core import Refable -from .IdentityDict import IdentityDict from .Ports import BasePort from .Range import Range diff --git a/edg/core/BlockInterfaceMixin.py b/edg/core/BlockInterfaceMixin.py index 77287f185..481d39726 100644 --- a/edg/core/BlockInterfaceMixin.py +++ b/edg/core/BlockInterfaceMixin.py @@ -1,4 +1,5 @@ from typing import TypeVar, Generic, Type, List, Optional, get_args, get_origin, Tuple, Callable, Any +from typing_extensions import TypeVar from .Core import non_library, HasMetadata from .Blocks import AbstractBlockProperty diff --git a/edg/core/ConstraintExpr.py b/edg/core/ConstraintExpr.py index efcb8c978..5a52ef02d 100644 --- a/edg/core/ConstraintExpr.py +++ b/edg/core/ConstraintExpr.py @@ -4,6 +4,7 @@ from deprecated import deprecated from itertools import chain from typing import * +from typing_extensions import TypeVar from .Binding import Binding, ParamBinding, BoolLiteralBinding, IntLiteralBinding, \ FloatLiteralBinding, RangeLiteralBinding, StringLiteralBinding, RangeBuilderBinding, \ @@ -11,7 +12,6 @@ from .Binding import NumericOp, BoolOp, EqOp, OrdOp, RangeSetOp from .Builder import builder from .Core import Refable -from .IdentityDict import IdentityDict from .Range import Range from .. import edgir diff --git a/edg/core/MultipackBlock.py b/edg/core/MultipackBlock.py index 1a7b3abb1..aa976e8c7 100644 --- a/edg/core/MultipackBlock.py +++ b/edg/core/MultipackBlock.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TypeVar, NamedTuple, Optional, Union, List, Tuple, Generic, Callable, overload +from typing import NamedTuple, Optional, Union, List, Tuple, Generic, Callable, overload +from typing_extensions import TypeVar from deprecated import deprecated from .Array import Vector diff --git a/edg/core/Ports.py b/edg/core/Ports.py index c1fb689c0..0b46ad34a 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -3,6 +3,7 @@ import itertools from abc import abstractmethod from typing import * +from typing_extensions import TypeVar from .. import edgir from .Binding import ParamBinding, IsConnectedBinding, NameBinding diff --git a/edg/electronics_model/CircuitBlock.py b/edg/electronics_model/CircuitBlock.py index a26bb10aa..f3b5bf9f9 100644 --- a/edg/electronics_model/CircuitBlock.py +++ b/edg/electronics_model/CircuitBlock.py @@ -1,11 +1,9 @@ from __future__ import annotations from typing import * +from typing_extensions import TypeVar -from .. import edgir from ..core import * -from ..core import IdentityDict # TODO: this is ugly -from ..core.ConstraintExpr import Refable from .KiCadImportableBlock import KiCadImportableBlock from ..core.HdlUserExceptions import EdgTypeError diff --git a/pyproject.toml b/pyproject.toml index f571cafc3..214cee0d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "protobuf >= 3.20.0", "sexpdata==0.0.3", "Deprecated", - "typing_extensions", + "typing_extensions >= 4.4.0", ] requires-python = ">=3.9" From 7c6ea53518ba849a5a99d9457c67a8d666df4042 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 13:51:01 -0800 Subject: [PATCH 12/21] wip --- edg/abstract_parts/AbstractBjt.py | 2 +- edg/abstract_parts/AbstractCapacitor.py | 142 ++++++++++++------------ edg/abstract_parts/StandardFootprint.py | 8 +- 3 files changed, 77 insertions(+), 75 deletions(-) diff --git a/edg/abstract_parts/AbstractBjt.py b/edg/abstract_parts/AbstractBjt.py index 75141986f..487d15bef 100644 --- a/edg/abstract_parts/AbstractBjt.py +++ b/edg/abstract_parts/AbstractBjt.py @@ -8,7 +8,7 @@ @abstract_block -class Bjt(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint['Bjt']): +class Bjt(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint): """Base class for untyped BJTs """ _STANDARD_FOOTPRINT = BjtStandardFootprint diff --git a/edg/abstract_parts/AbstractCapacitor.py b/edg/abstract_parts/AbstractCapacitor.py index e8f420beb..8918435eb 100644 --- a/edg/abstract_parts/AbstractCapacitor.py +++ b/edg/abstract_parts/AbstractCapacitor.py @@ -11,77 +11,6 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class CapacitorStandardFootprint(StandardFootprint['Capacitor']): - REFDES_PREFIX = 'C' - - # IMPORTANT! DummyFootprint doesn't use this, it will break on anything that isn't this pinning - FOOTPRINT_PINNING_MAP = { - ( - 'Capacitor_SMD:C_0201_0603Metric', - 'Capacitor_SMD:C_0402_1005Metric', - 'Capacitor_SMD:C_0603_1608Metric', - 'Capacitor_SMD:C_0805_2012Metric', - 'Capacitor_SMD:C_1206_3216Metric', - 'Capacitor_SMD:C_1210_3225Metric', - 'Capacitor_SMD:C_1812_4532Metric', - 'Capacitor_SMD:C_2512_6332Metric', - - 'Capacitor_SMD:CP_Elec_3x5.3', - 'Capacitor_SMD:CP_Elec_3x5.4', - 'Capacitor_SMD:CP_Elec_4x3', - 'Capacitor_SMD:CP_Elec_4x3.9', - 'Capacitor_SMD:CP_Elec_4x4.5', - 'Capacitor_SMD:CP_Elec_4x5.3', - 'Capacitor_SMD:CP_Elec_4x5.4', - 'Capacitor_SMD:CP_Elec_4x5.7', - 'Capacitor_SMD:CP_Elec_4x5.8', - 'Capacitor_SMD:CP_Elec_5x3', - 'Capacitor_SMD:CP_Elec_5x3.9', - 'Capacitor_SMD:CP_Elec_5x4.4', - 'Capacitor_SMD:CP_Elec_5x4.5', - 'Capacitor_SMD:CP_Elec_5x5.3', - 'Capacitor_SMD:CP_Elec_5x5.4', - 'Capacitor_SMD:CP_Elec_5x5.7', - 'Capacitor_SMD:CP_Elec_5x5.8', - 'Capacitor_SMD:CP_Elec_5x5.9', - 'Capacitor_SMD:CP_Elec_6.3x3', - 'Capacitor_SMD:CP_Elec_6.3x3.9', - 'Capacitor_SMD:CP_Elec_6.3x4.5', - 'Capacitor_SMD:CP_Elec_6.3x4.9', - 'Capacitor_SMD:CP_Elec_6.3x5.2', - 'Capacitor_SMD:CP_Elec_6.3x5.3', - 'Capacitor_SMD:CP_Elec_6.3x5.4', - 'Capacitor_SMD:CP_Elec_6.3x5.7', - 'Capacitor_SMD:CP_Elec_6.3x5.8', - 'Capacitor_SMD:CP_Elec_6.3x5.9', - 'Capacitor_SMD:CP_Elec_6.3x7.7', - 'Capacitor_SMD:CP_Elec_6.3x9.9', - 'Capacitor_SMD:CP_Elec_8x5.4', - 'Capacitor_SMD:CP_Elec_8x6.2', - 'Capacitor_SMD:CP_Elec_8x6.5', - 'Capacitor_SMD:CP_Elec_8x6.7', - 'Capacitor_SMD:CP_Elec_8x6.9', - 'Capacitor_SMD:CP_Elec_8x10', - 'Capacitor_SMD:CP_Elec_8x10.5', - 'Capacitor_SMD:CP_Elec_8x11.9', - 'Capacitor_SMD:CP_Elec_10x7.7', - 'Capacitor_SMD:CP_Elec_10x7.9', - 'Capacitor_SMD:CP_Elec_10x10', - 'Capacitor_SMD:CP_Elec_10x10.5', - 'Capacitor_SMD:CP_Elec_10x12.5', - 'Capacitor_SMD:CP_Elec_10x12.6', - 'Capacitor_SMD:CP_Elec_10x14.3', - 'Capacitor_SMD:CP_Elec_16x17.5', - 'Capacitor_SMD:CP_Elec_16x22', - 'Capacitor_SMD:CP_Elec_18x7.5', - 'Capacitor_SMD:CP_Elec_18x22', - ): lambda block: { - '1': block.pos, - '2': block.neg, - }, - } - - @abstract_block class UnpolarizedCapacitor(PassiveComponent): """Base type for a capacitor, that defines its parameters and without ports (since capacitors can be polarized)""" @@ -156,6 +85,77 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.neg = self.Port(Passive.empty()) +class CapacitorStandardFootprint(StandardFootprint[Capacitor]): + REFDES_PREFIX = 'C' + + # IMPORTANT! DummyFootprint doesn't use this, it will break on anything that isn't this pinning + FOOTPRINT_PINNING_MAP = { + ( + 'Capacitor_SMD:C_0201_0603Metric', + 'Capacitor_SMD:C_0402_1005Metric', + 'Capacitor_SMD:C_0603_1608Metric', + 'Capacitor_SMD:C_0805_2012Metric', + 'Capacitor_SMD:C_1206_3216Metric', + 'Capacitor_SMD:C_1210_3225Metric', + 'Capacitor_SMD:C_1812_4532Metric', + 'Capacitor_SMD:C_2512_6332Metric', + + 'Capacitor_SMD:CP_Elec_3x5.3', + 'Capacitor_SMD:CP_Elec_3x5.4', + 'Capacitor_SMD:CP_Elec_4x3', + 'Capacitor_SMD:CP_Elec_4x3.9', + 'Capacitor_SMD:CP_Elec_4x4.5', + 'Capacitor_SMD:CP_Elec_4x5.3', + 'Capacitor_SMD:CP_Elec_4x5.4', + 'Capacitor_SMD:CP_Elec_4x5.7', + 'Capacitor_SMD:CP_Elec_4x5.8', + 'Capacitor_SMD:CP_Elec_5x3', + 'Capacitor_SMD:CP_Elec_5x3.9', + 'Capacitor_SMD:CP_Elec_5x4.4', + 'Capacitor_SMD:CP_Elec_5x4.5', + 'Capacitor_SMD:CP_Elec_5x5.3', + 'Capacitor_SMD:CP_Elec_5x5.4', + 'Capacitor_SMD:CP_Elec_5x5.7', + 'Capacitor_SMD:CP_Elec_5x5.8', + 'Capacitor_SMD:CP_Elec_5x5.9', + 'Capacitor_SMD:CP_Elec_6.3x3', + 'Capacitor_SMD:CP_Elec_6.3x3.9', + 'Capacitor_SMD:CP_Elec_6.3x4.5', + 'Capacitor_SMD:CP_Elec_6.3x4.9', + 'Capacitor_SMD:CP_Elec_6.3x5.2', + 'Capacitor_SMD:CP_Elec_6.3x5.3', + 'Capacitor_SMD:CP_Elec_6.3x5.4', + 'Capacitor_SMD:CP_Elec_6.3x5.7', + 'Capacitor_SMD:CP_Elec_6.3x5.8', + 'Capacitor_SMD:CP_Elec_6.3x5.9', + 'Capacitor_SMD:CP_Elec_6.3x7.7', + 'Capacitor_SMD:CP_Elec_6.3x9.9', + 'Capacitor_SMD:CP_Elec_8x5.4', + 'Capacitor_SMD:CP_Elec_8x6.2', + 'Capacitor_SMD:CP_Elec_8x6.5', + 'Capacitor_SMD:CP_Elec_8x6.7', + 'Capacitor_SMD:CP_Elec_8x6.9', + 'Capacitor_SMD:CP_Elec_8x10', + 'Capacitor_SMD:CP_Elec_8x10.5', + 'Capacitor_SMD:CP_Elec_8x11.9', + 'Capacitor_SMD:CP_Elec_10x7.7', + 'Capacitor_SMD:CP_Elec_10x7.9', + 'Capacitor_SMD:CP_Elec_10x10', + 'Capacitor_SMD:CP_Elec_10x10.5', + 'Capacitor_SMD:CP_Elec_10x12.5', + 'Capacitor_SMD:CP_Elec_10x12.6', + 'Capacitor_SMD:CP_Elec_10x14.3', + 'Capacitor_SMD:CP_Elec_16x17.5', + 'Capacitor_SMD:CP_Elec_16x22', + 'Capacitor_SMD:CP_Elec_18x7.5', + 'Capacitor_SMD:CP_Elec_18x22', + ): lambda block: { + '1': block.pos, + '2': block.neg, + }, + } + + @abstract_block class CeramicCapacitor(Capacitor): """Abstract base class for ceramic capacitors, which appear more ideal in terms of lower ESP""" diff --git a/edg/abstract_parts/StandardFootprint.py b/edg/abstract_parts/StandardFootprint.py index aa6dc3b95..66a14b3a6 100644 --- a/edg/abstract_parts/StandardFootprint.py +++ b/edg/abstract_parts/StandardFootprint.py @@ -1,4 +1,5 @@ -from typing import Optional, Dict, TypeVar, Generic, Callable, Union, Tuple, ClassVar, Protocol, Type +from typing import Optional, Dict, TypeVar, Generic, Callable, Union, Tuple, ClassVar, Type +from typing_extensions import Self from ..electronics_model import * @@ -36,6 +37,7 @@ def _make_pinning(cls, block: StandardPinningType, footprint: str) -> Dict[str, return cls._footprint_pinning_map()[footprint](block) -class HasStandardFootprint(Generic[StandardPinningType]): +@non_library +class HasStandardFootprint(Block): """Base class that defines that a class supports a StandardFootprint""" - _STANDARD_FOOTPRINT: ClassVar[Type[StandardFootprint[StandardPinningType]]] + _STANDARD_FOOTPRINT: ClassVar[Type[StandardFootprint[Self]]] From 33701eef91c3db137a128612d91ce79ffdad1062 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 14:29:49 -0800 Subject: [PATCH 13/21] wip --- edg/abstract_parts/AbstractBjt.py | 2 +- edg/abstract_parts/AbstractCapacitor.py | 2 +- edg/abstract_parts/CustomDiode.py | 4 ++-- edg/abstract_parts/CustomFet.py | 4 ++-- edg/abstract_parts/GenericResistor.py | 4 ++-- edg/abstract_parts/PartsTablePart.py | 4 ++-- edg/abstract_parts/StandardFootprint.py | 12 +++++++++++- edg/core/Array.py | 16 +++++++++------- edg/core/ArrayExpr.py | 5 +++-- edg/core/BlockInterfaceMixin.py | 2 +- edg/core/ConstraintExpr.py | 3 ++- edg/core/Ports.py | 6 +++--- edg/electronics_model/CircuitBlock.py | 5 +++-- edg/parts/JlcElectrolyticCapacitor.py | 2 +- 14 files changed, 43 insertions(+), 28 deletions(-) diff --git a/edg/abstract_parts/AbstractBjt.py b/edg/abstract_parts/AbstractBjt.py index 487d15bef..060db1a12 100644 --- a/edg/abstract_parts/AbstractBjt.py +++ b/edg/abstract_parts/AbstractBjt.py @@ -11,7 +11,7 @@ class Bjt(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint): """Base class for untyped BJTs """ - _STANDARD_FOOTPRINT = BjtStandardFootprint + _STANDARD_FOOTPRINT = lambda: BjtStandardFootprint def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]: # TODO actually check that the device channel corresponds with the schematic? diff --git a/edg/abstract_parts/AbstractCapacitor.py b/edg/abstract_parts/AbstractCapacitor.py index 8918435eb..d44c40a99 100644 --- a/edg/abstract_parts/AbstractCapacitor.py +++ b/edg/abstract_parts/AbstractCapacitor.py @@ -48,7 +48,7 @@ def contents(self) -> None: @abstract_block class Capacitor(UnpolarizedCapacitor, KiCadInstantiableBlock, HasStandardFootprint): """Polarized capacitor, which we assume will be the default""" - _STANDARD_FOOTPRINT = CapacitorStandardFootprint + _STANDARD_FOOTPRINT = lambda: CapacitorStandardFootprint CAPACITOR_REGEX = re.compile("^" + f"([\d.{PartParserUtil.SI_PREFIXES}]+)\s*F?" + "\s*" + "((?:\+-|\+/-|±)?\s*[\d.]+\s*%)?" + diff --git a/edg/abstract_parts/CustomDiode.py b/edg/abstract_parts/CustomDiode.py index 5926403e8..1c4f7915c 100644 --- a/edg/abstract_parts/CustomDiode.py +++ b/edg/abstract_parts/CustomDiode.py @@ -22,8 +22,8 @@ def __init__(self, *args: Any, footprint_spec: StringLike = "", def generate(self) -> None: self.footprint( - self._STANDARD_FOOTPRINT.REFDES_PREFIX, self.footprint_spec, - self._STANDARD_FOOTPRINT._make_pinning(self, self.get(self.footprint_spec)), + self.standard_footprint().REFDES_PREFIX, self.footprint_spec, + self.standard_footprint()._make_pinning(self, self.get(self.footprint_spec)), mfr=self.manufacturer_spec, part=self.part_spec, value=self.part_spec, datasheet="" diff --git a/edg/abstract_parts/CustomFet.py b/edg/abstract_parts/CustomFet.py index 98c1770af..329d9011e 100644 --- a/edg/abstract_parts/CustomFet.py +++ b/edg/abstract_parts/CustomFet.py @@ -25,8 +25,8 @@ def __init__(self, *args: Any, footprint_spec: StringLike = "", def generate(self) -> None: self.footprint( - self._STANDARD_FOOTPRINT.REFDES_PREFIX, self.footprint_spec, - self._STANDARD_FOOTPRINT._make_pinning(self, self.get(self.footprint_spec)), + self.standard_footprint().REFDES_PREFIX, self.footprint_spec, + self.standard_footprint()._make_pinning(self, self.get(self.footprint_spec)), mfr=self.manufacturer_spec, part=self.part_spec, value=self.part_spec, datasheet="" diff --git a/edg/abstract_parts/GenericResistor.py b/edg/abstract_parts/GenericResistor.py index 94316977f..496f0d208 100644 --- a/edg/abstract_parts/GenericResistor.py +++ b/edg/abstract_parts/GenericResistor.py @@ -57,8 +57,8 @@ def generate(self) -> None: self.assign(self.actual_power_rating, Range.zero_to_upper(suitable_packages[0][0])) self.footprint( - self._STANDARD_FOOTPRINT.REFDES_PREFIX, suitable_packages[0][1], - self._STANDARD_FOOTPRINT._make_pinning(self, suitable_packages[0][1]), + self.standard_footprint().REFDES_PREFIX, suitable_packages[0][1], + self.standard_footprint()._make_pinning(self, suitable_packages[0][1]), value=f'{UnitUtils.num_to_prefix(selected_center, 3)}, {tolerance * 100:0.3g}%, {suitable_packages[0][0]} W', ) diff --git a/edg/abstract_parts/PartsTablePart.py b/edg/abstract_parts/PartsTablePart.py index e1163d18b..56a5c5d55 100644 --- a/edg/abstract_parts/PartsTablePart.py +++ b/edg/abstract_parts/PartsTablePart.py @@ -116,8 +116,8 @@ class PartsTableSelectorFootprint(PartsTableFootprintFilter, FootprintBlock, Has def _row_generate(self, row: PartsTableRow) -> None: super()._row_generate(row) self.footprint( - self._STANDARD_FOOTPRINT.REFDES_PREFIX, row[self.KICAD_FOOTPRINT], - self._STANDARD_FOOTPRINT._make_pinning(self, row[self.KICAD_FOOTPRINT]), + self.standard_footprint().REFDES_PREFIX, row[self.KICAD_FOOTPRINT], + self.standard_footprint()._make_pinning(self, row[self.KICAD_FOOTPRINT]), mfr=row[self.MANUFACTURER_COL], part=row[self.PART_NUMBER_COL], value=row[self.DESCRIPTION_COL], datasheet=row[self.DATASHEET_COL] diff --git a/edg/abstract_parts/StandardFootprint.py b/edg/abstract_parts/StandardFootprint.py index 66a14b3a6..8a49c1e74 100644 --- a/edg/abstract_parts/StandardFootprint.py +++ b/edg/abstract_parts/StandardFootprint.py @@ -40,4 +40,14 @@ def _make_pinning(cls, block: StandardPinningType, footprint: str) -> Dict[str, @non_library class HasStandardFootprint(Block): """Base class that defines that a class supports a StandardFootprint""" - _STANDARD_FOOTPRINT: ClassVar[Type[StandardFootprint[Self]]] + # the footprint can be a lambda to allow the pinning to be defined after + _STANDARD_FOOTPRINT: ClassVar[Union[Type[StandardFootprint[Self]], + Callable[[], Type[StandardFootprint[Self]]]]] + + @classmethod + def standard_footprint(cls) -> Type[StandardFootprint[Self]]: + """Returns the StandardFootprint class for this block""" + if callable(cls._STANDARD_FOOTPRINT): + return cls._STANDARD_FOOTPRINT() + else: + return cls._STANDARD_FOOTPRINT diff --git a/edg/core/Array.py b/edg/core/Array.py index efc15fe2d..758b983b0 100644 --- a/edg/core/Array.py +++ b/edg/core/Array.py @@ -2,18 +2,20 @@ import itertools from abc import abstractmethod -from typing import * -from typing_extensions import TypeVar +from typing import Generic, Any, Tuple, Type, Optional, Union, Iterable, overload, Hashable, List, \ + ItemsView, Callable + from deprecated import deprecated +from typing_extensions import TypeVar -from .HdlUserExceptions import EdgTypeError -from .. import edgir +from .ArrayExpr import ArrayExpr, ArrayRangeExpr, ArrayStringExpr, ArrayBoolExpr, ArrayFloatExpr, ArrayIntExpr from .Binding import LengthBinding, AllocatedBinding from .Builder import builder from .ConstraintExpr import BoolExpr, ConstraintExpr, FloatExpr, RangeExpr, StringExpr, IntExpr, Binding from .Core import Refable, non_library +from .HdlUserExceptions import EdgTypeError from .Ports import BaseContainerPort, BasePort, Port -from .ArrayExpr import ArrayExpr, ArrayRangeExpr, ArrayStringExpr, ArrayBoolExpr, ArrayFloatExpr, ArrayIntExpr +from .. import edgir class MapExtractBinding(Binding): @@ -206,7 +208,7 @@ def defined(self) -> None: assert builder.get_enclosing_block() is self._block_parent(), "can only create elts in block parent of array" if self._elts is None: - self._elts = OrderedDict() + self._elts = {} def append_elt(self, tpe: VectorType, suggested_name: Optional[str] = None) -> VectorType: """Appends a new element of this array (if this is not to be a dynamically-sized array - including @@ -220,7 +222,7 @@ def append_elt(self, tpe: VectorType, suggested_name: Optional[str] = None) -> V assert type(tpe) is type(self._tpe), f"created elts {type(tpe)} must be same type as array type {type(self._tpe)}" if self._elts is None: - self._elts = OrderedDict() + self._elts = {} if suggested_name is None: suggested_name = str(self._elt_next_index) self._elt_next_index += 1 diff --git a/edg/core/ArrayExpr.py b/edg/core/ArrayExpr.py index cee8926f4..87eefac9a 100644 --- a/edg/core/ArrayExpr.py +++ b/edg/core/ArrayExpr.py @@ -1,9 +1,9 @@ from __future__ import annotations -from typing import * +from typing import Generic, Any, Type, Optional, Union, Iterable, Sequence, List + from typing_extensions import TypeVar -from .. import edgir from .Binding import EqOp, ArrayBinding, UnarySetOpBinding, BinarySetOpBinding from .ConstraintExpr import ConstraintExpr, IntLike, FloatExpr, FloatLike, RangeExpr, RangeLike, \ BoolExpr, BoolLike, StringLike, \ @@ -11,6 +11,7 @@ from .Core import Refable from .Ports import BasePort from .Range import Range +from .. import edgir class SampleElementBinding(Binding): diff --git a/edg/core/BlockInterfaceMixin.py b/edg/core/BlockInterfaceMixin.py index 481d39726..ab7b0da50 100644 --- a/edg/core/BlockInterfaceMixin.py +++ b/edg/core/BlockInterfaceMixin.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Generic, Type, List, Optional, get_args, get_origin, Tuple, Callable, Any +from typing import Generic, Type, List, Optional, get_args, get_origin, Tuple, Callable, Any from typing_extensions import TypeVar from .Core import non_library, HasMetadata diff --git a/edg/core/ConstraintExpr.py b/edg/core/ConstraintExpr.py index 5a52ef02d..21d86ccd2 100644 --- a/edg/core/ConstraintExpr.py +++ b/edg/core/ConstraintExpr.py @@ -1,9 +1,10 @@ from __future__ import annotations from abc import abstractmethod +from typing import Generic, Any, TYPE_CHECKING, Tuple, Type, Optional, Union, Iterable, overload, NoReturn + from deprecated import deprecated from itertools import chain -from typing import * from typing_extensions import TypeVar from .Binding import Binding, ParamBinding, BoolLiteralBinding, IntLiteralBinding, \ diff --git a/edg/core/Ports.py b/edg/core/Ports.py index 0b46ad34a..fca9d5893 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -2,20 +2,20 @@ import itertools from abc import abstractmethod -from typing import * +from typing import Generic, Optional, Dict, Hashable, List + from typing_extensions import TypeVar -from .. import edgir 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 .HdlUserExceptions import * from .IdentityDict import IdentityDict +from .. import edgir if TYPE_CHECKING: from .Blocks import BaseBlock - from .Link import Link from .PortBlocks import PortBridge, PortAdapter diff --git a/edg/electronics_model/CircuitBlock.py b/edg/electronics_model/CircuitBlock.py index f3b5bf9f9..376a3e73a 100644 --- a/edg/electronics_model/CircuitBlock.py +++ b/edg/electronics_model/CircuitBlock.py @@ -1,10 +1,11 @@ from __future__ import annotations -from typing import * +from typing import Generic, Any, Optional, List, Mapping, Dict + from typing_extensions import TypeVar -from ..core import * from .KiCadImportableBlock import KiCadImportableBlock +from ..core import * from ..core.HdlUserExceptions import EdgTypeError CircuitLinkType = TypeVar('CircuitLinkType', bound=Link, covariant=True, default=Link) diff --git a/edg/parts/JlcElectrolyticCapacitor.py b/edg/parts/JlcElectrolyticCapacitor.py index 0045f9957..7af7ed8bd 100644 --- a/edg/parts/JlcElectrolyticCapacitor.py +++ b/edg/parts/JlcElectrolyticCapacitor.py @@ -38,7 +38,7 @@ def parse_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: else: return None - if new_cols[cls.KICAD_FOOTPRINT] not in cls._STANDARD_FOOTPRINT._footprint_pinning_map(): + if new_cols[cls.KICAD_FOOTPRINT] not in cls.standard_footprint()._footprint_pinning_map(): return None new_cols.update(cls._parse_jlcpcb_common(row)) From e7457ef781b8778549f3c79f9f263167ad0688f0 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 15:04:00 -0800 Subject: [PATCH 14/21] fix standard pinning --- edg/abstract_parts/AbstractCrystal.py | 50 ++++----- edg/abstract_parts/AbstractDiodes.py | 26 ++--- edg/abstract_parts/AbstractFerriteBead.py | 47 +++++---- edg/abstract_parts/AbstractFets.py | 108 ++++++++++---------- edg/abstract_parts/AbstractFuse.py | 44 ++++---- edg/abstract_parts/AbstractInductor.py | 94 ++++++++--------- edg/abstract_parts/AbstractLed.py | 34 +++--- edg/abstract_parts/AbstractResistor.py | 66 ++++++------ edg/abstract_parts/AbstractResistorArray.py | 76 +++++++------- 9 files changed, 272 insertions(+), 273 deletions(-) diff --git a/edg/abstract_parts/AbstractCrystal.py b/edg/abstract_parts/AbstractCrystal.py index 74c24f24c..56746ec57 100644 --- a/edg/abstract_parts/AbstractCrystal.py +++ b/edg/abstract_parts/AbstractCrystal.py @@ -6,6 +6,31 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint +@abstract_block +class Crystal(DiscreteComponent, HasStandardFootprint): + _STANDARD_FOOTPRINT = lambda: CrystalStandardFootprint + + def __init__(self, frequency: RangeLike) -> None: + """Discrete crystal component.""" + super().__init__() + + self.frequency = self.ArgParameter(frequency) + self.actual_frequency = self.Parameter(RangeExpr()) + self.actual_capacitance = self.Parameter(FloatExpr()) + + self.crystal = self.Port(CrystalPort(self.actual_frequency), [InOut]) # set by subclass + self.gnd = self.Port(Ground(), [Common]) + + def contents(self) -> None: + super().contents() + + self.description = DescriptionString( + "frequency: ", DescriptionString.FormatUnits(self.actual_frequency, "Hz"), + " of spec: ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n", + "capacitance: ", DescriptionString.FormatUnits(self.actual_capacitance, "F") + ) + + class CrystalStandardFootprint(StandardFootprint['Crystal']): REFDES_PREFIX = 'X' @@ -31,31 +56,6 @@ class CrystalStandardFootprint(StandardFootprint['Crystal']): } -@abstract_block -class Crystal(DiscreteComponent, HasStandardFootprint): - _STANDARD_FOOTPRINT = CrystalStandardFootprint - - def __init__(self, frequency: RangeLike) -> None: - """Discrete crystal component.""" - super().__init__() - - self.frequency = self.ArgParameter(frequency) - self.actual_frequency = self.Parameter(RangeExpr()) - self.actual_capacitance = self.Parameter(FloatExpr()) - - self.crystal = self.Port(CrystalPort(self.actual_frequency), [InOut]) # set by subclass - self.gnd = self.Port(Ground(), [Common]) - - def contents(self) -> None: - super().contents() - - self.description = DescriptionString( - "frequency: ", DescriptionString.FormatUnits(self.actual_frequency, "Hz"), - " of spec: ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n", - "capacitance: ", DescriptionString.FormatUnits(self.actual_capacitance, "F") - ) - - @non_library class TableCrystal(PartsTableSelector, Crystal): FREQUENCY = PartsTableColumn(Range) diff --git a/edg/abstract_parts/AbstractDiodes.py b/edg/abstract_parts/AbstractDiodes.py index ff892ee72..e22b32c6e 100644 --- a/edg/abstract_parts/AbstractDiodes.py +++ b/edg/abstract_parts/AbstractDiodes.py @@ -9,6 +9,19 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint +@non_library +class BaseDiode(DiscreteSemiconductor, HasStandardFootprint): + """Base class for diodes, with anode and cathode pins, including a very wide range of devices. + """ + _STANDARD_FOOTPRINT = lambda: DiodeStandardFootprint + + def __init__(self) -> None: + super().__init__() + + self.anode = self.Port(Passive.empty()) + self.cathode = self.Port(Passive.empty()) + + class DiodeStandardFootprint(StandardFootprint['BaseDiode']): REFDES_PREFIX = 'D' @@ -35,19 +48,6 @@ class DiodeStandardFootprint(StandardFootprint['BaseDiode']): } -@non_library -class BaseDiode(DiscreteSemiconductor, HasStandardFootprint): - """Base class for diodes, with anode and cathode pins, including a very wide range of devices. - """ - _STANDARD_FOOTPRINT = DiodeStandardFootprint - - def __init__(self) -> None: - super().__init__() - - self.anode = self.Port(Passive.empty()) - self.cathode = self.Port(Passive.empty()) - - @abstract_block class Diode(KiCadImportableBlock, BaseDiode): """Base class for untyped diodes diff --git a/edg/abstract_parts/AbstractFerriteBead.py b/edg/abstract_parts/AbstractFerriteBead.py index a72c1169b..002a5e0e9 100644 --- a/edg/abstract_parts/AbstractFerriteBead.py +++ b/edg/abstract_parts/AbstractFerriteBead.py @@ -7,30 +7,9 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class FerriteBeadStandardFootprint(StandardFootprint['FerriteBead']): - REFDES_PREFIX = 'FB' - - FOOTPRINT_PINNING_MAP = { - ( - 'Inductor_SMD:L_0201_0603Metric', - 'Inductor_SMD:L_0402_1005Metric', - 'Inductor_SMD:L_0603_1608Metric', - 'Inductor_SMD:L_0805_2012Metric', - 'Inductor_SMD:L_1206_3216Metric', - 'Inductor_SMD:L_1210_3225Metric', - 'Inductor_SMD:L_1812_4532Metric', - 'Inductor_SMD:L_2010_5025Metric', - 'Inductor_SMD:L_2512_6332Metric', - ): lambda block: { - '1': block.a, - '2': block.b, - }, - } - - @abstract_block class FerriteBead(PassiveComponent, KiCadImportableBlock, HasStandardFootprint): - _STANDARD_FOOTPRINT = FerriteBeadStandardFootprint + _STANDARD_FOOTPRINT = lambda: FerriteBeadStandardFootprint def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]: assert symbol_name in ('Device:L_Ferrite', 'Device:L_Ferrite_Small') @@ -64,10 +43,30 @@ def contents(self) -> None: ) +class FerriteBeadStandardFootprint(StandardFootprint[FerriteBead]): + REFDES_PREFIX = 'FB' + + FOOTPRINT_PINNING_MAP = { + ( + 'Inductor_SMD:L_0201_0603Metric', + 'Inductor_SMD:L_0402_1005Metric', + 'Inductor_SMD:L_0603_1608Metric', + 'Inductor_SMD:L_0805_2012Metric', + 'Inductor_SMD:L_1206_3216Metric', + 'Inductor_SMD:L_1210_3225Metric', + 'Inductor_SMD:L_1812_4532Metric', + 'Inductor_SMD:L_2010_5025Metric', + 'Inductor_SMD:L_2512_6332Metric', + ): lambda block: { + '1': block.a, + '2': block.b, + }, + } + + + @non_library class TableFerriteBead(PartsTableSelector, FerriteBead): - _STANDARD_FOOTPRINT = FerriteBeadStandardFootprint - CURRENT_RATING = PartsTableColumn(Range) HF_IMPEDANCE = PartsTableColumn(Range) DC_RESISTANCE = PartsTableColumn(Range) diff --git a/edg/abstract_parts/AbstractFets.py b/edg/abstract_parts/AbstractFets.py index 544228ded..db03eb734 100644 --- a/edg/abstract_parts/AbstractFets.py +++ b/edg/abstract_parts/AbstractFets.py @@ -7,59 +7,6 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class FetStandardFootprint(StandardFootprint['Fet']): - REFDES_PREFIX = 'Q' - - FOOTPRINT_PINNING_MAP = { - ( - 'Package_TO_SOT_SMD:SOT-23', - 'Package_TO_SOT_SMD:SOT-323_SC-70', - ): lambda block: { - '1': block.gate, - '2': block.source, - '3': block.drain, - }, - ( - 'Package_TO_SOT_SMD:SOT-223-3_TabPin2', - 'Package_TO_SOT_SMD:TO-252-2', - 'Package_TO_SOT_SMD:TO-263-2' - ): lambda block: { - '1': block.gate, - '2': block.drain, - '3': block.source, - }, - 'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm': lambda block: { - '1': block.source, - '2': block.source, - '3': block.source, - '4': block.gate, - '5': block.drain, - '6': block.drain, - '7': block.drain, - '8': block.drain, - }, - ( - 'Package_SO:PowerPAK_SO-8_Single', - 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', - ): lambda block: { - '1': block.source, - '2': block.source, - '3': block.source, - '4': block.gate, - '5': block.drain, - }, - ( - 'Package_TO_SOT_THT:TO-220-3_Horizontal_TabUp', - 'Package_TO_SOT_THT:TO-220-3_Horizontal_TabDown', - 'Package_TO_SOT_THT:TO-220-3_Vertical', - ): lambda block: { - '1': block.gate, - '2': block.drain, - '3': block.source, - }, - } - - @abstract_block class Fet(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint): """Base class for untyped MOSFETs @@ -80,7 +27,7 @@ class Fet(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint): - https://www.allaboutcircuits.com/technical-articles/choosing-the-right-transistor-understanding-low-frequency-mosfet-parameters/ - https://www.allaboutcircuits.com/technical-articles/choosing-the-right-transistor-understanding-dynamic-mosfet-parameters/ """ - _STANDARD_FOOTPRINT = FetStandardFootprint + _STANDARD_FOOTPRINT = lambda: FetStandardFootprint def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]: # TODO actually check that the device channel corresponds with the schematic? @@ -143,6 +90,59 @@ def contents(self) -> None: ) +class FetStandardFootprint(StandardFootprint[Fet]): + REFDES_PREFIX = 'Q' + + FOOTPRINT_PINNING_MAP = { + ( + 'Package_TO_SOT_SMD:SOT-23', + 'Package_TO_SOT_SMD:SOT-323_SC-70', + ): lambda block: { + '1': block.gate, + '2': block.source, + '3': block.drain, + }, + ( + 'Package_TO_SOT_SMD:SOT-223-3_TabPin2', + 'Package_TO_SOT_SMD:TO-252-2', + 'Package_TO_SOT_SMD:TO-263-2' + ): lambda block: { + '1': block.gate, + '2': block.drain, + '3': block.source, + }, + 'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm': lambda block: { + '1': block.source, + '2': block.source, + '3': block.source, + '4': block.gate, + '5': block.drain, + '6': block.drain, + '7': block.drain, + '8': block.drain, + }, + ( + 'Package_SO:PowerPAK_SO-8_Single', + 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', + ): lambda block: { + '1': block.source, + '2': block.source, + '3': block.source, + '4': block.gate, + '5': block.drain, + }, + ( + 'Package_TO_SOT_THT:TO-220-3_Horizontal_TabUp', + 'Package_TO_SOT_THT:TO-220-3_Horizontal_TabDown', + 'Package_TO_SOT_THT:TO-220-3_Vertical', + ): lambda block: { + '1': block.gate, + '2': block.drain, + '3': block.source, + }, + } + + @non_library class BaseTableFet(Fet): """Shared table columns for both TableFet and TableSwitchFet""" diff --git a/edg/abstract_parts/AbstractFuse.py b/edg/abstract_parts/AbstractFuse.py index e93718d62..23215bf99 100644 --- a/edg/abstract_parts/AbstractFuse.py +++ b/edg/abstract_parts/AbstractFuse.py @@ -9,30 +9,9 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class FuseStandardFootprint(StandardFootprint['Fuse']): - REFDES_PREFIX = 'F' - - FOOTPRINT_PINNING_MAP = { - ( - 'Resistor_SMD:R_0201_0603Metric', - 'Resistor_SMD:R_0402_1005Metric', - 'Resistor_SMD:R_0603_1608Metric', - 'Resistor_SMD:R_0805_2012Metric', - 'Resistor_SMD:R_1206_3216Metric', - 'Resistor_SMD:R_1210_3225Metric', - 'Resistor_SMD:R_1812_4532Metric', - 'Resistor_SMD:R_2010_5025Metric', - 'Resistor_SMD:R_2512_6332Metric', - ): lambda block: { - '1': block.a, - '2': block.b, - }, - } - - @abstract_block class Fuse(DiscreteComponent, HasStandardFootprint): - _STANDARD_FOOTPRINT = FuseStandardFootprint + _STANDARD_FOOTPRINT = lambda: FuseStandardFootprint def __init__(self, trip_current: RangeLike, *, hold_current: RangeLike = RangeExpr.ALL, voltage: RangeLike = RangeExpr.ZERO) -> None: @@ -69,6 +48,27 @@ def contents(self) -> None: "operating voltage not within rating") +class FuseStandardFootprint(StandardFootprint[Fuse]): + REFDES_PREFIX = 'F' + + FOOTPRINT_PINNING_MAP = { + ( + 'Resistor_SMD:R_0201_0603Metric', + 'Resistor_SMD:R_0402_1005Metric', + 'Resistor_SMD:R_0603_1608Metric', + 'Resistor_SMD:R_0805_2012Metric', + 'Resistor_SMD:R_1206_3216Metric', + 'Resistor_SMD:R_1210_3225Metric', + 'Resistor_SMD:R_1812_4532Metric', + 'Resistor_SMD:R_2010_5025Metric', + 'Resistor_SMD:R_2512_6332Metric', + ): lambda block: { + '1': block.a, + '2': block.b, + }, + } + + class SeriesPowerFuse(Protection): """Series fuse for power applications""" FUSE_TYPE = Fuse diff --git a/edg/abstract_parts/AbstractInductor.py b/edg/abstract_parts/AbstractInductor.py index b58ef5d8d..45241005a 100644 --- a/edg/abstract_parts/AbstractInductor.py +++ b/edg/abstract_parts/AbstractInductor.py @@ -7,7 +7,53 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class InductorStandardFootprint(StandardFootprint['Inductor']): +@abstract_block +class Inductor(PassiveComponent, KiCadImportableBlock, HasStandardFootprint): + _STANDARD_FOOTPRINT = lambda: InductorStandardFootprint + + def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]: + assert symbol_name in ('Device:L', 'Device:L_Small') + return {'1': self.a, '2': self.b} + + def __init__(self, inductance: RangeLike, + current: RangeLike = RangeExpr.ZERO, + frequency: RangeLike = RangeExpr.ZERO, + resistance_dc: RangeLike = (0, 1)*Ohm, # generic sane choice? + *, + experimental_filter_fn: StringLike = "" + ) -> None: + super().__init__() + + self.a = self.Port(Passive.empty()) + self.b = self.Port(Passive.empty()) + + self.inductance = self.ArgParameter(inductance) + self.current = self.ArgParameter(current) # defined as operating current range, non-directioned + self.frequency = self.ArgParameter(frequency) # defined as operating frequency range + self.resistance_dc = self.ArgParameter(resistance_dc) + self.experimental_filter_fn = self.ArgParameter(experimental_filter_fn) + + self.actual_inductance = self.Parameter(RangeExpr()) + self.actual_current_rating = self.Parameter(RangeExpr()) + self.actual_frequency_rating = self.Parameter(RangeExpr()) + self.actual_resistance_dc = self.Parameter(RangeExpr()) + + def contents(self) -> None: + super().contents() + + self.description = DescriptionString( + "inductance: ", DescriptionString.FormatUnits(self.actual_inductance, "H"), + " of spec: ", DescriptionString.FormatUnits(self.inductance, "H"), "\n", + "current rating: ", DescriptionString.FormatUnits(self.actual_current_rating, "A"), + " of operating: ", DescriptionString.FormatUnits(self.current, "A"), "\n", + "frequency rating: ", DescriptionString.FormatUnits(self.actual_frequency_rating, "Hz"), + " of operating: ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n", + "dc resistance: ", DescriptionString.FormatUnits(self.actual_resistance_dc, "Ω"), + " of spec: ", DescriptionString.FormatUnits(self.resistance_dc, "Ω"), + ) + + +class InductorStandardFootprint(StandardFootprint[Inductor]): REFDES_PREFIX = 'L' FOOTPRINT_PINNING_MAP = { @@ -82,52 +128,6 @@ class InductorStandardFootprint(StandardFootprint['Inductor']): } -@abstract_block -class Inductor(PassiveComponent, KiCadImportableBlock, HasStandardFootprint): - _STANDARD_FOOTPRINT = InductorStandardFootprint - - def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]: - assert symbol_name in ('Device:L', 'Device:L_Small') - return {'1': self.a, '2': self.b} - - def __init__(self, inductance: RangeLike, - current: RangeLike = RangeExpr.ZERO, - frequency: RangeLike = RangeExpr.ZERO, - resistance_dc: RangeLike = (0, 1)*Ohm, # generic sane choice? - *, - experimental_filter_fn: StringLike = "" - ) -> None: - super().__init__() - - self.a = self.Port(Passive.empty()) - self.b = self.Port(Passive.empty()) - - self.inductance = self.ArgParameter(inductance) - self.current = self.ArgParameter(current) # defined as operating current range, non-directioned - self.frequency = self.ArgParameter(frequency) # defined as operating frequency range - self.resistance_dc = self.ArgParameter(resistance_dc) - self.experimental_filter_fn = self.ArgParameter(experimental_filter_fn) - - self.actual_inductance = self.Parameter(RangeExpr()) - self.actual_current_rating = self.Parameter(RangeExpr()) - self.actual_frequency_rating = self.Parameter(RangeExpr()) - self.actual_resistance_dc = self.Parameter(RangeExpr()) - - def contents(self) -> None: - super().contents() - - self.description = DescriptionString( - "inductance: ", DescriptionString.FormatUnits(self.actual_inductance, "H"), - " of spec: ", DescriptionString.FormatUnits(self.inductance, "H"), "\n", - "current rating: ", DescriptionString.FormatUnits(self.actual_current_rating, "A"), - " of operating: ", DescriptionString.FormatUnits(self.current, "A"), "\n", - "frequency rating: ", DescriptionString.FormatUnits(self.actual_frequency_rating, "Hz"), - " of operating: ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n", - "dc resistance: ", DescriptionString.FormatUnits(self.actual_resistance_dc, "Ω"), - " of spec: ", DescriptionString.FormatUnits(self.resistance_dc, "Ω"), - ) - - @non_library class TableInductor(PartsTableSelector, Inductor): INDUCTANCE = PartsTableColumn(Range) # actual inductance incl. tolerance diff --git a/edg/abstract_parts/AbstractLed.py b/edg/abstract_parts/AbstractLed.py index ead6ddcfd..37fc1621e 100644 --- a/edg/abstract_parts/AbstractLed.py +++ b/edg/abstract_parts/AbstractLed.py @@ -8,29 +8,13 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class LedStandardFootprint(StandardFootprint['Led']): - REFDES_PREFIX = 'D' - - FOOTPRINT_PINNING_MAP = { - ( - 'LED_SMD:LED_0402_1005Metric', - 'LED_SMD:LED_0603_1608Metric', - 'LED_SMD:LED_0805_2012Metric', - 'LED_SMD:LED_1206_3216Metric', - ): lambda block: { - '2': block.a, - '1': block.k, - }, - } - - LedColor = str # type alias LedColorLike = StringLike # type alias @abstract_block class Led(DiscreteSemiconductor, HasStandardFootprint): - _STANDARD_FOOTPRINT = LedStandardFootprint + _STANDARD_FOOTPRINT = lambda: LedStandardFootprint # Common color definitions Red: LedColor = "red" @@ -52,6 +36,22 @@ def __init__(self, color: LedColorLike = Any): self.k = self.Port(Passive.empty()) +class LedStandardFootprint(StandardFootprint[Led]): + REFDES_PREFIX = 'D' + + FOOTPRINT_PINNING_MAP = { + ( + 'LED_SMD:LED_0402_1005Metric', + 'LED_SMD:LED_0603_1608Metric', + 'LED_SMD:LED_0805_2012Metric', + 'LED_SMD:LED_1206_3216Metric', + ): lambda block: { + '2': block.a, + '1': block.k, + }, + } + + @non_library class TableLed(PartsTableSelector, Led): COLOR = PartsTableColumn(str) diff --git a/edg/abstract_parts/AbstractResistor.py b/edg/abstract_parts/AbstractResistor.py index 523825da4..9b96e186a 100644 --- a/edg/abstract_parts/AbstractResistor.py +++ b/edg/abstract_parts/AbstractResistor.py @@ -9,41 +9,9 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class ResistorStandardFootprint(StandardFootprint['Resistor']): - REFDES_PREFIX = 'R' - - FOOTPRINT_PINNING_MAP = { - ( - 'Resistor_SMD:R_0201_0603Metric', - 'Resistor_SMD:R_0402_1005Metric', - 'Resistor_SMD:R_0603_1608Metric', - 'Resistor_SMD:R_0805_2012Metric', - 'Resistor_SMD:R_1206_3216Metric', - 'Resistor_SMD:R_1210_3225Metric', - 'Resistor_SMD:R_2010_5025Metric', - 'Resistor_SMD:R_2512_6332Metric', - - 'Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P5.08mm_Horizontal', - 'Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal', - 'Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P12.70mm_Horizontal', - 'Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P12.70mm_Horizontal', - 'Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P15.24mm_Horizontal', - - 'Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P1.90mm_Vertical', - 'Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P2.54mm_Vertical', - 'Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P2.54mm_Vertical', - 'Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P5.08mm_Vertical', - 'Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P5.08mm_Vertical', - ): lambda block: { - '1': block.a, - '2': block.b, - }, - } - - @abstract_block class Resistor(PassiveComponent, KiCadInstantiableBlock, HasStandardFootprint): - _STANDARD_FOOTPRINT = ResistorStandardFootprint + _STANDARD_FOOTPRINT = lambda: ResistorStandardFootprint RESISTOR_REGEX = re.compile("^" + f"([\d.{PartParserUtil.SI_PREFIXES}]+(?:\s*[{PartParserUtil.SI_PREFIXES}])?)\s*[RΩ]?" + "\s*" + "((?:\+-|\+/-|±)?\s*[\d.]+\s*%?)?" + "$") @@ -97,6 +65,38 @@ def contents(self) -> None: ) +class ResistorStandardFootprint(StandardFootprint[Resistor]): + REFDES_PREFIX = 'R' + + FOOTPRINT_PINNING_MAP = { + ( + 'Resistor_SMD:R_0201_0603Metric', + 'Resistor_SMD:R_0402_1005Metric', + 'Resistor_SMD:R_0603_1608Metric', + 'Resistor_SMD:R_0805_2012Metric', + 'Resistor_SMD:R_1206_3216Metric', + 'Resistor_SMD:R_1210_3225Metric', + 'Resistor_SMD:R_2010_5025Metric', + 'Resistor_SMD:R_2512_6332Metric', + + 'Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P5.08mm_Horizontal', + 'Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal', + 'Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P12.70mm_Horizontal', + 'Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P12.70mm_Horizontal', + 'Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P15.24mm_Horizontal', + + 'Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P1.90mm_Vertical', + 'Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P2.54mm_Vertical', + 'Resistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P2.54mm_Vertical', + 'Resistor_THT:R_Axial_DIN0411_L9.9mm_D3.6mm_P5.08mm_Vertical', + 'Resistor_THT:R_Axial_DIN0414_L11.9mm_D4.5mm_P5.08mm_Vertical', + ): lambda block: { + '1': block.a, + '2': block.b, + }, + } + + @non_library class TableResistor(PartsTableSelector, Resistor): RESISTANCE = PartsTableColumn(Range) diff --git a/edg/abstract_parts/AbstractResistorArray.py b/edg/abstract_parts/AbstractResistorArray.py index f2a28164f..95f65a121 100644 --- a/edg/abstract_parts/AbstractResistorArray.py +++ b/edg/abstract_parts/AbstractResistorArray.py @@ -8,43 +8,6 @@ from .StandardFootprint import StandardFootprint, HasStandardFootprint -class ResistorArrayStandardFootprint(StandardFootprint['ResistorArray']): - REFDES_PREFIX = 'RN' - - # TODO some way to ensure the resistor count is sufficient? - FOOTPRINT_PINNING_MAP = { # these are all the footprints in KiCad as of 2022 05 31 - ( - 'Resistor_SMD:R_Array_Concave_2x0603', - 'Resistor_SMD:R_Array_Convex_2x0402', - 'Resistor_SMD:R_Array_Convex_2x0603', - 'Resistor_SMD:R_Array_Convex_2x0606', - 'Resistor_SMD:R_Array_Convex_2x1206', - ): lambda block: { - '1': block.a['0'], - '4': block.b['0'], - '2': block.a['1'], - '3': block.b['1'], - }, - ( - 'Resistor_SMD:R_Array_Concave_4x0402', - 'Resistor_SMD:R_Array_Concave_4x0603', - 'Resistor_SMD:R_Array_Convex_4x0402', - 'Resistor_SMD:R_Array_Convex_4x0603', - 'Resistor_SMD:R_Array_Convex_4x0612', - 'Resistor_SMD:R_Array_Convex_4x1206', - ): lambda block: { - '1': block.a['0'], - '8': block.b['0'], - '2': block.a['1'], - '7': block.b['1'], - '3': block.a['2'], - '6': block.b['2'], - '4': block.a['3'], - '5': block.b['3'], - }, - } - - class ResistorArrayElement(Resistor): # to avoid an abstract part error def __init__(self) -> None: super().__init__(resistance=RangeExpr(), power=RangeExpr()) @@ -53,7 +16,7 @@ def __init__(self) -> None: @abstract_block class ResistorArray(MultipackDevice, MultipackBlock, HasStandardFootprint): """An n-element resistor array, where all resistors have the same resistance and power rating.""" - _STANDARD_FOOTPRINT = ResistorArrayStandardFootprint + _STANDARD_FOOTPRINT = lambda: ResistorArrayStandardFootprint def __init__(self, count: IntLike = 0) -> None: # 0 means 'size automatically' super().__init__() @@ -86,6 +49,43 @@ def contents(self) -> None: ) +class ResistorArrayStandardFootprint(StandardFootprint[ResistorArray]): + REFDES_PREFIX = 'RN' + + # TODO some way to ensure the resistor count is sufficient? + FOOTPRINT_PINNING_MAP = { # these are all the footprints in KiCad as of 2022 05 31 + ( + 'Resistor_SMD:R_Array_Concave_2x0603', + 'Resistor_SMD:R_Array_Convex_2x0402', + 'Resistor_SMD:R_Array_Convex_2x0603', + 'Resistor_SMD:R_Array_Convex_2x0606', + 'Resistor_SMD:R_Array_Convex_2x1206', + ): lambda block: { + '1': block.a['0'], + '4': block.b['0'], + '2': block.a['1'], + '3': block.b['1'], + }, + ( + 'Resistor_SMD:R_Array_Concave_4x0402', + 'Resistor_SMD:R_Array_Concave_4x0603', + 'Resistor_SMD:R_Array_Convex_4x0402', + 'Resistor_SMD:R_Array_Convex_4x0603', + 'Resistor_SMD:R_Array_Convex_4x0612', + 'Resistor_SMD:R_Array_Convex_4x1206', + ): lambda block: { + '1': block.a['0'], + '8': block.b['0'], + '2': block.a['1'], + '7': block.b['1'], + '3': block.a['2'], + '6': block.b['2'], + '4': block.a['3'], + '5': block.b['3'], + }, + } + + @non_library class TableResistorArray(PartsTableSelector, ResistorArray): RESISTANCE = PartsTableColumn(Range) From 3282922d7d205b4fe0ab638b09a58e45cc8c5674 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 15:16:47 -0800 Subject: [PATCH 15/21] wip --- edg/abstract_parts/PartsTable.py | 4 ++-- edg/abstract_parts/StandardFootprint.py | 2 +- edg/core/Array.py | 4 ++-- edg/core/Builder.py | 15 +++++++++++++-- edg/core/Core.py | 6 +++--- edg/core/Generator.py | 2 +- edg/core/HdlUserExceptions.py | 2 +- edg/core/HierarchyBlock.py | 4 +++- edg/core/Ports.py | 1 + 9 files changed, 27 insertions(+), 13 deletions(-) diff --git a/edg/abstract_parts/PartsTable.py b/edg/abstract_parts/PartsTable.py index 8202c4b2b..ecc17874a 100644 --- a/edg/abstract_parts/PartsTable.py +++ b/edg/abstract_parts/PartsTable.py @@ -2,7 +2,7 @@ import csv import itertools -from typing import Generic, Type, overload, Union, Callable, List, Dict, Any, KeysView, Optional, OrderedDict, \ +from typing import Generic, Type, overload, Union, Callable, List, Dict, Any, KeysView, Optional, \ cast, Tuple, Sequence, Protocol from typing_extensions import ParamSpec, TypeVar @@ -83,7 +83,7 @@ def from_csv_files(cls, csv_names: List[str], encoding: str='utf-8') -> 'PartsTa return cls.from_dict_rows(dict_rows) @staticmethod - def from_dict_rows(*dict_rowss: Union[List[Dict[str, str]], List[OrderedDict[str, str]]]) -> 'PartsTable': + def from_dict_rows(*dict_rowss: Union[List[Dict[str, str]], List[Dict[str, str]]]) -> 'PartsTable': """Creates a parts table from dict rows, such as parsed by csv.DictReader. Checks to make sure all incoming rows are dense (have all cells).""" all_dict_rows = list(itertools.chain(*dict_rowss)) diff --git a/edg/abstract_parts/StandardFootprint.py b/edg/abstract_parts/StandardFootprint.py index 8a49c1e74..fd3910359 100644 --- a/edg/abstract_parts/StandardFootprint.py +++ b/edg/abstract_parts/StandardFootprint.py @@ -48,6 +48,6 @@ class HasStandardFootprint(Block): def standard_footprint(cls) -> Type[StandardFootprint[Self]]: """Returns the StandardFootprint class for this block""" if callable(cls._STANDARD_FOOTPRINT): - return cls._STANDARD_FOOTPRINT() + return cls._STANDARD_FOOTPRINT() # type: ignore else: return cls._STANDARD_FOOTPRINT diff --git a/edg/core/Array.py b/edg/core/Array.py index 758b983b0..197480bb8 100644 --- a/edg/core/Array.py +++ b/edg/core/Array.py @@ -3,7 +3,7 @@ import itertools from abc import abstractmethod from typing import Generic, Any, Tuple, Type, Optional, Union, Iterable, overload, Hashable, List, \ - ItemsView, Callable + ItemsView, Callable, Dict from deprecated import deprecated from typing_extensions import TypeVar @@ -108,7 +108,7 @@ def __init__(self, tpe: VectorType) -> None: assert not tpe._is_bound() self._tpe = tpe self._elt_sample = tpe._bind(self) - self._elts: Optional[OrderedDict[str, VectorType]] = None # concrete elements, for boundary ports + self._elts: Optional[Dict[str, VectorType]] = None # concrete elements, for boundary ports self._elt_next_index = 0 self._requests: List[Tuple[Optional[str], BasePort]] = [] # used to track request / request_vector for ref_map diff --git a/edg/core/Builder.py b/edg/core/Builder.py index ff31499f0..f2f9e70eb 100644 --- a/edg/core/Builder.py +++ b/edg/core/Builder.py @@ -8,6 +8,8 @@ if TYPE_CHECKING: from .Blocks import BaseBlock + from .Link import Link + from .HierarchyBlock import Block class Builder: @@ -41,14 +43,23 @@ def get_enclosing_block(self) -> Optional[BaseBlock]: def get_curr_context(self) -> Optional[BaseBlock]: return self.get_enclosing_block() + @overload + def elaborate_toplevel(self, block: Block, *, + is_generator: bool = False, + generate_values: Iterable[Tuple[edgir.LocalPath, edgir.ValueLit]] = []) -> edgir.HierarchyBlock: ... + @overload + def elaborate_toplevel(self, block: Link, *, + is_generator: bool = False, + generate_values: Iterable[Tuple[edgir.LocalPath, edgir.ValueLit]] = []) -> edgir.Link: ... + def elaborate_toplevel(self, block: BaseBlock, *, is_generator: bool = False, - generate_values: Iterable[Tuple[edgir.LocalPath, edgir.ValueLit]] = []) -> edgir.HierarchyBlock: + generate_values: Iterable[Tuple[edgir.LocalPath, edgir.ValueLit]] = []) -> edgir.BlockLikeTypes: try: if is_generator: # TODO this is kind of nasty =( from .Generator import GeneratorBlock assert isinstance(block, GeneratorBlock) - elaborated = block._generated_def_to_proto(generate_values) + elaborated: edgir.BlockLikeTypes = block._generated_def_to_proto(generate_values) else: # TODO check is a GeneratorBlock w/o circular imports? elaborated = block._elaborated_def_to_proto() diff --git a/edg/core/Core.py b/edg/core/Core.py index 6c3448a82..58a5ba5cb 100644 --- a/edg/core/Core.py +++ b/edg/core/Core.py @@ -16,7 +16,7 @@ def __init__(self, anon_prefix: Optional[str]=None) -> None: self.anon_prefix = anon_prefix self.container: Dict[str, ElementType] = {} self.names = IdentityDict[ElementType, str]() # TODO unify w/ container? - self.keys_list: List = [] + self.keys_list: List[str] = [] self.closed = False def register(self, item: ElementType) -> ElementType: @@ -80,7 +80,7 @@ def name_of(self, elt: ElementType) -> Optional[str]: class SubElementManager: def __init__(self) -> None: - self.dicts: List[Tuple[Union[Type, Tuple[Type, ...]], SubElementDict]] = [] + self.dicts: List[Tuple[Union[Type[Any], Tuple[Type[Any], ...]], SubElementDict[Any]]] = [] self.aliases = IdentityDict[Any, Any]() def new_dict(self, filter_type: Union[Type[ElementType], Tuple[Type[ElementType], ...]], @@ -274,7 +274,7 @@ def _get_bases_of(cls, base_type: Type[BaseType]) -> Tuple[List[Type[BaseType]], superclasses order is not defined (but MRO in current practice). mypy currently does not allow passing in abstract types, so generally calls to this need type: ignore.""" - direct_bases: Set[Type] = set() + direct_bases: Set[Type[HasMetadata]] = set() def process_direct_base(bcls: Type[HasMetadata.BaseType]) -> None: if not issubclass(bcls, base_type): return # ignore above base_type diff --git a/edg/core/Generator.py b/edg/core/Generator.py index 44ddf48e6..07d8c1da0 100644 --- a/edg/core/Generator.py +++ b/edg/core/Generator.py @@ -52,7 +52,7 @@ def get(self, param: ConstraintExpr[WrappedType, Any]) -> WrappedType: # Generator dependency data # class GeneratorRecord(NamedTuple): - fn: Callable + fn: Callable[..., None] req_params: Tuple[ConstraintExpr, ...] # all required params for generator to fire fn_args: Tuple[ConstraintExpr, ...] # params to unpack for the generator function diff --git a/edg/core/HdlUserExceptions.py b/edg/core/HdlUserExceptions.py index 8b4ddcbda..5e42f6df5 100644 --- a/edg/core/HdlUserExceptions.py +++ b/edg/core/HdlUserExceptions.py @@ -12,7 +12,7 @@ def __init__(self, exc: str, resolution: str = ""): class EdgTypeError(EdslUserError): """Argument of the wrong type passed into a EDG core function.""" - def __init__(self, item_desc: str, object: Any, expected_type: Union[Type, Tuple[Type, ...]]): + def __init__(self, item_desc: str, object: Any, expected_type: Union[Type[Any], Tuple[Type[Any], ...]]): if isinstance(expected_type, tuple): expected_type_str = '/'.join([t.__name__ for t in expected_type]) else: diff --git a/edg/core/HierarchyBlock.py b/edg/core/HierarchyBlock.py index a85aaa5f6..c0a687dac 100644 --- a/edg/core/HierarchyBlock.py +++ b/edg/core/HierarchyBlock.py @@ -288,7 +288,8 @@ def __init__(self) -> None: super().__init__() if hasattr(self, '_init_params'): # used to propagate params generated in the metaclass __init__ hook - for param_name, param in cast(Dict, self._init_params).items(): + self._init_params: Dict[str, ConstraintExpr] + for param_name, param in self._init_params.items(): self._parameters.register(param) self.manager.add_element(param_name, param) delattr(self, '_init_params') @@ -320,6 +321,7 @@ def _build_ref_map(self, ref_map: Refable.RefMapType, prefix: edgir.LocalPath, * def _populate_def_proto_block_base(self, pb: edgir.BlockLikeTypes) -> None: super()._populate_def_proto_block_base(pb) + assert isinstance(pb, edgir.HierarchyBlock) # generate param defaults for param_name, param in self._parameters.items(): diff --git a/edg/core/Ports.py b/edg/core/Ports.py index fca9d5893..ba3508830 100644 --- a/edg/core/Ports.py +++ b/edg/core/Ports.py @@ -16,6 +16,7 @@ if TYPE_CHECKING: from .Blocks import BaseBlock + from .Link import Link from .PortBlocks import PortBridge, PortAdapter From 60d90abae53a0439724de0626a712552db67195e Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 15:44:17 -0800 Subject: [PATCH 16/21] almost there? --- edg/core/PortBlocks.py | 2 +- edg/electronics_model/CircuitBlock.py | 2 +- edg/electronics_model/ConnectedGenerator.py | 6 +++--- edg/electronics_model/PassivePort.py | 2 +- .../resources/build_kicad_footprint_table.py | 2 +- edg/electronics_model/test_kicad_schematic_parser.py | 4 ++-- edg/parts/JlcPart.py | 6 +++--- examples/jlcpcb_pcba_postprocess.py | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/edg/core/PortBlocks.py b/edg/core/PortBlocks.py index 996da8f28..fa9b70e7a 100644 --- a/edg/core/PortBlocks.py +++ b/edg/core/PortBlocks.py @@ -38,7 +38,7 @@ def Port(self, tpe: T, *args: Any, **kwargs: Any) -> T: return super().Port(tpe, *args, optional=True, **kwargs) -AdapterDstType = TypeVar('AdapterDstType', bound=Port) +AdapterDstType = TypeVar('AdapterDstType', covariant=True, bound=Port, default=Port) @abstract_block class PortAdapter(InternalBlock, Block, Generic[AdapterDstType]): """Defines an adapter from one port type to another port type. This behaves as a normal block, and both the src and diff --git a/edg/electronics_model/CircuitBlock.py b/edg/electronics_model/CircuitBlock.py index 376a3e73a..79197956d 100644 --- a/edg/electronics_model/CircuitBlock.py +++ b/edg/electronics_model/CircuitBlock.py @@ -115,7 +115,7 @@ def contents(self) -> None: self.net() -AdapterDstType = TypeVar('AdapterDstType', bound='CircuitPort') +AdapterDstType = TypeVar('AdapterDstType', covariant=True, bound='CircuitPort', default='CircuitPort') @abstract_block class CircuitPortAdapter(KiCadImportableBlock, NetBaseBlock, PortAdapter[AdapterDstType], Generic[AdapterDstType]): def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]: diff --git a/edg/electronics_model/ConnectedGenerator.py b/edg/electronics_model/ConnectedGenerator.py index f8f73d4e5..6d57402d9 100644 --- a/edg/electronics_model/ConnectedGenerator.py +++ b/edg/electronics_model/ConnectedGenerator.py @@ -16,9 +16,9 @@ class DefaultConnectionBlock(InternalBlock): pass -OutputType = TypeVar('OutputType', bound=Port) -InputsType = TypeVar('InputsType', bound=Port) -LinkType = TypeVar('LinkType', bound=Link) +OutputType = TypeVar('OutputType', covariant=True, bound=Port, default=Port) +InputsType = TypeVar('InputsType', covariant=True, bound=Port, default=Port) +LinkType = TypeVar('LinkType', covariant=True, bound=Link, default=Link) SelfType = TypeVar('SelfType', bound='BaseConnectedGenerator') @non_library # this can't be instantiated class BaseConnectedGenerator(DefaultConnectionBlock, GeneratorBlock, Generic[OutputType, InputsType, LinkType]): diff --git a/edg/electronics_model/PassivePort.py b/edg/electronics_model/PassivePort.py index 82566e85a..f50e9177e 100644 --- a/edg/electronics_model/PassivePort.py +++ b/edg/electronics_model/PassivePort.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TypeVar, Type, Dict +from typing import TypeVar, Type, Dict, Mapping from ..core import * from .GroundPort import Ground diff --git a/edg/electronics_model/resources/build_kicad_footprint_table.py b/edg/electronics_model/resources/build_kicad_footprint_table.py index 7a5067c9e..acbce4942 100644 --- a/edg/electronics_model/resources/build_kicad_footprint_table.py +++ b/edg/electronics_model/resources/build_kicad_footprint_table.py @@ -63,7 +63,7 @@ def polygon_lines_area(lines: List[Line]) -> Optional[float]: return abs(sum) / 2 -def sexp_list_find_all(container: list, key: str) -> List[List[Any]]: +def sexp_list_find_all(container: list[Any], key: str) -> List[List[Any]]: """Given a sexp list, return all elements which are lists where the first element is a symbol of key.""" matching_elts = [] for elt in container: diff --git a/edg/electronics_model/test_kicad_schematic_parser.py b/edg/electronics_model/test_kicad_schematic_parser.py index b57fdb27b..03b13f2de 100644 --- a/edg/electronics_model/test_kicad_schematic_parser.py +++ b/edg/electronics_model/test_kicad_schematic_parser.py @@ -1,12 +1,12 @@ import unittest import os.path -from typing import Set, Tuple, Type +from typing import Set, Tuple, Type, Any from .KiCadSchematicParser import KiCadSchematic, ParsedNet, KiCadGlobalLabel, KiCadLabel, KiCadPowerLabel -def net_to_tuple(net: ParsedNet) -> Tuple[Set[Tuple[Type, str]], Set[str]]: +def net_to_tuple(net: ParsedNet) -> Tuple[Set[Tuple[Type[Any], str]], Set[str]]: """Converts a ParsedNet to a tuple of net labels and net pins, so it can be compared during unit testing.""" labels = set([(x.__class__, x.name) for x in net.labels]) pins = set([f"{x.refdes}.{x.pin_number}" for x in net.pins]) diff --git a/edg/parts/JlcPart.py b/edg/parts/JlcPart.py index 54d1a6df9..e92fc3d9c 100644 --- a/edg/parts/JlcPart.py +++ b/edg/parts/JlcPart.py @@ -18,8 +18,8 @@ def __init__(self, *args: Any, require_basic_part: BoolLike = False, **kwargs: A self.require(self.require_basic_part.implies(self.actual_basic_part), "required basic part") -DescriptionParser = Tuple[re.Pattern, - Callable[[re.Match], Dict[PartsTableColumn, Any]]] +DescriptionParser = Tuple[re.Pattern[str], + Callable[[re.Match[str]], Dict[PartsTableColumn, Any]]] class JlcTableBase(PartsTableBase): """Defines common table headers, columns, and functionality for parsing JLCPCB parts tables.""" PART_NUMBER_COL = 'MFR.Part' # used only for translation to the PartsTableFootprint col @@ -63,7 +63,7 @@ def _parse_jlcpcb_common(cls, row: PartsTableRow) -> Dict[PartsTableColumn, Any] } @staticmethod - def parse(description: str, regex_dictionary: Dict[str, re.Pattern]) -> Dict[str, str]: + def parse(description: str, regex_dictionary: Dict[str, re.Pattern[str]]) -> Dict[str, str]: extraction_table = {} for key, pattern in regex_dictionary.items(): diff --git a/examples/jlcpcb_pcba_postprocess.py b/examples/jlcpcb_pcba_postprocess.py index fb520eabe..ce321390f 100644 --- a/examples/jlcpcb_pcba_postprocess.py +++ b/examples/jlcpcb_pcba_postprocess.py @@ -144,7 +144,7 @@ def remap_by_dict(elt: str, remap_dict: Dict[str, str]) -> str: if os.path.exists(f'{args.file_path_prefix}.csv'): # remove previous one to avoid confusion os.remove(f'{args.file_path_prefix}.csv') with open(f'{args.file_path_prefix}.csv', 'w', newline='') as bom_out: - merged_csv_out: Optional[csv.DictWriter] = None + merged_csv_out: Optional[csv.DictWriter[str]] = None for input_bom_file in args.merge_boms: with open(input_bom_file, 'r', newline='') as bom_in: csv_dict_in = csv.DictReader(bom_in) From 2a07eee5efb7260498da3a61ac75c4b464e568f8 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 15:47:41 -0800 Subject: [PATCH 17/21] invariance --- edg/core/Array.py | 6 +++--- edg/core/Binding.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/edg/core/Array.py b/edg/core/Array.py index 197480bb8..a21e37e77 100644 --- a/edg/core/Array.py +++ b/edg/core/Array.py @@ -19,7 +19,7 @@ class MapExtractBinding(Binding): - def __init__(self, container: Vector, elt: ConstraintExpr): + def __init__(self, container: BaseVector, elt: ConstraintExpr): super().__init__() self.container = container self.elt = elt @@ -28,7 +28,7 @@ def get_subexprs(self) -> Iterable[Union[ConstraintExpr, BasePort]]: return [self.container] def expr_to_proto(self, expr: ConstraintExpr, ref_map: Refable.RefMapType) -> edgir.ValueExpr: - contained_map = self.container._elt_sample._create_ref_map(edgir.LocalPath()) + contained_map = self.container._get_elt_sample()._create_ref_map(edgir.LocalPath()) pb = edgir.ValueExpr() pb.map_extract.container.ref.CopyFrom(ref_map[self.container]) # TODO support arbitrary refs @@ -59,7 +59,7 @@ def _get_elt_sample(self) -> BasePort: # A 'fake'/'intermediate'/'view' vector object used as a return in map_extract operations. -VectorType = TypeVar('VectorType', covariant=True, bound=Port, default=Port) +VectorType = TypeVar('VectorType', bound=Port, default=Port) @non_library class DerivedVector(BaseVector, Generic[VectorType]): # TODO: Library types need to be removed from the type hierarchy, because this does not generate into a library elt diff --git a/edg/core/Binding.py b/edg/core/Binding.py index 19a60e260..1abff5cfc 100644 --- a/edg/core/Binding.py +++ b/edg/core/Binding.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: from .Ports import BasePort, Port - from .Array import Vector + from .Array import Vector, BaseVector from .Blocks import BaseBlock from .ConstraintExpr import ConstraintExpr, FloatExpr, BoolExpr @@ -449,7 +449,7 @@ class LengthBinding(Binding): def __repr__(self) -> str: return f"Length" - def __init__(self, src: Vector): + def __init__(self, src: BaseVector): super().__init__() self.src = src @@ -467,7 +467,7 @@ class AllocatedBinding(Binding): def __repr__(self) -> str: return f"Allocated" - def __init__(self, src: Vector): + def __init__(self, src: BaseVector): super().__init__() self.src = src From af9ab1a2a0201ead941d0bb02f2fbf00326dc90a Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 15:54:20 -0800 Subject: [PATCH 18/21] fix --- edg/core/PortBlocks.py | 6 ++---- edg/electronics_model/ConnectedGenerator.py | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/edg/core/PortBlocks.py b/edg/core/PortBlocks.py index fa9b70e7a..4dbac8698 100644 --- a/edg/core/PortBlocks.py +++ b/edg/core/PortBlocks.py @@ -1,12 +1,10 @@ from __future__ import annotations -from typing import * +from typing import Any, Generic +from typing_extensions import TypeVar -from .. import edgir from .Categories import InternalBlock -from .Core import Refable from .HierarchyBlock import Block, abstract_block -from .IdentityDict import IdentityDict from .Ports import BasePort, Port diff --git a/edg/electronics_model/ConnectedGenerator.py b/edg/electronics_model/ConnectedGenerator.py index 6d57402d9..78a808cd6 100644 --- a/edg/electronics_model/ConnectedGenerator.py +++ b/edg/electronics_model/ConnectedGenerator.py @@ -1,4 +1,5 @@ -from typing import Type, TypeVar, Generic +from typing import Type, Generic +from typing_extensions import TypeVar from ..core import * from .VoltagePorts import VoltageLink, VoltageSink, VoltageSource From 1892be1e2d1fee4f84e72f5b4bcc8484dab893a2 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 15:57:53 -0800 Subject: [PATCH 19/21] fully green --- edg/core/Array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edg/core/Array.py b/edg/core/Array.py index a21e37e77..9a67173d0 100644 --- a/edg/core/Array.py +++ b/edg/core/Array.py @@ -313,7 +313,7 @@ def map_extract(self, selector: Callable[[VectorType], StringExpr]) -> ArrayStri @overload def map_extract(self, selector: Callable[[VectorType], ExtractPortType]) -> DerivedVector[ExtractPortType]: ... - def map_extract(self, selector: Callable[[VectorType], Union[ConstraintExpr, Port]]) -> Union[ArrayExpr, DerivedVector]: + def map_extract(self, selector: Callable[[VectorType], Union[ConstraintExpr, ExtractPortType]]) -> Union[ArrayExpr, DerivedVector[ExtractPortType]]: param = selector(self._elt_sample) if isinstance(param, ConstraintExpr): # TODO check that returned type is child return ArrayExpr.array_of_elt(param)._bind(MapExtractBinding(self, param)) From c9363a45896d63c4cbeb94e9303e7cbbc15af6c3 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 16:09:38 -0800 Subject: [PATCH 20/21] fix --- edg/abstract_parts/CustomDiode.py | 4 ++-- edg/abstract_parts/CustomFet.py | 4 ++-- edg/abstract_parts/GenericResistor.py | 4 ++-- edg/abstract_parts/PartsTablePart.py | 4 ++-- edg/abstract_parts/StandardFootprint.py | 2 +- edg/parts/JlcElectrolyticCapacitor.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/edg/abstract_parts/CustomDiode.py b/edg/abstract_parts/CustomDiode.py index 1c4f7915c..e4c87fff0 100644 --- a/edg/abstract_parts/CustomDiode.py +++ b/edg/abstract_parts/CustomDiode.py @@ -22,8 +22,8 @@ def __init__(self, *args: Any, footprint_spec: StringLike = "", def generate(self) -> None: self.footprint( - self.standard_footprint().REFDES_PREFIX, self.footprint_spec, - self.standard_footprint()._make_pinning(self, self.get(self.footprint_spec)), + self._standard_footprint().REFDES_PREFIX, self.footprint_spec, + self._standard_footprint()._make_pinning(self, self.get(self.footprint_spec)), mfr=self.manufacturer_spec, part=self.part_spec, value=self.part_spec, datasheet="" diff --git a/edg/abstract_parts/CustomFet.py b/edg/abstract_parts/CustomFet.py index 329d9011e..4cb706d2b 100644 --- a/edg/abstract_parts/CustomFet.py +++ b/edg/abstract_parts/CustomFet.py @@ -25,8 +25,8 @@ def __init__(self, *args: Any, footprint_spec: StringLike = "", def generate(self) -> None: self.footprint( - self.standard_footprint().REFDES_PREFIX, self.footprint_spec, - self.standard_footprint()._make_pinning(self, self.get(self.footprint_spec)), + self._standard_footprint().REFDES_PREFIX, self.footprint_spec, + self._standard_footprint()._make_pinning(self, self.get(self.footprint_spec)), mfr=self.manufacturer_spec, part=self.part_spec, value=self.part_spec, datasheet="" diff --git a/edg/abstract_parts/GenericResistor.py b/edg/abstract_parts/GenericResistor.py index 496f0d208..46d095974 100644 --- a/edg/abstract_parts/GenericResistor.py +++ b/edg/abstract_parts/GenericResistor.py @@ -57,8 +57,8 @@ def generate(self) -> None: self.assign(self.actual_power_rating, Range.zero_to_upper(suitable_packages[0][0])) self.footprint( - self.standard_footprint().REFDES_PREFIX, suitable_packages[0][1], - self.standard_footprint()._make_pinning(self, suitable_packages[0][1]), + self._standard_footprint().REFDES_PREFIX, suitable_packages[0][1], + self._standard_footprint()._make_pinning(self, suitable_packages[0][1]), value=f'{UnitUtils.num_to_prefix(selected_center, 3)}, {tolerance * 100:0.3g}%, {suitable_packages[0][0]} W', ) diff --git a/edg/abstract_parts/PartsTablePart.py b/edg/abstract_parts/PartsTablePart.py index 56a5c5d55..7a0c117c0 100644 --- a/edg/abstract_parts/PartsTablePart.py +++ b/edg/abstract_parts/PartsTablePart.py @@ -116,8 +116,8 @@ class PartsTableSelectorFootprint(PartsTableFootprintFilter, FootprintBlock, Has def _row_generate(self, row: PartsTableRow) -> None: super()._row_generate(row) self.footprint( - self.standard_footprint().REFDES_PREFIX, row[self.KICAD_FOOTPRINT], - self.standard_footprint()._make_pinning(self, row[self.KICAD_FOOTPRINT]), + self._standard_footprint().REFDES_PREFIX, row[self.KICAD_FOOTPRINT], + self._standard_footprint()._make_pinning(self, row[self.KICAD_FOOTPRINT]), mfr=row[self.MANUFACTURER_COL], part=row[self.PART_NUMBER_COL], value=row[self.DESCRIPTION_COL], datasheet=row[self.DATASHEET_COL] diff --git a/edg/abstract_parts/StandardFootprint.py b/edg/abstract_parts/StandardFootprint.py index fd3910359..292c72aad 100644 --- a/edg/abstract_parts/StandardFootprint.py +++ b/edg/abstract_parts/StandardFootprint.py @@ -45,7 +45,7 @@ class HasStandardFootprint(Block): Callable[[], Type[StandardFootprint[Self]]]]] @classmethod - def standard_footprint(cls) -> Type[StandardFootprint[Self]]: + def _standard_footprint(cls) -> Type[StandardFootprint[Self]]: """Returns the StandardFootprint class for this block""" if callable(cls._STANDARD_FOOTPRINT): return cls._STANDARD_FOOTPRINT() # type: ignore diff --git a/edg/parts/JlcElectrolyticCapacitor.py b/edg/parts/JlcElectrolyticCapacitor.py index 7af7ed8bd..d8e6abbf5 100644 --- a/edg/parts/JlcElectrolyticCapacitor.py +++ b/edg/parts/JlcElectrolyticCapacitor.py @@ -38,7 +38,7 @@ def parse_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: else: return None - if new_cols[cls.KICAD_FOOTPRINT] not in cls.standard_footprint()._footprint_pinning_map(): + if new_cols[cls.KICAD_FOOTPRINT] not in cls._standard_footprint()._footprint_pinning_map(): return None new_cols.update(cls._parse_jlcpcb_common(row)) From de06871ec98ac3c79f79513e3b784aa7e5311bae Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Thu, 27 Nov 2025 16:24:44 -0800 Subject: [PATCH 21/21] cleanup --- edg/abstract_parts/AbstractFerriteBead.py | 1 - edg/abstract_parts/StandardFootprint.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/edg/abstract_parts/AbstractFerriteBead.py b/edg/abstract_parts/AbstractFerriteBead.py index 002a5e0e9..a43f34717 100644 --- a/edg/abstract_parts/AbstractFerriteBead.py +++ b/edg/abstract_parts/AbstractFerriteBead.py @@ -64,7 +64,6 @@ class FerriteBeadStandardFootprint(StandardFootprint[FerriteBead]): } - @non_library class TableFerriteBead(PartsTableSelector, FerriteBead): CURRENT_RATING = PartsTableColumn(Range) diff --git a/edg/abstract_parts/StandardFootprint.py b/edg/abstract_parts/StandardFootprint.py index 292c72aad..5eb34fd4b 100644 --- a/edg/abstract_parts/StandardFootprint.py +++ b/edg/abstract_parts/StandardFootprint.py @@ -24,7 +24,7 @@ def _footprint_pinning_map(cls) -> Dict[str, PinningFunction[StandardPinningType assert pinning_footprint not in footprint_map, f"duplicate footprint entry {pinning_footprint}" footprint_map[pinning_footprint] = pinning_fn elif isinstance(pinning_footprints, str): - assert pinning_footprints not in footprint_map, f"duplicate footprint entry {pinning_footprint}" + assert pinning_footprints not in footprint_map, f"duplicate footprint entry {pinning_footprints}" footprint_map[pinning_footprints] = pinning_fn else: raise ValueError(f"unknown footprint entry {pinning_footprints}")