Skip to content

Commit ddd8f11

Browse files
authored
Enable strict typing with generics (#438)
Adds default type parameters to generics to allow most code to work while the "default" generic class is a base class of sorts. This makes a lot of type parameters covariant. The exception is Vector, which allows mutable elements, and remains invariant. BaseVector (non-parameterized) serves as the base class. This also properly parameterizes the StandardFootprint type, so the block type in the lambdas are checked. HasStandardFootprint also now requires a StandardFootprint type-parameterzed on Self. To avoid circular dependencies, the _STANDARD_FOOTPRINT can be a lambda. This uses typing_extensions for TypeVar defaults and Self for earlier version of Python. Also removes OrderedDict and ABCMeta and the IR type parameter for Block.
1 parent b30392e commit ddd8f11

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+538
-509
lines changed

edg/abstract_parts/AbstractBjt.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,11 @@
77
from .StandardFootprint import StandardFootprint, HasStandardFootprint
88

99

10-
class BjtStandardFootprint(StandardFootprint['Bjt']):
11-
REFDES_PREFIX = 'Q'
12-
13-
FOOTPRINT_PINNING_MAP = {
14-
(
15-
'Package_TO_SOT_SMD:SOT-23',
16-
'Package_TO_SOT_SMD:SOT-323_SC-70',
17-
): lambda block: {
18-
'1': block.base,
19-
'2': block.emitter,
20-
'3': block.collector,
21-
},
22-
'Package_TO_SOT_SMD:SOT-89-3': lambda block: {
23-
'1': block.base,
24-
'2': block.collector,
25-
'3': block.emitter,
26-
},
27-
}
28-
29-
3010
@abstract_block
3111
class Bjt(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint):
3212
"""Base class for untyped BJTs
3313
"""
34-
_STANDARD_FOOTPRINT = BjtStandardFootprint
14+
_STANDARD_FOOTPRINT = lambda: BjtStandardFootprint
3515

3616
def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
3717
# TODO actually check that the device channel corresponds with the schematic?
@@ -83,6 +63,26 @@ def contents(self) -> None:
8363
)
8464

8565

66+
class BjtStandardFootprint(StandardFootprint[Bjt]):
67+
REFDES_PREFIX = 'Q'
68+
69+
FOOTPRINT_PINNING_MAP = {
70+
(
71+
'Package_TO_SOT_SMD:SOT-23',
72+
'Package_TO_SOT_SMD:SOT-323_SC-70',
73+
): lambda block: {
74+
'1': block.base,
75+
'2': block.emitter,
76+
'3': block.collector,
77+
},
78+
'Package_TO_SOT_SMD:SOT-89-3': lambda block: {
79+
'1': block.base,
80+
'2': block.collector,
81+
'3': block.emitter,
82+
},
83+
}
84+
85+
8686
class TableBjt(PartsTableSelector, Bjt):
8787
VCE_RATING = PartsTableColumn(Range)
8888
ICE_RATING = PartsTableColumn(Range)

edg/abstract_parts/AbstractCapacitor.py

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -11,77 +11,6 @@
1111
from .StandardFootprint import StandardFootprint, HasStandardFootprint
1212

1313

