pyedb 0.56.0__py3-none-any.whl → 0.58.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyedb might be problematic. Click here for more details.

Files changed (110) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/configuration/cfg_data.py +3 -0
  3. pyedb/configuration/cfg_pin_groups.py +2 -0
  4. pyedb/configuration/cfg_terminals.py +232 -0
  5. pyedb/configuration/configuration.py +146 -3
  6. pyedb/dotnet/clr_module.py +1 -2
  7. pyedb/dotnet/database/Variables.py +30 -22
  8. pyedb/dotnet/database/cell/hierarchy/component.py +2 -8
  9. pyedb/dotnet/database/cell/layout.py +5 -1
  10. pyedb/dotnet/database/cell/primitive/primitive.py +2 -2
  11. pyedb/dotnet/database/cell/terminal/bundle_terminal.py +12 -0
  12. pyedb/dotnet/database/cell/terminal/pingroup_terminal.py +1 -1
  13. pyedb/dotnet/database/cell/terminal/terminal.py +38 -0
  14. pyedb/dotnet/database/components.py +15 -19
  15. pyedb/dotnet/database/dotnet/database.py +1 -0
  16. pyedb/dotnet/database/edb_data/control_file.py +19 -8
  17. pyedb/dotnet/database/edb_data/nets_data.py +3 -3
  18. pyedb/dotnet/database/edb_data/padstacks_data.py +39 -14
  19. pyedb/dotnet/database/edb_data/ports.py +0 -25
  20. pyedb/dotnet/database/edb_data/primitives_data.py +3 -3
  21. pyedb/dotnet/database/edb_data/raptor_x_simulation_setup_data.py +18 -19
  22. pyedb/dotnet/database/edb_data/simulation_configuration.py +3 -3
  23. pyedb/dotnet/database/edb_data/sources.py +21 -2
  24. pyedb/dotnet/database/general.py +1 -6
  25. pyedb/dotnet/database/hfss.py +9 -8
  26. pyedb/dotnet/database/layout_validation.py +14 -3
  27. pyedb/dotnet/database/materials.py +1 -3
  28. pyedb/dotnet/database/modeler.py +7 -3
  29. pyedb/dotnet/database/nets.py +27 -19
  30. pyedb/dotnet/database/padstack.py +4 -2
  31. pyedb/dotnet/database/sim_setup_data/io/siwave.py +54 -1
  32. pyedb/dotnet/database/siwave.py +4 -3
  33. pyedb/dotnet/database/stackup.py +55 -58
  34. pyedb/dotnet/database/utilities/heatsink.py +0 -1
  35. pyedb/dotnet/database/utilities/hfss_simulation_setup.py +81 -0
  36. pyedb/dotnet/database/utilities/simulation_setup.py +7 -5
  37. pyedb/dotnet/database/utilities/siwave_cpa_simulation_setup.py +1 -0
  38. pyedb/dotnet/database/utilities/siwave_simulation_setup.py +264 -13
  39. pyedb/dotnet/edb.py +65 -47
  40. pyedb/exceptions.py +1 -2
  41. pyedb/extensions/create_cell_array.py +67 -49
  42. pyedb/generic/data_handlers.py +13 -23
  43. pyedb/generic/design_types.py +9 -35
  44. pyedb/generic/filesystem.py +4 -2
  45. pyedb/generic/general_methods.py +28 -41
  46. pyedb/generic/plot.py +8 -23
  47. pyedb/generic/process.py +78 -10
  48. pyedb/grpc/database/_typing.py +0 -0
  49. pyedb/grpc/database/components.py +14 -13
  50. pyedb/grpc/database/control_file.py +27 -40
  51. pyedb/grpc/database/definition/materials.py +1 -1
  52. pyedb/grpc/database/definition/package_def.py +6 -3
  53. pyedb/grpc/database/definition/padstack_def.py +14 -12
  54. pyedb/grpc/database/hfss.py +1 -4
  55. pyedb/grpc/database/hierarchy/component.py +5 -13
  56. pyedb/grpc/database/hierarchy/pingroup.py +16 -3
  57. pyedb/grpc/database/layers/layer.py +1 -2
  58. pyedb/grpc/database/layers/stackup_layer.py +42 -19
  59. pyedb/grpc/database/layout/layout.py +43 -27
  60. pyedb/grpc/database/layout/voltage_regulator.py +6 -1
  61. pyedb/grpc/database/layout_validation.py +5 -2
  62. pyedb/grpc/database/modeler.py +254 -252
  63. pyedb/grpc/database/net/differential_pair.py +9 -2
  64. pyedb/grpc/database/net/extended_net.py +24 -9
  65. pyedb/grpc/database/net/net.py +14 -5
  66. pyedb/grpc/database/net/net_class.py +24 -7
  67. pyedb/grpc/database/nets.py +11 -43
  68. pyedb/grpc/database/padstacks.py +67 -119
  69. pyedb/grpc/database/primitive/bondwire.py +3 -67
  70. pyedb/grpc/database/primitive/circle.py +42 -3
  71. pyedb/grpc/database/primitive/padstack_instance.py +58 -31
  72. pyedb/grpc/database/primitive/path.py +160 -11
  73. pyedb/grpc/database/primitive/polygon.py +73 -7
  74. pyedb/grpc/database/primitive/primitive.py +2 -2
  75. pyedb/grpc/database/primitive/rectangle.py +105 -4
  76. pyedb/grpc/database/simulation_setup/hfss_general_settings.py +0 -2
  77. pyedb/grpc/database/simulation_setup/hfss_settings_options.py +0 -4
  78. pyedb/grpc/database/simulation_setup/hfss_simulation_setup.py +79 -0
  79. pyedb/grpc/database/simulation_setup/siwave_cpa_simulation_setup.py +1 -0
  80. pyedb/grpc/database/simulation_setup/sweep_data.py +1 -3
  81. pyedb/grpc/database/siwave.py +6 -13
  82. pyedb/grpc/database/source_excitations.py +46 -63
  83. pyedb/grpc/database/stackup.py +55 -60
  84. pyedb/grpc/database/terminal/bundle_terminal.py +10 -3
  85. pyedb/grpc/database/terminal/padstack_instance_terminal.py +9 -11
  86. pyedb/grpc/database/terminal/pingroup_terminal.py +8 -1
  87. pyedb/grpc/database/terminal/point_terminal.py +30 -0
  88. pyedb/grpc/database/terminal/terminal.py +35 -10
  89. pyedb/grpc/database/utility/heat_sink.py +0 -1
  90. pyedb/grpc/database/utility/hfss_extent_info.py +2 -2
  91. pyedb/grpc/database/utility/xml_control_file.py +19 -8
  92. pyedb/grpc/edb.py +63 -32
  93. pyedb/grpc/edb_init.py +1 -0
  94. pyedb/ipc2581/ecad/cad_data/layer_feature.py +6 -2
  95. pyedb/ipc2581/ecad/cad_data/step.py +1 -1
  96. pyedb/ipc2581/ipc2581.py +8 -7
  97. pyedb/libraries/common.py +3 -4
  98. pyedb/libraries/rf_libraries/base_functions.py +7 -16
  99. pyedb/libraries/rf_libraries/planar_antennas.py +3 -21
  100. pyedb/misc/aedtlib_personalib_install.py +2 -2
  101. pyedb/misc/downloads.py +19 -3
  102. pyedb/misc/misc.py +5 -2
  103. pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +3 -2
  104. pyedb/misc/siw_feature_config/xtalk_scan/scan_config.py +0 -1
  105. pyedb/misc/utilities.py +0 -1
  106. pyedb/modeler/geometry_operators.py +3 -2
  107. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/METADATA +6 -7
  108. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/RECORD +110 -108
  109. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/WHEEL +0 -0
  110. {pyedb-0.56.0.dist-info → pyedb-0.58.0.dist-info}/licenses/LICENSE +0 -0
