PySimultan 0.5.3__py3-none-any.whl → 0.5.4__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.
- PySimultan2/__about__.py +1 -1
- PySimultan2/data_model.py +9 -4
- PySimultan2/files.py +143 -3
- PySimultan2/type_setter_lookup.py +3 -2
- PySimultan2/utils.py +54 -7
- {pysimultan-0.5.3.dist-info → pysimultan-0.5.4.dist-info}/METADATA +1 -1
- {pysimultan-0.5.3.dist-info → pysimultan-0.5.4.dist-info}/RECORD +9 -9
- {pysimultan-0.5.3.dist-info → pysimultan-0.5.4.dist-info}/WHEEL +0 -0
- {pysimultan-0.5.3.dist-info → pysimultan-0.5.4.dist-info}/licenses/LICENSE.txt +0 -0
PySimultan2/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
version = '0.5.
|
1
|
+
version = '0.5.4'
|
PySimultan2/data_model.py
CHANGED
@@ -43,7 +43,7 @@ from System.Security import *
|
|
43
43
|
from System.Security.Cryptography import *
|
44
44
|
from System.Text import *
|
45
45
|
|
46
|
-
from .files import add_tag_to_resource, FileInfo as PythonFileInfo
|
46
|
+
from .files import add_tag_to_resource, FileInfo as PythonFileInfo, DirectoryInfo as PythonDirectoryInfo
|
47
47
|
|
48
48
|
|
49
49
|
if TYPE_CHECKING:
|
@@ -165,6 +165,11 @@ class DataModel:
|
|
165
165
|
def assets(self):
|
166
166
|
return self.project_data_manager.AssetManager.Resources
|
167
167
|
|
168
|
+
@property
|
169
|
+
def file_directories(self):
|
170
|
+
return [PythonDirectoryInfo(resource_entry=x,
|
171
|
+
data_model=self) for x in self.project_data_manager.AssetManager.Resources if isinstance(x, ResourceDirectoryEntry)]
|
172
|
+
|
168
173
|
@property
|
169
174
|
def models(self) -> dict[int, 'GeometryModel']:
|
170
175
|
"""
|
@@ -503,7 +508,7 @@ class DataModel:
|
|
503
508
|
|
504
509
|
def add_empty_resource(self,
|
505
510
|
filename: str,
|
506
|
-
target_dir: Union[ResourceDirectoryEntry,
|
511
|
+
target_dir: Union[ResourceDirectoryEntry, FileInfo, str] = None) -> ResourceEntry:
|
507
512
|
"""
|
508
513
|
Add an empty resource to the project
|
509
514
|
:param filename: name of the new resource
|
@@ -519,7 +524,7 @@ class DataModel:
|
|
519
524
|
|
520
525
|
if isinstance(target_dir, ResourceDirectoryEntry):
|
521
526
|
target_dir = target_dir.CurrentFullPath
|
522
|
-
if isinstance(target_dir,
|
527
|
+
if isinstance(target_dir, FileInfo):
|
523
528
|
target_dir = target_dir.FullPath
|
524
529
|
|
525
530
|
return self.project.AddEmptyResource(FileInfo(
|
@@ -571,7 +576,7 @@ class DataModel:
|
|
571
576
|
target_dir = DirectoryInfo(target_dir)
|
572
577
|
elif isinstance(target_dir, ResourceDirectoryEntry):
|
573
578
|
target_dir = DirectoryInfo(target_dir.CurrentFullPath)
|
574
|
-
elif isinstance(target_dir,
|
579
|
+
elif isinstance(target_dir, FileInfo):
|
575
580
|
pass
|
576
581
|
|
577
582
|
resource = self.project.CopyResourceAsContainedFileEntry(filename,
|
PySimultan2/files.py
CHANGED
@@ -69,7 +69,7 @@ def add_tag_to_resource(resource: Union[ResourceFileEntry, ContainedResourceFile
|
|
69
69
|
|
70
70
|
|
71
71
|
def add_asset_to_component(comp: [SimComponent, SimultanObject],
|
72
|
-
asset: Union[ResourceFileEntry, ContainedResourceFileEntry],
|
72
|
+
asset: Union[ResourceFileEntry, ContainedResourceFileEntry, ResourceDirectoryEntry],
|
73
73
|
content_id: str = '',
|
74
74
|
tag: SimTaxonomyEntry = None) -> Asset:
|
75
75
|
"""
|
@@ -127,8 +127,14 @@ def create_asset_from_string(filename: str,
|
|
127
127
|
with open(filepath, 'w') as f:
|
128
128
|
f.write(content)
|
129
129
|
|
130
|
-
|
131
|
-
|
130
|
+
if target_dir is not None:
|
131
|
+
if isinstance(target_dir, DirectoryInfo):
|
132
|
+
target_dir = target_dir.current_full_path
|
133
|
+
|
134
|
+
resource = data_model.add_resource(filepath,
|
135
|
+
target_dir=target_dir)
|
136
|
+
else:
|
137
|
+
resource = data_model.add_resource(filepath)
|
132
138
|
|
133
139
|
if tag is not None:
|
134
140
|
add_tag_to_resource(resource,
|
@@ -225,6 +231,22 @@ class MetaMock(type):
|
|
225
231
|
return obj
|
226
232
|
|
227
233
|
|
234
|
+
class DirectoryInfoMetaMock(type):
|
235
|
+
|
236
|
+
def __call__(cls, *args, **kwargs):
|
237
|
+
resource_entry: Optional[ResourceDirectoryEntry] = kwargs.get('resource_entry', None)
|
238
|
+
if resource_entry is not None and hasattr(resource_entry, 'Key'):
|
239
|
+
obj = cls._cls_instances.get(resource_entry.Key, None)
|
240
|
+
if obj is not None:
|
241
|
+
return obj
|
242
|
+
|
243
|
+
obj = cls.__new__(cls)
|
244
|
+
obj.__init__(*args, **kwargs)
|
245
|
+
if obj.resource_entry is not None:
|
246
|
+
cls._cls_instances[obj.resource_entry.Key] = obj
|
247
|
+
return obj
|
248
|
+
|
249
|
+
|
228
250
|
class FileInfo(object, metaclass=MetaMock):
|
229
251
|
|
230
252
|
_cls_instances = {}
|
@@ -471,3 +493,121 @@ class FileInfo(object, metaclass=MetaMock):
|
|
471
493
|
"$key": str(self.key)
|
472
494
|
}
|
473
495
|
}
|
496
|
+
|
497
|
+
|
498
|
+
class DirectoryInfo(object, metaclass=DirectoryInfoMetaMock):
|
499
|
+
|
500
|
+
_cls_instances = {}
|
501
|
+
|
502
|
+
@classmethod
|
503
|
+
def get_by_key(cls, key: int) -> Optional[DirectoryInfo]:
|
504
|
+
return cls._cls_instances.get(key, None)
|
505
|
+
|
506
|
+
def __init__(self,
|
507
|
+
path: Optional[str] = None,
|
508
|
+
*args,
|
509
|
+
**kwargs):
|
510
|
+
|
511
|
+
self._resource_entry: Optional[ResourceDirectoryEntry] = None
|
512
|
+
self.data_model: Optional[DataModel] = kwargs.get('data_model', None)
|
513
|
+
self.path: str = path
|
514
|
+
|
515
|
+
self.resource_entry = kwargs.get('resource_entry', None)
|
516
|
+
|
517
|
+
@property
|
518
|
+
def tags(self) -> List[SimTaxonomyEntry]:
|
519
|
+
return list(self.resource_entry.Tags)
|
520
|
+
|
521
|
+
@property
|
522
|
+
def full_path(self) -> str:
|
523
|
+
return self.resource_entry.CurrentFullPath
|
524
|
+
|
525
|
+
@property
|
526
|
+
def relative_path(self) -> str:
|
527
|
+
return self.resource_entry.CurrentRelativePath
|
528
|
+
|
529
|
+
@property
|
530
|
+
def resource_entry(self) -> Optional[ResourceDirectoryEntry]:
|
531
|
+
if self._resource_entry is None:
|
532
|
+
if self.data_model is None:
|
533
|
+
logger.warning(
|
534
|
+
f'No data model provided. Using default data model: {config.get_default_data_model().id}.')
|
535
|
+
self.data_model = config.get_default_data_model()
|
536
|
+
if self.data_model is not None:
|
537
|
+
self.resource_entry = self.data_model.create_resource_directory(self.path)
|
538
|
+
self._cls_instances[self.resource_entry.Key] = self
|
539
|
+
self.path = self.resource_entry.CurrentFullPath
|
540
|
+
return self._resource_entry
|
541
|
+
|
542
|
+
@resource_entry.setter
|
543
|
+
def resource_entry(self, value):
|
544
|
+
|
545
|
+
orig_value = self._resource_entry
|
546
|
+
self._resource_entry = value
|
547
|
+
|
548
|
+
if self._resource_entry is None:
|
549
|
+
if orig_value is not None:
|
550
|
+
del self._cls_instances[orig_value.Key]
|
551
|
+
return
|
552
|
+
|
553
|
+
if self.key is not None:
|
554
|
+
if value is not None:
|
555
|
+
self._cls_instances[value.Key] = self
|
556
|
+
else:
|
557
|
+
del self._cls_instances[self._resource_entry.Key]
|
558
|
+
self._resource_entry = value
|
559
|
+
|
560
|
+
@property
|
561
|
+
def parent(self) -> Optional[ResourceDirectoryEntry]:
|
562
|
+
if self.resource_entry.Parent is not None:
|
563
|
+
if self.resource_entry.Parent.Key in self._cls_instances:
|
564
|
+
return self.get_by_key(self.resource_entry.Parent.Key)
|
565
|
+
return DirectoryInfo(resource_entry=self.resource_entry.Parent)
|
566
|
+
else:
|
567
|
+
return self.resource_entry.Parent
|
568
|
+
|
569
|
+
@property
|
570
|
+
def sub_directories(self) -> List[DirectoryInfo]:
|
571
|
+
return [DirectoryInfo(resource_entry=entry,
|
572
|
+
data_model=self.data_model) for entry in self.resource_entry.Children if isinstance(entry, ResourceDirectoryEntry)]
|
573
|
+
|
574
|
+
@property
|
575
|
+
def files(self) -> List[FileInfo]:
|
576
|
+
return [FileInfo(resource_entry=entry,
|
577
|
+
data_model=self.data_model) for entry in self.resource_entry.Children if isinstance(entry,
|
578
|
+
(
|
579
|
+
ResourceFileEntry,
|
580
|
+
ContainedResourceFileEntry)
|
581
|
+
)
|
582
|
+
]
|
583
|
+
|
584
|
+
@property
|
585
|
+
def key(self) -> Optional[int]:
|
586
|
+
if self.resource_entry is not None:
|
587
|
+
return self.resource_entry.Key
|
588
|
+
else:
|
589
|
+
return None
|
590
|
+
|
591
|
+
def add_sub_directory(self, name: str) -> DirectoryInfo:
|
592
|
+
return DirectoryInfo(path=os.path.join(self.resource_entry.current_relative_path, name),
|
593
|
+
data_model=self.data_model)
|
594
|
+
|
595
|
+
def add_file(self,
|
596
|
+
filename: str,
|
597
|
+
content: Optional[str] = None) -> FileInfo:
|
598
|
+
|
599
|
+
if content is not None:
|
600
|
+
return FileInfo.from_string(filename=filename,
|
601
|
+
content=content,
|
602
|
+
target_dir=self.resource_entry,
|
603
|
+
data_model=self.data_model)
|
604
|
+
else:
|
605
|
+
new_resource = self.data_model.add_empty_resource(filename=os.path.join(self.full_path, filename))
|
606
|
+
return FileInfo(resource_entry=new_resource,
|
607
|
+
data_model=self.data_model)
|
608
|
+
|
609
|
+
def add_tag(self, tag: SimTaxonomyEntry) -> None:
|
610
|
+
add_tag_to_resource(self.resource_entry, tag)
|
611
|
+
|
612
|
+
def __repr__(self):
|
613
|
+
return f'DirectoryInfo(key:{self.key}, hash: {hash(self)}; {self.full_path};)'
|
@@ -5,9 +5,9 @@ import inspect
|
|
5
5
|
import enum
|
6
6
|
|
7
7
|
from .utils import (SimComponent, SimDoubleParameter, SimIntegerParameter, SimStringParameter,
|
8
|
-
SimBoolParameter, SimEnumParameter, SimMultiValueField3D, SimMultiValueBigTable, FileInfo,
|
8
|
+
SimBoolParameter, SimEnumParameter, SimMultiValueField3D, SimMultiValueBigTable, FileInfo, DirectoryInfo,
|
9
9
|
set_property_to_sim_component, set_property_to_parameter, set_property_to_value_field,
|
10
|
-
set_property_to_file_info, set_property_to_list, set_property_to_dict)
|
10
|
+
set_property_to_file_info, set_property_to_list, set_property_to_dict, set_property_to_directory_info)
|
11
11
|
|
12
12
|
from .simultan_object import SimultanObject, MetaMock
|
13
13
|
|
@@ -36,6 +36,7 @@ class TypeSetterFcnLookupDict(object):
|
|
36
36
|
str: set_property_to_parameter,
|
37
37
|
bool: set_property_to_parameter,
|
38
38
|
FileInfo: set_property_to_file_info,
|
39
|
+
DirectoryInfo: set_property_to_directory_info,
|
39
40
|
list: set_property_to_list,
|
40
41
|
tuple: set_property_to_list,
|
41
42
|
set: set_property_to_list,
|
PySimultan2/utils.py
CHANGED
@@ -20,12 +20,13 @@ from SIMULTAN.Data.Components import SimDefaultSlotKeys
|
|
20
20
|
from SIMULTAN.Data.MultiValues import (SimMultiValueField3D, SimMultiValueField3DParameterSource, SimMultiValueBigTable,
|
21
21
|
SimMultiValueBigTableHeader, SimMultiValueBigTableParameterSource)
|
22
22
|
|
23
|
-
from SIMULTAN.Data.Assets import ResourceEntry, ResourceFileEntry, ContainedResourceFileEntry, Asset,
|
23
|
+
from SIMULTAN.Data.Assets import (ResourceEntry, ResourceFileEntry, ContainedResourceFileEntry, Asset,
|
24
|
+
LinkedResourceFileEntry, ResourceDirectoryEntry)
|
24
25
|
from SIMULTAN.Data.Geometry import Face, Edge, Vertex, Volume
|
25
26
|
|
26
27
|
from .multi_values import (simultan_multi_value_field_3d_to_numpy, set_parameter_to_value_field,
|
27
28
|
create_field_parameter, simultan_multi_value_big_table_to_pandas)
|
28
|
-
from .files import FileInfo, remove_asset_from_component, add_asset_to_component
|
29
|
+
from .files import FileInfo, remove_asset_from_component, add_asset_to_component, DirectoryInfo
|
29
30
|
|
30
31
|
if TYPE_CHECKING:
|
31
32
|
from .default_types import ComponentList, ComponentDictionary
|
@@ -767,10 +768,15 @@ def get_sim_double_parameter_value(obj: SimDoubleParameter,
|
|
767
768
|
|
768
769
|
def get_resource_entry_value(obj: ResourceEntry,
|
769
770
|
data_model: DataModel = None,
|
770
|
-
object_mapper: PythonMapper = None) -> FileInfo:
|
771
|
-
|
772
|
-
|
773
|
-
|
771
|
+
object_mapper: PythonMapper = None) -> Union[FileInfo, DirectoryInfo]:
|
772
|
+
if isinstance(obj, (ResourceFileEntry, ContainedResourceFileEntry, LinkedResourceFileEntry)):
|
773
|
+
return FileInfo(file_path=obj.File.FullPath,
|
774
|
+
resource_entry=obj,
|
775
|
+
data_model=data_model)
|
776
|
+
elif isinstance(obj, ResourceDirectoryEntry):
|
777
|
+
return DirectoryInfo(file_path=obj.CurrentFullPath,
|
778
|
+
resource_entry=obj,
|
779
|
+
data_model=data_model)
|
774
780
|
|
775
781
|
|
776
782
|
type_convert_dict = {SimComponent: get_sim_component_value,
|
@@ -780,13 +786,16 @@ type_convert_dict = {SimComponent: get_sim_component_value,
|
|
780
786
|
SimBoolParameter: get_parameter_value,
|
781
787
|
SimEnumParameter: get_parameter_value,
|
782
788
|
ResourceEntry: get_resource_entry_value,
|
789
|
+
ResourceFileEntry: get_resource_entry_value,
|
790
|
+
ResourceDirectoryEntry: get_resource_entry_value,
|
783
791
|
ContainedResourceFileEntry: get_resource_entry_value,
|
784
792
|
LinkedResourceFileEntry: get_resource_entry_value
|
785
793
|
}
|
786
794
|
|
787
795
|
|
788
796
|
def get_obj_value(obj: Union[SimComponent, SimDoubleParameter, SimIntegerParameter, SimStringParameter,
|
789
|
-
SimBoolParameter, SimEnumParameter, ResourceEntry,
|
797
|
+
SimBoolParameter, SimEnumParameter, ResourceEntry, ResourceFileEntry, ContainedResourceFileEntry,
|
798
|
+
ResourceDirectoryEntry, None],
|
790
799
|
data_model: DataModel,
|
791
800
|
object_mapper: PythonMapper) -> Union[SimultanObject, int, float, str, FileInfo, None, pd.DataFrame,
|
792
801
|
np.ndarray]:
|
@@ -1161,6 +1170,44 @@ def set_property_to_file_info(value: FileInfo,
|
|
1161
1170
|
tag=taxonomy_entry)
|
1162
1171
|
|
1163
1172
|
|
1173
|
+
def set_property_to_directory_info(value: DirectoryInfo,
|
1174
|
+
component: SimultanObject,
|
1175
|
+
prop_name: str,
|
1176
|
+
taxonomy_entry: SimTaxonomyEntry,
|
1177
|
+
slot_extension: Union[str, int, float],
|
1178
|
+
component_idx: int = None,
|
1179
|
+
ref_component_idx: int = None,
|
1180
|
+
parameter_idx: int = None,
|
1181
|
+
ref_asset_idx: int = None,
|
1182
|
+
content: Content = None) -> None:
|
1183
|
+
|
1184
|
+
remove_prop_from_sim_component(component=component,
|
1185
|
+
component_idx=component_idx,
|
1186
|
+
ref_component_idx=ref_component_idx,
|
1187
|
+
parameter_idx=parameter_idx,
|
1188
|
+
ref_asset_idx=ref_asset_idx,
|
1189
|
+
keep=['ref_asset_idx'])
|
1190
|
+
|
1191
|
+
value.data_model = component._data_model
|
1192
|
+
|
1193
|
+
if ref_asset_idx is not None:
|
1194
|
+
asset = component._wrapped_obj.ReferencedAssets.Items[ref_asset_idx]
|
1195
|
+
|
1196
|
+
if hasattr(value, 'resource_entry'):
|
1197
|
+
if asset.Resource.Key == value.resource_entry.Key:
|
1198
|
+
return
|
1199
|
+
elif asset.Resource.CurrentFullPath == str(value.file_path):
|
1200
|
+
return
|
1201
|
+
|
1202
|
+
remove_asset_from_component(component._wrapped_obj, asset)
|
1203
|
+
ref_asset_idx = None
|
1204
|
+
|
1205
|
+
add_asset_to_component(component._wrapped_obj,
|
1206
|
+
value.resource_entry,
|
1207
|
+
'0',
|
1208
|
+
tag=taxonomy_entry)
|
1209
|
+
|
1210
|
+
|
1164
1211
|
def set_property_to_parameter(value: Union[int, float, str, Enum, bool],
|
1165
1212
|
component: SimultanObject,
|
1166
1213
|
prop_name: str,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: PySimultan
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.4
|
4
4
|
Project-URL: Documentation, https://github.com/Bühler Maximilian/PySimultan2#readme
|
5
5
|
Project-URL: Issues, https://github.com/Bühler Maximilian/PySimultan2/issues
|
6
6
|
Project-URL: Source, https://github.com/Bühler Maximilian/PySimultan2
|
@@ -1,15 +1,15 @@
|
|
1
1
|
PySimultan2/CHANGELOG.md,sha256=LeVHBC6dGDwDwgXipNrwgvgews9QkyjsqtYa_jiLMm0,217
|
2
|
-
PySimultan2/__about__.py,sha256=
|
2
|
+
PySimultan2/__about__.py,sha256=N_0JaEMS7tHfCVPCoxXAr5ZZHHSVV9O6Uipd_gj4y00,19
|
3
3
|
PySimultan2/__init__.py,sha256=PGVR1uhY01dF5tHyad-znURUZ_LVB95vsjId2BT0aJM,3245
|
4
|
-
PySimultan2/data_model.py,sha256=
|
4
|
+
PySimultan2/data_model.py,sha256=YYtfcR6jvGztng6Be8Vw9FQ02cPkl8ATHTZMoIu1ABQ,34140
|
5
5
|
PySimultan2/default_types.py,sha256=K-Eka5BCKk8DT3HU5761Ym_-ZFmu1_Dro0zW0LVGoHA,27157
|
6
|
-
PySimultan2/files.py,sha256=
|
6
|
+
PySimultan2/files.py,sha256=j3mGpu63h4BCDzyAZSfOIeylLysIUas3ac1MAeRhLXY,21709
|
7
7
|
PySimultan2/multi_values.py,sha256=ZFXlTLuZo32x7_7diYAp2XEjp5uwgHLgNOzN7v74-5I,13650
|
8
8
|
PySimultan2/object_mapper.py,sha256=_SQye38NmIr4m_-X9CuvUJnVDBmjmUDdPH2bnaxpzKY,18546
|
9
9
|
PySimultan2/simultan_object.py,sha256=akaSUZZWIVfwx1wT5EdOgRR2UeShUthX-LE2Uk6w8CQ,19058
|
10
10
|
PySimultan2/taxonomy_maps.py,sha256=abMB2RSKEaliW-Ewegq-Inq9npHeOD1VVrMYoKJAlC0,8762
|
11
|
-
PySimultan2/type_setter_lookup.py,sha256=
|
12
|
-
PySimultan2/utils.py,sha256=
|
11
|
+
PySimultan2/type_setter_lookup.py,sha256=px92E-BlnvY-11F-F7L7cOwbE1_L8FQVqi-23nJi5j4,3518
|
12
|
+
PySimultan2/utils.py,sha256=2rVCg6rX4kI3H4ziOm4ki-akTBJJlBr1n_6-mGSes5M,66388
|
13
13
|
PySimultan2/geometry/__init__.py,sha256=nJolTD1i5J8qUkOQa-r3D20aq3Co3sN31Xc0n4wJpJo,248
|
14
14
|
PySimultan2/geometry/geometry_base.py,sha256=nbb9U2W3vFviVLxISLHRi2CVyLEM-3zIKvoZ1uSYs_8,23420
|
15
15
|
PySimultan2/geometry/utils.py,sha256=J25YsK8sso_UL7xRusItQZvyjtvxdOsSPelBQYFABhY,8519
|
@@ -75,7 +75,7 @@ PySimultan2/resources/assimp.dll,sha256=HwfDwXqoPDTFRyoQpA3qmgZoUdFtziJkV5fNtktE
|
|
75
75
|
PySimultan2/resources/componentmanager.user,sha256=hrzr1US4pqkFnLHXcvPkvrgGd7QvlxaV8mhS6fuikEs,760
|
76
76
|
PySimultan2/resources/defaultsettings.xml,sha256=s6Tk1tubLz5UYqXZWpD42EDHzedemRY1nEneoIVcUfg,392
|
77
77
|
PySimultan2/resources/setup.bat,sha256=fjvvYfVM6TalS-QTSiKAbAId5nTsk8kGGo06ba-wWaY,32
|
78
|
-
pysimultan-0.5.
|
79
|
-
pysimultan-0.5.
|
80
|
-
pysimultan-0.5.
|
81
|
-
pysimultan-0.5.
|
78
|
+
pysimultan-0.5.4.dist-info/METADATA,sha256=rpPIvUKiZc-IY9kSfDo9wPpGJY5Hd0A31UXgbmnQ6kM,2665
|
79
|
+
pysimultan-0.5.4.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
80
|
+
pysimultan-0.5.4.dist-info/licenses/LICENSE.txt,sha256=pmSr98k6N005KMojnZxzLGRuRlDjDx3PUrK1lFj53HA,1126
|
81
|
+
pysimultan-0.5.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|