14-
class CapacitorStandardFootprint(StandardFootprint['Capacitor']):
15-
REFDES_PREFIX = 'C'
16-
17-
# IMPORTANT! DummyFootprint doesn't use this, it will break on anything that isn't this pinning
18-
FOOTPRINT_PINNING_MAP = {
19-
(
20-
'Capacitor_SMD:C_0201_0603Metric',
21-
'Capacitor_SMD:C_0402_1005Metric',
22-
'Capacitor_SMD:C_0603_1608Metric',
23-
'Capacitor_SMD:C_0805_2012Metric',
24-
'Capacitor_SMD:C_1206_3216Metric',
25-
'Capacitor_SMD:C_1210_3225Metric',
26-
'Capacitor_SMD:C_1812_4532Metric',
27-
'Capacitor_SMD:C_2512_6332Metric',
28-
29-
'Capacitor_SMD:CP_Elec_3x5.3',
30-
'Capacitor_SMD:CP_Elec_3x5.4',
31-
'Capacitor_SMD:CP_Elec_4x3',
32-
'Capacitor_SMD:CP_Elec_4x3.9',
33-
'Capacitor_SMD:CP_Elec_4x4.5',
34-
'Capacitor_SMD:CP_Elec_4x5.3',
35-
'Capacitor_SMD:CP_Elec_4x5.4',
36-
'Capacitor_SMD:CP_Elec_4x5.7',
37-
'Capacitor_SMD:CP_Elec_4x5.8',
38-
'Capacitor_SMD:CP_Elec_5x3',
39-
'Capacitor_SMD:CP_Elec_5x3.9',
40-
'Capacitor_SMD:CP_Elec_5x4.4',
41-
'Capacitor_SMD:CP_Elec_5x4.5',
42-
'Capacitor_SMD:CP_Elec_5x5.3',
43-
'Capacitor_SMD:CP_Elec_5x5.4',
44-
'Capacitor_SMD:CP_Elec_5x5.7',
45-
'Capacitor_SMD:CP_Elec_5x5.8',
46-
'Capacitor_SMD:CP_Elec_5x5.9',
47-
'Capacitor_SMD:CP_Elec_6.3x3',
48-
'Capacitor_SMD:CP_Elec_6.3x3.9',
49-
'Capacitor_SMD:CP_Elec_6.3x4.5',
50-
'Capacitor_SMD:CP_Elec_6.3x4.9',
51-
'Capacitor_SMD:CP_Elec_6.3x5.2',
52-
'Capacitor_SMD:CP_Elec_6.3x5.3',
53-
'Capacitor_SMD:CP_Elec_6.3x5.4',
54-
'Capacitor_SMD:CP_Elec_6.3x5.7',
55-
'Capacitor_SMD:CP_Elec_6.3x5.8',
56-
'Capacitor_SMD:CP_Elec_6.3x5.9',
57-
'Capacitor_SMD:CP_Elec_6.3x7.7',
58-
'Capacitor_SMD:CP_Elec_6.3x9.9',
59-
'Capacitor_SMD:CP_Elec_8x5.4',
60-
'Capacitor_SMD:CP_Elec_8x6.2',
61-
'Capacitor_SMD:CP_Elec_8x6.5',
62-
'Capacitor_SMD:CP_Elec_8x6.7',
63-
'Capacitor_SMD:CP_Elec_8x6.9',
64-
'Capacitor_SMD:CP_Elec_8x10',
65-
'Capacitor_SMD:CP_Elec_8x10.5',
66-
'Capacitor_SMD:CP_Elec_8x11.9',
67-
'Capacitor_SMD:CP_Elec_10x7.7',
68-
'Capacitor_SMD:CP_Elec_10x7.9',
69-
'Capacitor_SMD:CP_Elec_10x10',
70-
'Capacitor_SMD:CP_Elec_10x10.5',
71-
'Capacitor_SMD:CP_Elec_10x12.5',
72-
'Capacitor_SMD:CP_Elec_10x12.6',
73-
'Capacitor_SMD:CP_Elec_10x14.3',
74-
'Capacitor_SMD:CP_Elec_16x17.5',
75-
'Capacitor_SMD:CP_Elec_16x22',
76-
'Capacitor_SMD:CP_Elec_18x7.5',
77-
'Capacitor_SMD:CP_Elec_18x22',
78-
): lambda block: {
79-
'1': block.pos,
80-
'2': block.neg,
81-
},
82-
}
83-
84-
8514
@abstract_block
8615
class UnpolarizedCapacitor(PassiveComponent):
8716
"""Base type for a capacitor, that defines its parameters and without ports (since capacitors can be polarized)"""
@@ -119,7 +48,7 @@ def contents(self) -> None:
11948
@abstract_block
12049
class Capacitor(UnpolarizedCapacitor, KiCadInstantiableBlock, HasStandardFootprint):
12150
"""Polarized capacitor, which we assume will be the default"""
122-
_STANDARD_FOOTPRINT = CapacitorStandardFootprint
51+
_STANDARD_FOOTPRINT = lambda: CapacitorStandardFootprint
12352