@@ -23,21 +23,25 @@
23
23
  """
24
24
  This module contains the array building feature from unit cell.
25
25
  """
26
+
26
27
  import itertools
27
28
  from typing import Optional, Union
29
+ import warnings
28
30
 
29
31
  from pyedb import Edb
32
+ from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
33
+ from pyedb.misc.decorators import execution_timer
30
34
 
31
35
 
32
36
  # ----------------------
33
- # Public façade function
37
+ # Public api
34
38
  # ----------------------
35
39
  def create_array_from_unit_cell(
36
40
  edb: Edb,
37
41
  x_number: int = 2,
38
42
  y_number: int = 2,
39
- offset_x: Optional[Union[int, float]] = None,
40
- offset_y: Optional[Union[int, float]] = None,
43
+ offset_x: Optional[Union[int, float, str]] = None,
44
+ offset_y: Optional[Union[int, float, str]] = None,
41
45
  ) -> bool:
42
46
  """
43
47
  Create a 2-D rectangular array from the current EDB unit cell.
@@ -56,10 +60,10 @@ def create_array_from_unit_cell(
56
60
  Number of columns (X-direction). Must be > 0. Defaults to 2.
57
61
  y_number : int, optional
58
62
  Number of rows (Y-direction). Must be > 0. Defaults to 2.
59
- offset_x : int | float | None, optional
63
+ offset_x : int | float | str, None, optional
60
64
  Horizontal pitch (distance between cell origins). When *None* the
61
65
  value is derived from the outline geometry.
62
- offset_y : int | float | None, optional
66
+ offset_y : int | float | str, None, optional
63
67
  Vertical pitch (distance between cell origins). When *None* the
64
68
  value is derived from the outline geometry.
65
69
 
@@ -93,12 +97,15 @@ def create_array_from_unit_cell(
93
97
  adapter = _GrpcAdapter(edb)
94
98
  else:
95
99
  adapter = _DotNetAdapter(edb)
100
+ warnings.warn(".NET back-end is deprecated and will be removed in future releases.", UserWarning)
101
+ warnings.warn("Consider moving to PyEDB gRPC (ANSYS 2025R2) for better performances", UserWarning)
96
102
  return __create_array_from_unit_cell_impl(edb, adapter, x_number, y_number, offset_x, offset_y)
97
103
 
98
104
 
99
105
  # ------------------------------------------------------------------
100
106
  # Implementation (technology-agnostic)
101
107
  # ------------------------------------------------------------------
108
+ @execution_timer("create_array_from_unit_cell")
102
109
  def __create_array_from_unit_cell_impl(
103
110
  edb: Edb,
104
111
  adapter: "_BaseAdapter",
@@ -133,8 +140,12 @@ def __create_array_from_unit_cell_impl(
133
140
  # ---------- Sanity & auto-pitch detection ----------
134
141
  if x_number <= 0 or y_number <= 0:
135
142
  raise ValueError("x_number and y_number must be positive integers")
143
+ if offset_x and not offset_y:
144
+ raise ValueError("If offset_x is provided, offset_y must be provided as well")
145
+ if offset_y and not offset_x:
146
+ raise ValueError("If offset_y is provided, offset_x must be provided as well")
136
147
 
137
- if offset_x is None or offset_y is None:
148
+ if not offset_x and not offset_y:
138
149
  edb.logger.info("Auto-detecting outline extents")
139
150
  outline_prims = [p for p in edb.modeler.primitives if p.layer_name.lower() == "outline"]
140
151
  if not outline_prims:
@@ -143,40 +154,52 @@ def __create_array_from_unit_cell_impl(
143
154
  if not adapter.is_supported_outline(outline):
144
155
  raise RuntimeError("Outline primitive is not a polygon/rectangle. Provide offset_x / offset_y.")
145
156
  offset_x, offset_y = adapter.pitch_from_outline(outline)
157
+ offset_x = edb.value(offset_x)
158
+ offset_y = edb.value(offset_y)
146
159
 
147
160
  # ---------- Collect everything we have to replicate ----------
148
161
  primitives = [p for p in edb.modeler.primitives if adapter.is_primitive_to_copy(p)]
149
162
  paths = list(edb.modeler.paths)
150
163
  vias = list(edb.padstacks.vias.values())
151
164
  components = list(edb.components.instances.values())
165
+ pingroups = edb.layout.pin_groups
166
+ if edb.grpc:
167
+ pg_dict = {
168
+ pad.edb_uid: pg.name # edb_uid → PinGroup.name
169
+ for pg in pingroups # for every PinGroup
170
+ for pad in pg.pins.values() # for every PadstackInstance in its pin-dict
171
+ }
172
+ else:
173
+ pg_dict = {
174
+ pad.id: pg.name # edb_uid → PinGroup.name
175
+ for pg in pingroups # for every PinGroup
176
+ for pad in pg.pins.values() # for every PadstackInstance in its pin-dict
177
+ }
152
178
 
153
179
  # ---------- Replication loops ----------
154
180
  edb.logger.info(f"Starting array replication {x_number}×{y_number}")
181
+ total_number = x_number * y_number - 1 # minus original
182
+ cell_count = 0
155
183
  for i, j in itertools.product(range(x_number), range(y_number)):
156
184
  if i == 0 and j == 0:
157
185
  continue # original already exists
158
-
159
186
  dx = edb.value(offset_x * i)
160
187
  dy = edb.value(offset_y * j)
161
-
188
+ # Components
189
+ for comp in components:
190
+ adapter.duplicate_component(comp, dx, dy, i, j, pin_groups=pg_dict)
162
191
  # Primitives & voids
163
192
  for prim in primitives:
164
- new_poly = adapter.duplicate_primitive(prim, dx, dy, i, j)
165
- for void in prim.voids:
166
- adapter.duplicate_void(new_poly, void, dx, dy)
167
-
193
+ if not prim.is_void:
194
+ adapter.duplicate_primitive(prim, dx, dy, i, j)
168
195
  # Paths
169
196
  for path in paths:
170
197
  adapter.duplicate_path(path, dx, dy, i, j)
171
-
172
198
  # Stand-alone vias
173
199
  for via in (v for v in vias if not v.component):
174
200
  adapter.duplicate_standalone_via(via, dx, dy, i, j)
175
-
176
- # Components
177
- for comp in components:
178
- adapter.duplicate_component(comp, dx, dy, i, j)
179
-
201
+ cell_count += 1
202
+ edb.logger.info(f"Replicated cell {cell_count} of {total_number} ({(cell_count / total_number) * 100:.1f}%)")
180
203
  edb.logger.info("Array replication finished successfully")
181
204
  return True
182
205
 
@@ -189,6 +212,8 @@ class _BaseAdapter:
189
212
 
190
213
  def __init__(self, edb: Edb):
191
214
  self.edb = edb
215
+ self.layers = edb.stackup.layers
216
+ self.active_layout = edb.active_layout
192
217
 
193
218
  # ---- Outline helpers ----
194
219
  def is_supported_outline(self, outline) -> bool:
@@ -215,10 +240,6 @@ class _BaseAdapter:
215
240
  """Return a new primitive translated by (dx, dy)."""
216
241
  raise NotImplementedError
217
242
 
218
- def duplicate_void(self, new_poly, void, dx, dy):
219
- """Add a translated copy of *void* to *new_poly*."""
220
- raise NotImplementedError
221
-
222
243
  def duplicate_path(self, path, dx, dy, i, j):
223
244
  """Create a translated copy of *path*."""
224
245
  raise NotImplementedError
@@ -227,7 +248,7 @@ class _BaseAdapter:
227
248
  """Create a translated copy of a stand-alone via."""
228
249
  raise NotImplementedError
229
250
 
230
- def duplicate_component(self, comp, dx, dy, i, j):
251
+ def duplicate_component(self, comp, dx, dy, i, j, pin_groups=None):
231
252
  """Create a translated copy of *comp* (including its pins)."""
232
253
  raise NotImplementedError
233
254
 
@@ -247,15 +268,11 @@ class _GrpcAdapter(_BaseAdapter):
247
268
 
248
269
  def duplicate_primitive(self, prim, dx, dy, i, j):
249
270
  moved_pd = prim.polygon_data.move((dx, dy))
271
+ voids = [voids.polygon_data.move((dx, dy)) for voids in prim.voids]
250
272
  return self.edb.modeler.create_polygon(
251
- moved_pd,
252
- layer_name=prim.layer.name,
253
- net_name=prim.net.name,
273
+ moved_pd, layer_name=prim.layer.name, net_name=prim.net.name, voids=voids
254
274
  )
255
275
 
256
- def duplicate_void(self, new_poly, void, dx, dy):
257
- new_poly.add_void(void.polygon_data.move((dx, dy)))
258
-
259
276
  def duplicate_path(self, path, dx, dy, i, j):
260
277
  moved_line = path.cast().center_line.move((dx, dy))
261
278
  self.edb.modeler.create_trace(
@@ -263,29 +280,30 @@ class _GrpcAdapter(_BaseAdapter):
263
280
  width=path.width,
264
281
  layer_name=path.layer.name,
265
282
  net_name=path.net.name,
283
+ corner_style=path.corner_style,
284
+ start_cap_style=path.end_cap1,
285
+ end_cap_style=path.end_cap2,
266
286
  )
267
287
 
268
288
  def duplicate_standalone_via(self, via, dx, dy, i, j):
269
- from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
270
-
271
289
  pos = via.position
272
290
  PadstackInstance.create(
273
- self.edb.active_layout,
291
+ self.active_layout,
274
292
  net=via.net,
275
- name=f"{via.name}_i{i}_j{j}",
276
- padstack_def=self.edb.padstacks.definitions[via.padstack_definition],
293
+ name=f"{via.name}_X{i}_Y{j}",
294
+ padstack_def=via.definition,
277
295
  position_x=pos[0] + dx,
278
296
  position_y=pos[1] + dy,
279
297
  rotation=0.0,
280
- top_layer=self.edb.stackup.layers[via.start_layer],
281
- bottom_layer=self.edb.stackup.layers[via.stop_layer],
298
+ top_layer=self.layers[via.start_layer],
299
+ bottom_layer=self.layers[via.stop_layer],
282
300
  )
283
301
 
284
- def duplicate_component(self, comp, dx, dy, i, j):
285
- from pyedb.grpc.database.primitive.padstack_instance import PadstackInstance
286
-
302
+ def duplicate_component(self, comp, dx, dy, i, j, pin_groups=None):
287
303
  new_pins = []
304
+ _pg = {}
288
305
  for pin in comp.pins.values():
306
+ pg_name = pin_groups.get(pin.edb_uid, None) if pin_groups else None
289
307
  pos = pin.position
290
308
  new_pin = PadstackInstance.create(
291
309
  self.edb.active_layout,
@@ -299,6 +317,8 @@ class _GrpcAdapter(_BaseAdapter):
299
317
  bottom_layer=self.edb.stackup.layers[pin.stop_layer],
300
318
  )
301
319
  new_pins.append(new_pin)
320
+ if pg_name:
321
+ _pg.setdefault(pg_name, []).append(new_pin)
302
322
 
303
323
  if new_pins:
304
324
  res = self.edb.value(comp.res_value) if hasattr(comp, "res_value") and comp.res_value else None
@@ -313,8 +333,11 @@ class _GrpcAdapter(_BaseAdapter):
313
333
  l_value=ind,
314
334
  c_value=cap,
315
335
  )
336
+ new_comp.type = comp.type
316
337
  if hasattr(comp, "component_property") and comp.component_property:
317
338
  new_comp.component_property = comp.component_property
339
+ for pg_name, pins in _pg.items():
340
+ self.edb.components.create_pingroup_from_pins(pins=pins, group_name=f"{pg_name}_{i}_{j}")
318
341
 
319
342
 
320
343
  class _DotNetAdapter(_BaseAdapter):
@@ -342,14 +365,6 @@ class _DotNetAdapter(_BaseAdapter):
342
365
  net_name=prim.net.name,
343
366
  )
344
367
 
345
- def duplicate_void(self, new_poly, void, dx, dy):
346
- from pyedb.dotnet.database.geometry.point_data import PointData
347
-
348
- vector = PointData.create_from_xy(self.edb, x=dx, y=dy)
349
- void_polygon_data = void.polygon_data
350
- void_polygon_data._edb_object.Move(vector._edb_object)
351
- new_poly.add_void(void_polygon_data.points)
352
-
353
368
  def duplicate_path(self, path, dx, dy, i, j):
354
369
  from pyedb.dotnet.database.geometry.point_data import PointData
355
370
 
@@ -357,11 +372,15 @@ class _DotNetAdapter(_BaseAdapter):
357
372
  moved_path = path._edb_object.GetCenterLine()
358
373
  moved_path.Move(vector._edb_object)
359
374
  moved_path = [[pt.X.ToDouble(), pt.Y.ToDouble()] for pt in list(moved_path.Points)]
375
+ end_caps = path._edb_object.GetEndCapStyle()
376
+
360
377
  self.edb.modeler.create_trace(
361
378
  path_list=list(moved_path),
362
379
  width=path.width,
363
380
  layer_name=path.layer.name,
364
381
  net_name=path.net.name,
382
+ start_cap_style=str(end_caps[1]),
383
+ end_cap_style=str(end_caps[2]),
365
384
  )
366
385
 
367
386
  def duplicate_standalone_via(self, via, dx, dy, i, j):
@@ -372,7 +391,7 @@ class _DotNetAdapter(_BaseAdapter):
372
391
  via_name=f"{via.aedt_name}_i{i}_j{j}",
373
392
  )
374
393
 
375
- def duplicate_component(self, comp, dx, dy, i, j):
394
+ def duplicate_component(self, comp, dx, dy, i, j, pin_groups=None):
376
395
  new_pins = []
377
396
  for pin in comp.pins.values():
378
397
  pos = pin.position
@@ -382,7 +401,6 @@ class _DotNetAdapter(_BaseAdapter):
382
401
  via_name=f"{pin.aedt_name}_i{i}_j{j}",
383
402
  )
384
403
  new_pins.append(new_pin)
385
-
386
404
  if new_pins:
387
405
  new_comp = self.edb.components.create(
388
406
  pins=new_pins,
@@ -72,23 +72,13 @@ def unique_string_list(element_list, only_string=True): # pragma: no cover
72
72
  -------
73
73
 
74
74
  """
