najaeda 0.1.2__cp313-cp313t-macosx_11_0_arm64.whl → 0.1.3__cp313-cp313t-macosx_11_0_arm64.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 najaeda might be problematic. Click here for more details.

Binary file
najaeda/netlist.py CHANGED
@@ -4,6 +4,8 @@
4
4
  # SPDX-License-Identifier: Apache-2.0
5
5
 
6
6
  import itertools
7
+ import logging
8
+
7
9
  from najaeda import snl
8
10
 
9
11
 
@@ -46,102 +48,121 @@ class Equipotential:
46
48
 
47
49
 
48
50
  class Net:
49
- def __init__(self, path, net):
51
+ def __init__(self, path, net=None, net_concat=None):
52
+ if net is not None and net_concat is not None:
53
+ raise ValueError(
54
+ "Only one of `net` or `net_concat` should be provided, not both."
55
+ )
50
56
  self.path = path
51
- self.net = net
52
-
53
- def __eq__(self, value):
54
- return self.net == value.net and self.path == value.path
55
-
56
- def __ne__(self, value):
57
- return not self == value
58
-
59
- def __lt__(self, value):
60
- if self.path != value.path:
61
- return self.path < value.path
62
- return self.net < value.net
63
-
64
- def __le__(self, value):
65
- if self.path != value.path:
66
- return self.path < value.path
67
- return self.net <= value.net
57
+ if net is not None:
58
+ self.net = net
59
+ elif net_concat is not None:
60
+ self.net_concat = net_concat
68
61
 
69
- def __gt__(self, value):
70
- if self.path != value.path:
71
- return self.path > value.path
72
- return self.net > value.net
62
+ def __eq__(self, other):
63
+ if not isinstance(other, Net):
64
+ return NotImplemented
65
+ return vars(self) == vars(other)
73
66
 
74
- def __ge__(self, value):
75
- if self.path != value.path:
76
- return self.path > value.path
77
- return self.net >= value.net
67
+ def __ne__(self, other):
68
+ eq_result = self.__eq__(other)
69
+ if eq_result is NotImplemented:
70
+ return NotImplemented
71
+ return not eq_result
78
72
 
79
73
  def __str__(self):
74
+ if hasattr(self, "net"):
75
+ net_str = str(self.net)
76
+ elif hasattr(self, "net_concat"):
77
+ net_str = "{" + ",".join(map(str, self.net_concat)) + "}"
80
78
  if self.path.size() > 0:
81
- return f"{self.path}/{self.net}"
82
- return f"{self.net}"
83
-
84
- def __repr__(self):
85
- return f"Net({self.path}, {self.net})"
79
+ return f"{self.path}/{net_str}"
80
+ return net_str
86
81
 
87
82
  def get_name(self) -> str:
88
83
  """Return the name of the net."""
89
- return self.net.getName()
84
+ if hasattr(self, "net"):
85
+ return self.net.getName()
86
+ return "{" + ",".join(map(str, self.net_concat)) + "}"
90
87
 
91
88
  def get_msb(self) -> int:
92
89
  """Return the most significant bit of the net if it is a bus."""
93
- if isinstance(self.net, snl.SNLBusNet):
90
+ if hasattr(self, "net") and isinstance(self.net, snl.SNLBusNet):
94
91
  return self.net.getMSB()
95
92
  return None
96
93
 
97
94
  def get_lsb(self) -> int:
98
95
  """Return the least significant bit of the net if it is a bus."""
99
- if isinstance(self.net, snl.SNLBusNet):
96
+ if hasattr(self, "net") and isinstance(self.net, snl.SNLBusNet):
100
97
  return self.net.getLSB()
101
98
  return None
102
99
 
103
100
  def is_bus(self) -> bool:
104
101
  """Return True if the net is a bus."""
105
- return isinstance(self.net, snl.SNLBusNet)
102
+ return hasattr(self, "net") and isinstance(self.net, snl.SNLBusNet)
106
103
 
107
104
  def is_bus_bit(self) -> bool:
108
105
  """Return True if the net is a bit of a bus."""