12453
CAPACITOR_REGEX = re.compile("^" + f"([\d.{PartParserUtil.SI_PREFIXES}]+)\s*F?" +
12554
"\s*" + "((?:\+-|\+/-|±)?\s*[\d.]+\s*%)?" +
@@ -156,6 +85,77 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
15685
self.neg = self.Port(Passive.empty())
15786

15887

88+
class CapacitorStandardFootprint(StandardFootprint[Capacitor]):
89+
REFDES_PREFIX = 'C'
90+
91+
# IMPORTANT! DummyFootprint doesn't use this, it will break on anything that isn't this pinning
92+
FOOTPRINT_PINNING_MAP = {
93+
(
94+
'Capacitor_SMD:C_0201_0603Metric',
95+
'Capacitor_SMD:C_0402_1005Metric',
96+
'Capacitor_SMD:C_0603_1608Metric',
97+
'Capacitor_SMD:C_0805_2012Metric',
98+
'Capacitor_SMD:C_1206_3216Metric',
99+
'Capacitor_SMD:C_1210_3225Metric',
100+
'Capacitor_SMD:C_1812_4532Metric',
101+
'Capacitor_SMD:C_2512_6332Metric',
102+
103+
'Capacitor_SMD:CP_Elec_3x5.3',
104+
'Capacitor_SMD:CP_Elec_3x5.4',
105+
'Capacitor_SMD:CP_Elec_4x3',
106+
'Capacitor_SMD:CP_Elec_4x3.9',
107+
'Capacitor_SMD:CP_Elec_4x4.5',
108+
'Capacitor_SMD:CP_Elec_4x5.3',
109+
'Capacitor_SMD:CP_Elec_4x5.4',
110+
'Capacitor_SMD:CP_Elec_4x5.7',
111+
'Capacitor_SMD:CP_Elec_4x5.8',
112+
'Capacitor_SMD:CP_Elec_5x3',
113+
'Capacitor_SMD:CP_Elec_5x3.9',
114+
'Capacitor_SMD:CP_Elec_5x4.4',
115+
'Capacitor_SMD:CP_Elec_5x4.5',
116+
'Capacitor_SMD:CP_Elec_5x5.3',
117+
'Capacitor_SMD:CP_Elec_5x5.4',
118+
'Capacitor_SMD:CP_Elec_5x5.7',
119+
'Capacitor_SMD:CP_Elec_5x5.8',
120+
'Capacitor_SMD:CP_Elec_5x5.9',
121+
'Capacitor_SMD:CP_Elec_6.3x3',
122+
'Capacitor_SMD:CP_Elec_6.3x3.9',
123+
'Capacitor_SMD:CP_Elec_6.3x4.5',
124+
'Capacitor_SMD:CP_Elec_6.3x4.9',
125+
'Capacitor_SMD:CP_Elec_6.3x5.2',
126+
'Capacitor_SMD:CP_Elec_6.3x5.3',
127+
'Capacitor_SMD:CP_Elec_6.3x5.4',
128+
'Capacitor_SMD:CP_Elec_6.3x5.7',
129+
'Capacitor_SMD:CP_Elec_6.3x5.8',
130+
'Capacitor_SMD:CP_Elec_6.3x5.9',
131+
'Capacitor_SMD:CP_Elec_6.3x7.7',
132+
'Capacitor_SMD:CP_Elec_6.3x9.9',
133+
'Capacitor_SMD:CP_Elec_8x5.4',
134+
'Capacitor_SMD:CP_Elec_8x6.2',
135+
'Capacitor_SMD:CP_Elec_8x6.5',
136+
'Capacitor_SMD:CP_Elec_8x6.7',
137+
'Capacitor_SMD:CP_Elec_8x6.9',
138+
'Capacitor_SMD:CP_Elec_8x10',
139+
'Capacitor_SMD:CP_Elec_8x10.5',
140+
'Capacitor_SMD:CP_Elec_8x11.9',
141+
'Capacitor_SMD:CP_Elec_10x7.7',
142+
'Capacitor_SMD:CP_Elec_10x7.9',
143+
'Capacitor_SMD:CP_Elec_10x10',
144+
'Capacitor_SMD:CP_Elec_10x10.5',
145+
'Capacitor_SMD:CP_Elec_10x12.5',
146+
'Capacitor_SMD:CP_Elec_10x12.6',
147+
'Capacitor_SMD:CP_Elec_10x14.3',
148+
'Capacitor_SMD:CP_Elec_16x17.5',
149+
'Capacitor_SMD:CP_Elec_16x22',
150+
'Capacitor_SMD:CP_Elec_18x7.5',
151+
'Capacitor_SMD:CP_Elec_18x22',
152+
): lambda block: {
153+
'1': block.pos,
154+
'2': block.neg,
155+
},
156+
}
157+
158+
159159
@abstract_block
160160
class CeramicCapacitor(Capacitor):
161161
"""Abstract base class for ceramic capacitors, which appear more ideal in terms of lower ESP"""

edg/abstract_parts/AbstractCrystal.py

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,31 @@
66
from .StandardFootprint import StandardFootprint, HasStandardFootprint
77

88

9+
@abstract_block
10+
class Crystal(DiscreteComponent, HasStandardFootprint):
11+
_STANDARD_FOOTPRINT = lambda: CrystalStandardFootprint
12+
13+
def __init__(self, frequency: RangeLike) -> None:
14+
"""Discrete crystal component."""
15+
super().__init__()
16+
17+
self.frequency = self.ArgParameter(frequency)
18+
self.actual_frequency = self.Parameter(RangeExpr())
19+
self.actual_capacitance = self.Parameter(FloatExpr())
20+
21+
self.crystal = self.Port(CrystalPort(self.actual_frequency), [InOut]) # set by subclass
22+
self.gnd = self.Port(Ground(), [Common])
23+
24+
def contents(self) -> None:
25+
super().contents()
26+
27+
self.description = DescriptionString(
28+
"<b>frequency:</b> ", DescriptionString.FormatUnits(self.actual_frequency, "Hz"),
29+
" <b>of spec:</b> ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n",
30+
"<b>capacitance:</b> ", DescriptionString.FormatUnits(self.actual_capacitance, "F")
31+
)
32+
33+
934
class CrystalStandardFootprint(StandardFootprint['Crystal']):
1035
REFDES_PREFIX = 'X'
1136

@@ -31,31 +56,6 @@ class CrystalStandardFootprint(StandardFootprint['Crystal']):
3156
}
3257