75
- if element_list:
76
- if isinstance(element_list, list):
77
- element_list = set(element_list)
78
- elif isinstance(element_list, str):
79
- element_list = [element_list]
80
- else:
81
- error_message = "Invalid list data"
82
- try:
83
- error_message += " {}".format(element_list)
84
- except:
85
- pass
86
- raise Exception(error_message)
75
+ if isinstance(element_list, str):
76
+ element_list = [element_list]
87
77
 
88
- if only_string and any(not isinstance(x, str) for x in element_list):
89
- raise TypeError("Invalid list entries, some elements are not of type string.")
78
+ if only_string and any(not isinstance(x, str) for x in element_list):
79
+ raise TypeError("Invalid list entries, some elements are not of type string.")
90
80
 
91
- return element_list
81
+ return list(set(element_list))
92
82
 
93
83
 
94
84
  def string_list(element_list): # pragma: no cover
@@ -142,28 +132,28 @@ def from_rkm(code): # pragma: no cover
142
132
 
143
133
  Examples
144
134
  --------
145
- >>> from_rkm('R47')
135
+ >>> from_rkm("R47")
146
136
  '0.47'
147
137
 
148
- >>> from_rkm('4R7')
138
+ >>> from_rkm("4R7")
149
139
  '4.7'
150
140
 