109
- return isinstance(self.net, snl.SNLBusNetBit)
106
+ return hasattr(self, "net") and isinstance(self.net, snl.SNLBusNetBit)
110
107
 
111
108
  def is_scalar(self) -> bool:
112
109
  """Return True if the net is a scalar."""
113
- return isinstance(self.net, snl.SNLScalarNet)
110
+ return hasattr(self, "net") and isinstance(self.net, snl.SNLScalarNet)
114
111
 
115
112
  def is_bit(self) -> bool:
116
113
  """Return True if the net is a bit."""
117
114
  return self.is_scalar() or self.is_bus_bit()
118
115
 
119
- def is_constant(self) -> bool:
116
+ def is_concat(self) -> bool:
117
+ """Return True if the net is a concatenation."""
118
+ return hasattr(self, "net_concat")
119
+
120
+ def is_const(self) -> bool:
120
121
  """Return True if the net is a constant generator."""
121
- return self.net.isConstant()
122
+ if hasattr(self, "net"):
123
+ return self.net.isConstant()
124
+ for net in self.net_concat:
125
+ if not net.isConstant():
126
+ return False
127
+ return True
122
128
 
123
129
  def get_width(self) -> int:
124
130
  """Return the width of the net."""
125
- return self.net.getWidth()
131
+ if hasattr(self, "net"):
132
+ return self.net.getWidth()
133
+ return sum(1 for _ in self.net_concat)
126
134
 
127
135
  def get_bits(self):
128
- if isinstance(self.net, snl.SNLBusNet):
129
- for bit in self.net.getBits():
130
- yield Net(self.path, bit)
136
+ if hasattr(self, "net"):
137
+ if isinstance(self.net, snl.SNLBusNet):
138
+ for bit in self.net.getBits():
139
+ yield Net(self.path, bit)
140
+ else:
141
+ yield self
131
142
  else:
132
- yield self
143
+ for net in self.net_concat:
144
+ yield net
133
145
 
134
146
  def get_bit(self, index: int):
135
- if isinstance(self.net, snl.SNLBusNet):
136
- return Net(self.path, self.net.getBit(index))
147
+ if hasattr(self, "net"):
148
+ if isinstance(self.net, snl.SNLBusNet):
149
+ return Net(self.path, self.net.getBit(index))
150
+ else:
151
+ return None
152
+ if 0 <= index < len(self.net_concat):
153
+ return Net(self.path, self.net_concat[index])
137
154
  return None
138
155
 
139
156
  def get_inst_terms(self):
157
+ if hasattr(self, "net_concat"):
158
+ raise ValueError("Cannot get inst terms from a net_concat")
140
159
  for term in self.net.getInstTerms():
141
160
  path = snl.SNLPath(self.path, term.getInstance())
142
161
  yield Term(path, term.getBitTerm())
143
162
 
144
163
  def get_terms(self):
164
+ if hasattr(self, "net_concat"):
165
+ raise ValueError("Cannot get terms from a net_concat")
145
166
  for term in self.net.getBitTerms():
146
167
  yield Term(self.path, term)
147
168
 
@@ -151,9 +172,9 @@ class Net:
151
172
 
152
173
 
153
174
  class Term:
154
- Input = snl.SNLTerm.Direction.Input
155
- Output = snl.SNLTerm.Direction.Output
156
- InOut = snl.SNLTerm.Direction.InOut
175
+ INPUT = snl.SNLTerm.Direction.Input
176
+ OUTPUT = snl.SNLTerm.Direction.Output
177
+ INOUT = snl.SNLTerm.Direction.InOut
157
178
 
158
179
  def __init__(self, path, term):
159
180
  self.path = path
@@ -179,15 +200,19 @@ class Term:
179
200
  def __ge__(self, other) -> bool:
180
201
  return not self < other
181
202
 
182
- def __str__(self) -> str:
183
- return str(self.path) + "." + self.term.getName()
203
+ def __str__(self):
204
+ if self.path.size() == 0:
205
+ return self.term.getName()
206
+ else:
207
+ return f"{self.path}/{self.term}"
184
208
 
185
209
  def __repr__(self) -> str:
186
210
  return f"Term({self.path}, {self.term})"