3358

34-
@abstract_block
35-
class Crystal(DiscreteComponent, HasStandardFootprint):
36-
_STANDARD_FOOTPRINT = CrystalStandardFootprint
37-
38-
def __init__(self, frequency: RangeLike) -> None:
39-
"""Discrete crystal component."""
40-
super().__init__()
41-
42-
self.frequency = self.ArgParameter(frequency)
43-
self.actual_frequency = self.Parameter(RangeExpr())
44-
self.actual_capacitance = self.Parameter(FloatExpr())
45-
46-
self.crystal = self.Port(CrystalPort(self.actual_frequency), [InOut]) # set by subclass
47-
self.gnd = self.Port(Ground(), [Common])
48-
49-
def contents(self) -> None:
50-
super().contents()
51-
52-
self.description = DescriptionString(
53-
"<b>frequency:</b> ", DescriptionString.FormatUnits(self.actual_frequency, "Hz"),
54-
" <b>of spec:</b> ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n",
55-
"<b>capacitance:</b> ", DescriptionString.FormatUnits(self.actual_capacitance, "F")
56-
)
57-
58-
5959
@non_library
6060
class TableCrystal(PartsTableSelector, Crystal):
6161
FREQUENCY = PartsTableColumn(Range)

edg/abstract_parts/AbstractDiodes.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@
99
from .StandardFootprint import StandardFootprint, HasStandardFootprint
1010

1111