151
- >>> from_rkm('470R')
141
+ >>> from_rkm("470R")
152
142
  '470'
153
143
 
154
- >>> from_rkm('4K7')
144
+ >>> from_rkm("4K7")
155
145
  '4.7k'
156
146
 
157
- >>> from_rkm('47K')
147
+ >>> from_rkm("47K")
158
148
  '47k'
159
149
 
160
- >>> from_rkm('47K3')
150
+ >>> from_rkm("47K3")
161
151
  '47.3k'
162
152
 
163
- >>> from_rkm('470K')
153
+ >>> from_rkm("470K")
164
154
  '470k'
165
155
 
166
- >>> from_rkm('4M7')
156
+ >>> from_rkm("4M7")
167
157
  '4.7M'
168
158
 
169
159
  """
@@ -32,18 +32,15 @@ if TYPE_CHECKING:
32
32
 
33
33
 
34
34
  @overload
35
- def Edb(*, grpc: Literal[True], **kwargs) -> "EdbGrpc":
36
- ...
35
+ def Edb(*, grpc: Literal[True], **kwargs) -> "EdbGrpc": ...
37
36
 
38
37
 
39
38
  @overload
40
- def Edb(*, grpc: Literal[False] = False, **kwargs) -> "EdbDotnet":
41
- ...
39
+ def Edb(*, grpc: Literal[False] = False, **kwargs) -> "EdbDotnet": ...
42
40
 
43
41
 
44
42
  @overload
45
- def Edb(*, grpc: bool, **kwargs) -> Union["EdbGrpc", "EdbDotnet"]:
46
- ...
43
+ def Edb(*, grpc: bool, **kwargs) -> Union["EdbGrpc", "EdbDotnet"]: ...
47
44
 
48
45
 
49
46
  # lazy imports
@@ -167,17 +164,10 @@ def Edb(
167
164
  4. Simulation Setup
168
165
 
169
166
  # Create SIwave SYZ setup
170
- >>> syz_setup = edb.create_siwave_syz_setup(
171
- >>> name="GHz_Setup",
172
- >>> start_freq="1GHz",
173
- >>> stop_freq="10GHz"
174
- >>> )
167
+ >>> syz_setup = edb.create_siwave_syz_setup(name="GHz_Setup", start_freq="1GHz", stop_freq="10GHz")
175
168
 
176
169
  # Create SIwave DC setup
177
- >>> dc_setup = edb.create_siwave_dc_setup(
178
- >>> name="DC_Analysis",
179
- >>> use_dc_point=True
180
- >>> )
170
+ >>> dc_setup = edb.create_siwave_dc_setup(name="DC_Analysis", use_dc_point=True)
181
171
 
182
172
  # Solve with SIwave
183
173
  >>> edb.solve_siwave()
@@ -208,17 +198,10 @@ def Edb(
208
198
  7. Port Creation
209
199
 
210
200
  # Create wave port between two pins
211
- >>> wave_port = edb.source_excitation.create_port(
212
- >>> positive_terminal=pin1,
213
- >>> negative_terminal=pin2,
214
- >>> port_type="Wave"
215
- >>> )
201
+ >>> wave_port = edb.source_excitation.create_port(positive_terminal=pin1, negative_terminal=pin2, port_type="Wave")
216
202
 
217
203
  # Create lumped port
218
- >>> lumped_port = edb.source_excitation.create_port(
219
- >>> positive_terminal=via_terminal,
220
- >>> port_type="Lumped"
221
- >>> )
204
+ >>> lumped_port = edb.source_excitation.create_port(positive_terminal=via_terminal, port_type="Lumped")
222
205
 
223
206
  8. Component Management
224
207
 
@@ -231,12 +214,7 @@ def Edb(
231
214
  9. Parametrization
232
215
 
233
216
  # Auto-parametrize design elements
234
- >>> params = edb.auto_parametrize_design(
235
- >>> traces=True,
236
- >>> pads=True,
237
- >>> antipads=True,
238
- >>> use_relative_variables=True
239
- >>> )
217
+ >>> params = edb.auto_parametrize_design(traces=True, pads=True, antipads=True, use_relative_variables=True)
240
218
  >>> print("Created parameters:", params)
241
219
 
242
220
  10. Design Statistics
@@ -255,11 +233,7 @@ def Edb(
255
233
  12. Differential Pairs
256
234
 
257
235
  # Create differential pair
258
- >>> edb.differential_pairs.create(
259
- >>> positive_net="USB_P",
260
- >>> negative_net="USB_N",
261
- >>> name="USB_DP"
262
- >>> )
236
+ >>> edb.differential_pairs.create(positive_net="USB_P", negative_net="USB_N", name="USB_DP")
263
237
 
264
238
  13. Workflow Automation
265
239
 
@@ -3,6 +3,8 @@ import secrets
3
3
  import shutil
4
4
  import string
5
5
 
6
+ from pyedb.generic.settings import settings
7
+
6
8
 
7
9
  def search_files(dirname, pattern="*"):
8
10
  """Search for files inside a directory given a specific pattern.