187
211
 
188
212
  def __make_unique(self):
189
- if self.path.size() > 0:
190
- snl.SNLUniquifier(self.path)
213
+ if self.path.size() > 1:
214
+ path = self.path.getHeadPath()
215
+ snl.SNLUniquifier(path)
191
216
  if self.is_bus_bit():
192
217
  term = (
193
218
  self.path.getTailInstance().getModel().getTerm(self.term.getName())
@@ -237,24 +262,74 @@ class Term:
237
262
  def get_direction(self) -> snl.SNLTerm.Direction:
238
263
  """Return the direction of the term."""
239
264
  if self.term.getDirection() == snl.SNLTerm.Direction.Input:
240
- return Term.Input
265
+ return Term.INPUT
241
266
  elif self.term.getDirection() == snl.SNLTerm.Direction.Output:
242
- return Term.Output
267
+ return Term.OUTPUT
243
268
  elif self.term.getDirection() == snl.SNLTerm.Direction.InOut:
244
- return Term.InOut
269
+ return Term.INOUT
245
270
 
246
- def get_net(self) -> Net:
247
- if isinstance(self.term, snl.SNLBusTerm):
248
- return None # FIXME xtof in the future
249
- net = None
271
+ def __get_snl_bitnet(self, bit) -> Net:
272
+ # single bit
250
273
  if self.path.size() > 0:
251
- instTerm = self.path.getTailInstance().getInstTerm(self.term)
252
- net = instTerm.getNet()
274
+ instTerm = self.path.getTailInstance().getInstTerm(bit)
275
+ return instTerm.getNet()
276
+ else:
277
+ return bit.getNet()
278
+
279
+ def __get_snl_lower_bitnet(self, bit) -> Net:
280
+ return bit.getNet()
281
+
282
+ def __get_snl_busnet(self, snl_nets) -> snl.SNLBusNet:
283
+ # iterate on all elements of the list and check if
284
+ # a full SNLBusNet can be reconstructed
285
+ snl_bus_net = None
286
+ for i in range(len(snl_nets)):
287
+ snl_net = snl_nets[i]
288
+ if not isinstance(snl_net, snl.SNLBusNetBit):
289
+ return None
290
+ bit_bus = snl_net.getBus()
291
+ if bit_bus.getWidth() != len(snl_nets):
292
+ return None
293
+ if snl_bus_net is None:
294
+ snl_bus_net = bit_bus
295
+ if snl_bus_net != bit_bus:
296
+ return None
297
+ if snl_bus_net.getBitAtPosition(i) != snl_net:
298
+ return None
299
+ return snl_bus_net
300
+
301
+ def __get_net(self, path, snl_term_net_accessor) -> Net:
302
+ if isinstance(self.term, snl.SNLBusTerm):
303
+ snl_nets = []
304
+ for bit in self.term.getBits():
305
+ snl_net = snl_term_net_accessor(bit)
306
+ snl_nets.append(snl_net)
307
+ snl_bus_net = self.__get_snl_busnet(snl_nets)
308
+ if snl_bus_net is not None:
309
+ return Net(path, snl_bus_net)
310
+ else:
311
+ if all(element is not None for element in snl_nets):
312
+ return Net(path, net_concat=snl_nets)
253
313
  else:
254
- net = get_top().model.getTerm(self.term.getName()).getNet()
255
- return Net(self.path, net)
314
+ snl_net = snl_term_net_accessor(self.term)
315
+ if snl_net is not None:
316
+ return Net(path, snl_net)
317
+ return None
318
+
319
+ def get_lower_net(self) -> Net:
320
+ """Return the lower net of the term."""
321
+ return self.__get_net(self.path, self.__get_snl_lower_bitnet)
322
+
323
+ def get_net(self) -> Net:
324
+ """Return the net of the term."""
325
+ if self.path.empty():
326
+ return None
327
+ # path is one level up
328
+ path = self.path.getHeadPath()
329
+ return self.__get_net(path, self.__get_snl_bitnet)
256
330
 
257
331
  def get_instance(self):
332
+ """Return the instance of the term."""
258
333
  return Instance(self.path)
259
334
 
260
335
  def get_flat_fanout(self):
@@ -295,6 +370,7 @@ class Term:
295
370
  raise ValueError("Width mismatch")
296
371
  if self.get_instance().is_top():
297
372
  for bterm, bnet in zip(self.term.getBits(), net.net.getBits()):
373
+ logging.debug(f"Connecting {bterm} to {bnet}")
298
374
  bterm.setNet(bnet)
299
375
  else:
300
376
  self.__make_unique()
@@ -348,8 +424,15 @@ class Instance:
348
424
  def __eq__(self, other) -> bool:
349
425
  return self.path == other.path
350
426
 
351
- def __str__(self) -> str:
352
- return str(self.path)
427
+ def __str__(self):
428
+ if self.is_top():
429
+ top = self.__get_snl_model()
430
+ if top is not None:
431
+ return top.getName()
432
+ else:
433
+ return ""
434
+ else:
435
+ return str(self.path)
353
436
 
354
437
  def __repr__(self) -> str:
355
438
  return f"Instance({self.path})"
@@ -396,8 +479,6 @@ class Instance:
396
479
 
397
480
  def __find_snl_model(self, name: str) -> snl.SNLDesign:
398
481
  u = snl.SNLUniverse.get()
399
- if u is None:
400
- return None
401
482
  for db in u.getUserDBs():
402
483
  for lib in db.getLibraries():
403
484
  found_model = lib.getDesign(name)
@@ -419,15 +500,21 @@ class Instance:
419
500
  def get_number_of_child_instances(self) -> int:
420
501
  return sum(1 for _ in self.__get_snl_model().getInstances())
421
502
 
422
- def get_terms(self):
423
- for term in self.__get_snl_model().getTerms():
424
- yield Term(self.path, term)
425
-
426
- def get_term(self, name: str) -> Term:
427
- term = self.__get_snl_model().getTerm(name)
428
- if term is not None:
429
- return Term(self.path, self.__get_snl_model().getTerm(name))
430
- return None
503
+ # def get_flat_primitive_instances(self):
504
+ # FIXME: concat first local path with the path of the instance
505
+ # model = self.__get_snl_model()
506
+ # for inst in model.getInstances():
507
+ # path = snl.SNLPath(inst)
508
+ # stack = [[inst, path]]
509
+ # while stack:
510
+ # current = stack.pop()
511
+ # current_inst = current[0]
512
+ # current_path = current[1]
513
+ # for inst_child in current_inst.getModel().getInstances():
514
+ # path_child = snl.SNLPath(current_path, inst_child)
515
+ # if inst_child.getModel().isPrimitive():
516
+ # yield Instance(path_child)
517
+ # stack.append([inst_child, path_child])
431
518
 
432
519
  def get_nets(self):
433
520
  for net in self.__get_snl_model().getNets():
@@ -448,8 +535,23 @@ class Instance:
448
535
  return None
449
536
 
450
537
  def is_primitive(self) -> bool:
538
+ """Return True if this is a primitive."""
451
539
  return self.__get_snl_model().isPrimitive()
452
540
 
541
+ def get_terms(self):
542
+ for term in self.__get_snl_model().getTerms():
543
+ yield Term(self.path, term)
544
+
545
+ def get_flat_terms(self):
546
+ for term in self.__get_snl_model().getBitTerms():
547
+ yield Term(self.path, term)
548
+
549
+ def get_term(self, name: str) -> Term:
550
+ term = self.__get_snl_model().getTerm(name)
551
+ if term is not None:
552
+ return Term(self.path, self.__get_snl_model().getTerm(name))
553
+ return None
554
+
453
555
  def get_input_terms(self):
454
556
  for term in self.__get_snl_model().getTerms():
455
557
  if term.getDirection() == snl.SNLTerm.Direction.Input:
@@ -531,9 +633,10 @@ class Instance:
531
633
  def create_input_term(self, name: str) -> Term:
532
634
  return self.create_term(name, snl.SNLTerm.Direction.Input)
533
635
 
534
- def create_bus_term(
535
- self, name: str, msb: int, lsb: int, direction: snl.SNLTerm.Direction
536
- ) -> Term:
636
+ def create_inout_term(self, name: str) -> Term:
637
+ return self.create_term(name, snl.SNLTerm.Direction.InOut)
638
+
639
+ def create_bus_term(self, name: str, msb: int, lsb: int, direction) -> Term:
537
640
  if self.path.size() > 0:
538
641
  path = self.path
539
642
  snl.SNLUniquifier(path)
@@ -542,6 +645,9 @@ class Instance:
542
645
  newSNLTerm = snl.SNLBusTerm.create(design, direction, msb, lsb, name)
543
646
  return Term(self.path, newSNLTerm)
544
647
 
648
+ def create_inout_bus_term(self, name: str, msb: int, lsb: int) -> Term:
649
+ return self.create_bus_term(name, msb, lsb, snl.SNLTerm.Direction.InOut)
650
+
545
651
  def create_output_bus_term(self, name: str, msb: int, lsb: int) -> Term:
546
652
  return self.create_bus_term(name, msb, lsb, snl.SNLTerm.Direction.Output)
547
653
 
@@ -621,20 +727,3 @@ def get_model_name(id: tuple[int, int, int]) -> str:
621
727
  if model:
622
728
  return model.getName()
623
729
  return None
624
-
625
-
626
- def get_all_primitive_instances():
627
- top = snl.SNLUniverse.get().getTopDesign()
628
-
629
- for inst in top.getInstances():
630
- path = snl.SNLPath(inst)
631
- stack = [[inst, path]]
632
- while stack:
633
- current = stack.pop()
634
- current_inst = current[0]
635
- current_path = current[1]
636
- for inst_child in current_inst.getModel().getInstances():
637
- path_child = snl.SNLPath(current_path, inst_child)
638
- if inst_child.getModel().isPrimitive():
639
- yield Instance(path_child)
640
- stack.append([inst_child, path_child])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: najaeda
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Naja EDA Python package
5
5
  Author-Email: Naja Authors <contact@keplertech.io>
6
6
  License: Apache License 2.0
@@ -11,15 +11,8 @@ Description-Content-Type: text/x-rst
11
11
  Naja EDA Python Package
12
12
  =======================
13
13
 
14
- Naja EDA is a Python package that provides data structures and APIs for developing post-synthesis Electronic Design Automation (EDA) algorithms. It serves as the Python counterpart to the `Naja C++ project <https://github.com/najaeda/naja>`_.
15
-
16
- Features
17
- --------
18
-
19
- - **Netlist Simplification**: Perform constant propagation and dead logic elimination.
20
- - **Logic Replication**: Facilitate duplication of logic elements for optimization.
21
- - **Netlist Partitioning**: Divide netlists into manageable sections.
22
- - **Place and Route Support**: Assist in ASIC and FPGA design flows.
14
+ Naja EDA is a Python package that provides data structures and APIs for developing post-synthesis Electronic Design Automation (EDA) algorithms.
15
+ It serves as the Python counterpart to the `Naja C++ project <https://github.com/najaeda/naja>`_.
23
16
 
24
17
  Installation
25
18
  ------------
@@ -32,24 +25,35 @@ Install Naja EDA using pip:
32
25
 
33
26
  Examples
34
27
  --------
28
+
29
+ Load a design from a liberty file and a Verilog file
30
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35
31
  Following snippet shows how to load primitive cells from a liberty file and
36
32
  a netlist from a Verilog file.
33
+
37
34
  .. code-block:: python
38
35
 
39
36
  from os import path
37
+ import sys
40
38
  from najaeda import netlist
41
39
 
42
40
  benchmarks = path.join('..','benchmarks')
43
- liberty_files = ['NangateOpenCellLibrary_typical.lib', 'fakeram45_1024x32.lib', 'fakeram45_64x32.lib']
41
+ liberty_files = [
42
+ 'NangateOpenCellLibrary_typical.lib',
43
+ 'fakeram45_1024x32.lib',
44
+ 'fakeram45_64x32.lib'
45
+ ]
44
46
  liberty_files = list(map(lambda p:path.join(benchmarks, 'liberty', p), liberty_files))
45
47
 
46
48
  netlist.load_liberty(liberty_files)
47
49
  top = netlist.load_verilog([path.join(benchmarks, 'verilog', 'tinyrocket.v')])
48
50
 
49
- #dump verilog
50
- top.dump_verilog('tinyrocket_naja.v')
51
+ top.dump_verilog('.', 'tinyrocket_naja.v')
51
52
 
53
+ Print all the instances in the netlist
54
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52
55
  Next example shows how to browse all the netlist and print all its content.
56
+
53
57
  .. code-block:: python
54
58
 
55
59
  def print_netlist(instance):
@@ -60,7 +64,7 @@ Next example shows how to browse all the netlist and print all its content.
60
64
  Documentation
61
65
  -------------
62
66
 
63
- Comprehensive documentation is available on the `Naja GitHub repository <https://github.com/najaeda/naja>`_.
67
+ Naja documentation is available on the `Naja GitHub repository <https://github.com/najaeda/naja>`_.
64
68
 
65
69
  License
66
70
  -------
@@ -0,0 +1,11 @@
1
+ najaeda-0.1.3.dist-info/RECORD,,
2
+ najaeda-0.1.3.dist-info/WHEEL,sha256=WOiMFvo9bzNKIh5Rxx7xDwR4DzXJ4VAFWxSvvQdE_uA,115
3
+ najaeda-0.1.3.dist-info/METADATA,sha256=cUsTBMA9mhbFVF1WSI0wQYb3hc9-ThpBYShih2Yru6E,2147
4
+ najaeda-0.1.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
5
+ najaeda-0.1.3.dist-info/licenses/AUTHORS,sha256=e4DCPGHo6wKMyQz24V59EP7zMwalK-KBDBHHlMLFxHY,330
6
+ najaeda/netlist.py,sha256=sKIacJE8olG3-NxUru0ntAnzeU-enJNocnvJ3O0Jnp0,25102
7
+ najaeda/libnaja_snl_python.dylib,sha256=NufBTjIC-14Ailajr63ycpoHng0PONuZY3y26c9aehw,740272
8
+ najaeda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ najaeda/libnaja_snl.dylib,sha256=8Ei7KKU0b14IqlvH_8yQSSCpH0rjbxpeRaXgyfSemMY,574112
10
+ najaeda/snl.so,sha256=Pm6IrWbo5sJ7VbokbTf36Q_tVlttrtXkNT4A_b4X1Pg,97888
11
+ najaeda/.dylibs/PythonT,sha256=PKxrOOyA9Wrc11wkOBZVdQachBkACXp9879F1jMycxc,13116640
@@ -1,11 +0,0 @@
1
- najaeda-0.1.2.dist-info/RECORD,,
2
- najaeda-0.1.2.dist-info/WHEEL,sha256=WOiMFvo9bzNKIh5Rxx7xDwR4DzXJ4VAFWxSvvQdE_uA,115
3
- najaeda-0.1.2.dist-info/METADATA,sha256=agjVbL5DID69VNnjngj23q1t-TEOHBaCUhMnhIGEjZI,2267
4
- najaeda-0.1.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
5
- najaeda-0.1.2.dist-info/licenses/AUTHORS,sha256=e4DCPGHo6wKMyQz24V59EP7zMwalK-KBDBHHlMLFxHY,330
6
- najaeda/netlist.py,sha256=usJ__3Vh_A8GFK7xUdhwJSdsTc7DWhaIF3Ptp3dfuZU,21251
7
- najaeda/libnaja_snl_python.dylib,sha256=X7DlbBDkBvAT2MFrb47L27aU6SuNF8lr0YTAGLW4pAw,739984
8
- najaeda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- najaeda/libnaja_snl.dylib,sha256=8Ei7KKU0b14IqlvH_8yQSSCpH0rjbxpeRaXgyfSemMY,574112
10
- najaeda/snl.so,sha256=Pm6IrWbo5sJ7VbokbTf36Q_tVlttrtXkNT4A_b4X1Pg,97888
11
- najaeda/.dylibs/PythonT,sha256=PKxrOOyA9Wrc11wkOBZVdQachBkACXp9879F1jMycxc,13116640