12+
@non_library
13+
class BaseDiode(DiscreteSemiconductor, HasStandardFootprint):
14+
"""Base class for diodes, with anode and cathode pins, including a very wide range of devices.
15+
"""
16+
_STANDARD_FOOTPRINT = lambda: DiodeStandardFootprint
17+
18+
def __init__(self) -> None:
19+
super().__init__()
20+
21+
self.anode = self.Port(Passive.empty())
22+
self.cathode = self.Port(Passive.empty())
23+
24+
1225
class DiodeStandardFootprint(StandardFootprint['BaseDiode']):
1326
REFDES_PREFIX = 'D'
1427

@@ -35,19 +48,6 @@ class DiodeStandardFootprint(StandardFootprint['BaseDiode']):
3548
}
3649

3750

38-
@non_library
39-
class BaseDiode(DiscreteSemiconductor, HasStandardFootprint):
40-
"""Base class for diodes, with anode and cathode pins, including a very wide range of devices.
41-
"""
42-
_STANDARD_FOOTPRINT = DiodeStandardFootprint
43-
44-
def __init__(self) -> None:
45-
super().__init__()
46-
47-
self.anode = self.Port(Passive.empty())
48-
self.cathode = self.Port(Passive.empty())
49-
50-
5151
@abstract_block
5252
class Diode(KiCadImportableBlock, BaseDiode):
5353
"""Base class for untyped diodes

edg/abstract_parts/AbstractFerriteBead.py

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,9 @@
77
from .StandardFootprint import StandardFootprint, HasStandardFootprint
88

99

10-
class FerriteBeadStandardFootprint(StandardFootprint['FerriteBead']):
11-
REFDES_PREFIX = 'FB'
12-
13-
FOOTPRINT_PINNING_MAP = {
14-
(
15-
'Inductor_SMD:L_0201_0603Metric',
16-
'Inductor_SMD:L_0402_1005Metric',
17-
'Inductor_SMD:L_0603_1608Metric',
18-
'Inductor_SMD:L_0805_2012Metric',
19-
'Inductor_SMD:L_1206_3216Metric',
20-
'Inductor_SMD:L_1210_3225Metric',
21-
'Inductor_SMD:L_1812_4532Metric',
22-
'Inductor_SMD:L_2010_5025Metric',
23-
'Inductor_SMD:L_2512_6332Metric',
24-
): lambda block: {
25-
'1': block.a,
26-
'2': block.b,
27-
},
28-
}
29-
30-
3110
@abstract_block
3211
class FerriteBead(PassiveComponent, KiCadImportableBlock, HasStandardFootprint):
33-
_STANDARD_FOOTPRINT = FerriteBeadStandardFootprint
12+
_STANDARD_FOOTPRINT = lambda: FerriteBeadStandardFootprint
3413

3514
def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
3615
assert symbol_name in ('Device:L_Ferrite', 'Device:L_Ferrite_Small')
@@ -64,10 +43,29 @@ def contents(self) -> None:
6443
)
6544

6645

46+
class FerriteBeadStandardFootprint(StandardFootprint[FerriteBead]):
47+
REFDES_PREFIX = 'FB'
48+
49+
FOOTPRINT_PINNING_MAP = {
50+
(
51+
'Inductor_SMD:L_0201_0603Metric',
52+
'Inductor_SMD:L_0402_1005Metric',
53+
'Inductor_SMD:L_0603_1608Metric',
54+
'Inductor_SMD:L_0805_2012Metric',
55+
'Inductor_SMD:L_1206_3216Metric',
56+
'Inductor_SMD:L_1210_3225Metric',
57+
'Inductor_SMD:L_1812_4532Metric',
58+
'Inductor_SMD:L_2010_5025Metric',
59+
'Inductor_SMD:L_2512_6332Metric',
60+
): lambda block: {
61+
'1': block.a,
62+
'2': block.b,
63+
},
64+
}
65+
66+
6767
@non_library
6868
class TableFerriteBead(PartsTableSelector, FerriteBead):
69-
_STANDARD_FOOTPRINT = FerriteBeadStandardFootprint
70-
7169
CURRENT_RATING = PartsTableColumn(Range)
7270
HF_IMPEDANCE = PartsTableColumn(Range)
7371
DC_RESISTANCE = PartsTableColumn(Range)

0 commit comments

Comments
 (0)