@@ -65,8 +67,8 @@ class Scratch:
65
67
  try:
66
68
  # TODO check why on Anaconda 3.7 get errors with os.path.exists
67
69
  shutil.rmtree(self._scratch_path, ignore_errors=True)
68
- except:
69
- pass
70
+ except Exception:
71
+ settings.logger.error(f"An error occurred while removing {self._scratch_path}")
70
72
 
71
73
  def copyfile(self, src_file, dst_filename=None):
72
74
  """
@@ -50,13 +50,9 @@ is_linux = os.name == "posix"
50
50
  is_windows = not is_linux
51
51
  _pythonver = sys.version_info[0]
52
52
 
53
+ import xml.etree.cElementTree as ET # nosec B405
53
54
 
54
- try:
55
- import xml.etree.cElementTree as ET
56
-
57
- ET.VERSION
58
- except ImportError:
59
- ET = None
55
+ ET.VERSION
60
56
 
61
57
 
62
58
  class GrpcApiError(Exception):
@@ -118,7 +114,6 @@ def _exception(ex_info, func, args, kwargs, message="Type Error"):
118
114
  if message_to_print:
119
115
  _write_mes("Last Electronics Desktop Message - " + message_to_print)
120
116
 
121
- args_name = []
122
117
  try:
123
118
  args_dict = _get_args_dicts(func, args, kwargs)
124
119
  first_time_log = True
@@ -129,13 +124,13 @@ def _exception(ex_info, func, args, kwargs, message="Type Error"):
129
124
  _write_mes("Method arguments: ")
130
125
  first_time_log = False
131
126
  _write_mes(" {} = {} ".format(el, args_dict[el]))
132
- except:
133
- pass
134
- args = [func.__name__] + [i for i in args_name if i not in ["self"]]
127
+ except Exception:
128
+ settings.logger.error(f"An error occurred while parsing and logging an error with method {func.__name__}.")
129
+
135
130
  if not func.__name__.startswith("_"):
136
131
  _write_mes(
137
132
  "Check Online documentation on: https://edb.docs.pyansys.com/version/stable/search.html?q={}".format(
138
- "+".join(args)
133
+ func.__name__
139
134
  )
140
135
  )
141
136
 
@@ -695,13 +690,9 @@ def read_csv_pandas(filename, encoding="utf-8"): # pragma: no cover
695
690
  :class:`pandas.DataFrame`
696
691
 
697
692
  """
698
- try:
699
- import pandas as pd
693
+ import pandas as pd
700
694
 
701
- return pd.read_csv(filename, encoding=encoding, header=0, na_values=".")
702
- except ImportError:
703
- logging.error("Pandas is not available. Install it.")
704
- return None
695
+ return pd.read_csv(filename, encoding=encoding, header=0, na_values=".")
705
696
 
706
697
 
707
698
  def read_tab(filename): # pragma: no cover
@@ -735,14 +726,10 @@ def read_xlsx(filename): # pragma: no cover
735
726
  list
736
727
 
737
728
  """
738
- try:
739
- import pandas as pd
729
+ import pandas as pd
740
730
 
741
- lines = pd.read_excel(filename)
742
- return lines
743
- except ImportError:
744
- lines = []
745
- return lines
731
+ lines = pd.read_excel(filename)
732
+ return lines
746
733
 
747
734
 
748
735
  def write_csv(output, list_data, delimiter=",", quotechar="|", quoting=csv.QUOTE_MINIMAL): # pragma: no cover
@@ -872,11 +859,7 @@ def compute_fft(time_vals, value): # pragma: no cover
872
859
  tuple
873
860
  Frequency and Values.
874
861
  """
875
- try:
876
- import numpy as np
877
- except ImportError:
878
- logging.error("NumPy is not available. Install it.")
879
- return False
862
+ import numpy as np
880
863
 
881
864
  deltaT = time_vals[-1] - time_vals[0]
882
865
  num_points = len(time_vals)
@@ -925,11 +908,8 @@ def parse_excitation_file(
925
908
  tuple
926
909
  Frequency, magnitude and phase.
927
910
  """
928
- try:
929
- import numpy as np
930
- except ImportError:
931
- logging.error("NumPy is not available. Install it.")
932
- return False
911
+ import numpy as np
912
+
933
913
  df = read_csv_pandas(file_name, encoding=encoding)
934
914
  if is_time_domain:
935
915
  time = df[df.keys()[0]].values * x_scale
@@ -1166,6 +1146,11 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
1166
1146
  """Install a new package using pip.
1167
1147
  This method is useful for installing a package from the AEDT Console without launching the Python environment.
1168
1148
 
1149
+ .. warning::
1150
+ Do not execute this function with untrusted function argument, environment
1151
+ variables or pyedb global settings.
1152
+ See the :ref:`security guide<ref_security_consideration>` for details.
1153
+
1169
1154
  Parameters
1170
1155
  ----------
1171
1156
  package_name : str
@@ -1178,10 +1163,12 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
1178
1163
  Whether to install the package or uninstall the package.
1179
1164
  """
1180
1165
 
1181
- import subprocess
1166
+ import subprocess # nosec B404
1182
1167
 
1183
- executable = '"{}"'.format(sys.executable) if is_windows else sys.executable
1168
+ if not package_name or not isinstance(package_name, str):
1169
+ raise ValueError("A valid package name must be provided.")
1184
1170
 
1171
+ executable = sys.executable
1185
1172
  commands = []
1186
1173
  if uninstall:
1187
1174
  commands.append([executable, "-m", "pip", "uninstall", "--yes", package_name])
@@ -1195,12 +1182,12 @@ def install_with_pip(package_name, package_path=None, upgrade=False, uninstall=F
1195
1182
  command.append("-U")
1196
1183
 
1197
1184
  commands.append(command)
1185
+
1198
1186
  for command in commands:
1199
- if is_linux:
1200
- p = subprocess.Popen(command)
1201
- else:
1202
- p = subprocess.Popen(" ".join(command))
1203
- p.wait()
1187
+ try:
1188
+ subprocess.run(command, check=True) # nosec
1189
+ except subprocess.CalledProcessError as e: # nosec
1190
+ raise RuntimeError("An error occurred while installing with pip") from e
1204
1191
 
1205
1192
 
1206
1193
  class Help: # pragma: no cover
pyedb/generic/plot.py CHANGED
@@ -1,31 +1,16 @@
1
1
  import os
2
- import warnings
3
2
 
4
- try:
5
- import numpy # noqa: F401
6
- except ImportError:
7
- warnings.warn(
8
- "The NumPy module is required to run some functionalities of PostProcess.\n"
9
- "Install with \n\npip install numpy\n\nRequires CPython."
10
- )
11
- try:
12
- from matplotlib.patches import PathPatch
13
- from matplotlib.path import Path
3
+ from matplotlib.patches import PathPatch
4
+ from matplotlib.path import Path
5
+ import numpy # noqa: F401
14
6
 
15
- # Use matplotlib agg backend (non-interactive) when the CI is running.
16
- if bool(int(os.getenv("PYEDB_CI_NO_DISPLAY", "0"))): # pragma: no cover
17
- import matplotlib
7
+ # Use matplotlib agg backend (non-interactive) when the CI is running.
8
+ if bool(int(os.getenv("PYEDB_CI_NO_DISPLAY", "0"))): # pragma: no cover
9
+ import matplotlib
18
10
 
19
- matplotlib.use("Agg")
20
- import matplotlib.pyplot as plt
11
+ matplotlib.use("Agg")
21
12
 
22
- except ImportError:
23
- warnings.warn(
24
- "The Matplotlib module is required to run some functionalities of PostProcess.\n"
25
- "Install with \n\npip install matplotlib\n\nRequires CPython."
26
- )
27
- except:
28
- pass
13
+ import matplotlib.pyplot as plt
29
14
 
30
15
 
31
16
  def plot_matplotlib(