pyPreservica 3.2.0__py3-none-any.whl → 3.2.2__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 pyPreservica might be problematic. Click here for more details.
- pyPreservica/__init__.py +1 -1
- pyPreservica/common.py +31 -2
- pyPreservica/entityAPI.py +100 -21
- {pypreservica-3.2.0.dist-info → pypreservica-3.2.2.dist-info}/METADATA +4 -3
- {pypreservica-3.2.0.dist-info → pypreservica-3.2.2.dist-info}/RECORD +8 -8
- {pypreservica-3.2.0.dist-info → pypreservica-3.2.2.dist-info}/WHEEL +1 -1
- {pypreservica-3.2.0.dist-info → pypreservica-3.2.2.dist-info}/licenses/LICENSE.txt +0 -0
- {pypreservica-3.2.0.dist-info → pypreservica-3.2.2.dist-info}/top_level.txt +0 -0
pyPreservica/__init__.py
CHANGED
pyPreservica/common.py
CHANGED
|
@@ -27,6 +27,7 @@ from requests import Session
|
|
|
27
27
|
from urllib3.util import Retry
|
|
28
28
|
import requests
|
|
29
29
|
from requests.adapters import HTTPAdapter
|
|
30
|
+
from typing import TypeVar
|
|
30
31
|
|
|
31
32
|
import pyPreservica
|
|
32
33
|
|
|
@@ -420,6 +421,26 @@ class Bitstream:
|
|
|
420
421
|
return self.__str__()
|
|
421
422
|
|
|
422
423
|
|
|
424
|
+
class ExternIdentifier:
|
|
425
|
+
"""
|
|
426
|
+
Class to represent the External Identifier Object in the Preservica data model
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
def __init__(self, identifier_type: str, identifier_value: str):
|
|
430
|
+
self.type = identifier_type
|
|
431
|
+
self.value = identifier_value
|
|
432
|
+
self.id = None
|
|
433
|
+
|
|
434
|
+
def __str__(self):
|
|
435
|
+
return f"""
|
|
436
|
+
Identifier: {self.id}
|
|
437
|
+
Identifier Type: {self.type}
|
|
438
|
+
Identifier Value: {self.value}
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
def __repr__(self):
|
|
442
|
+
return self.__str__()
|
|
443
|
+
|
|
423
444
|
class Generation:
|
|
424
445
|
"""
|
|
425
446
|
Class to represent the Generation Object in the Preservica data model
|
|
@@ -528,6 +549,9 @@ class ContentObject(Entity):
|
|
|
528
549
|
self.tag = "ContentObject"
|
|
529
550
|
|
|
530
551
|
|
|
552
|
+
EntityT = TypeVar("EntityT", Folder, Asset, ContentObject, None)
|
|
553
|
+
|
|
554
|
+
|
|
531
555
|
class Representation:
|
|
532
556
|
"""
|
|
533
557
|
Class to represent the Representation Object in the Preservica data model
|
|
@@ -738,7 +762,7 @@ class AuthenticatedAPI:
|
|
|
738
762
|
Return the edition of this tenancy
|
|
739
763
|
"""
|
|
740
764
|
if self.major_version < 8 and self.minor_version < 3:
|
|
741
|
-
raise RuntimeError("Entitlement
|
|
765
|
+
raise RuntimeError("Entitlement API is only available when connected to a v7.3 System")
|
|
742
766
|
|
|
743
767
|
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json'}
|
|
744
768
|
|
|
@@ -777,6 +801,7 @@ class AuthenticatedAPI:
|
|
|
777
801
|
self.sec_ns = f"{NS_SEC_ROOT}/v{self.major_version}.{self.minor_version}"
|
|
778
802
|
self.admin_ns = f"{NS_ADMIN}/v{self.major_version}.{self.minor_version}"
|
|
779
803
|
|
|
804
|
+
|
|
780
805
|
def __version_number__(self):
|
|
781
806
|
"""
|
|
782
807
|
Determine the version number of the server
|
|
@@ -791,6 +816,10 @@ class AuthenticatedAPI:
|
|
|
791
816
|
self.major_version = int(version_numbers[0])
|
|
792
817
|
self.minor_version = int(version_numbers[1])
|
|
793
818
|
self.patch_version = int(version_numbers[2])
|
|
819
|
+
|
|
820
|
+
if self.server == "preview.preservica.com":
|
|
821
|
+
self.minor_version = 1
|
|
822
|
+
|
|
794
823
|
return version
|
|
795
824
|
elif request.status_code == requests.codes.unauthorized:
|
|
796
825
|
self.token = self.__token__()
|
|
@@ -801,7 +830,7 @@ class AuthenticatedAPI:
|
|
|
801
830
|
RuntimeError(request.status_code, "version number failed")
|
|
802
831
|
|
|
803
832
|
def __str__(self):
|
|
804
|
-
return f"pyPreservica version: {pyPreservica.__version__} (Preservica
|
|
833
|
+
return f"pyPreservica version: {pyPreservica.__version__} (Preservica 8.0 Compatible) " \
|
|
805
834
|
f"Connected to: {self.server} Preservica version: {self.version} as {self.username} " \
|
|
806
835
|
f"in tenancy {self.tenant}"
|
|
807
836
|
|
pyPreservica/entityAPI.py
CHANGED
|
@@ -149,7 +149,6 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
149
149
|
|
|
150
150
|
return storage_locations
|
|
151
151
|
|
|
152
|
-
|
|
153
152
|
def bitstream_content(self, bitstream: Bitstream, filename: str, chunk_size: int = CHUNK_SIZE) -> Union[int, None]:
|
|
154
153
|
"""
|
|
155
154
|
Download a file represented as a Bitstream to a local filename
|
|
@@ -486,6 +485,53 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
486
485
|
logger.error(request)
|
|
487
486
|
raise RuntimeError(request.status_code, "delete_identifier failed")
|
|
488
487
|
|
|
488
|
+
def entity_identifiers(self, entity: Entity, external_identifier_type = None) -> set[ExternIdentifier]:
|
|
489
|
+
"""
|
|
490
|
+
Get all external identifiers on an entity
|
|
491
|
+
|
|
492
|
+
Returns the set of external identifiers on the entity
|
|
493
|
+
|
|
494
|
+
:param entity: The Entity (Asset or Folder)
|
|
495
|
+
:param external_identifier_type: Optional identifier type to filter the results
|
|
496
|
+
:type entity: Entity
|
|
497
|
+
"""
|
|
498
|
+
headers = {HEADER_TOKEN: self.token}
|
|
499
|
+
request = self.session.get(
|
|
500
|
+
f'{self.protocol}://{self.server}/api/entity/{entity.path}/{entity.reference}/identifiers',
|
|
501
|
+
headers=headers)
|
|
502
|
+
if request.status_code == requests.codes.ok:
|
|
503
|
+
xml_response = str(request.content.decode('utf-8'))
|
|
504
|
+
logger.debug(xml_response)
|
|
505
|
+
entity_response = xml.etree.ElementTree.fromstring(xml_response)
|
|
506
|
+
identifier_list = entity_response.findall(f'.//{{{self.xip_ns}}}Identifier')
|
|
507
|
+
result = set()
|
|
508
|
+
for identifier in identifier_list:
|
|
509
|
+
identifier_value = identifier_type = identifier_id = ""
|
|
510
|
+
for child in identifier:
|
|
511
|
+
if child.tag.endswith("Type"):
|
|
512
|
+
identifier_type = child.text
|
|
513
|
+
if child.tag.endswith("Value"):
|
|
514
|
+
identifier_value = child.text
|
|
515
|
+
if child.tag.endswith("ApiId"):
|
|
516
|
+
identifier_id = child.text
|
|
517
|
+
if external_identifier_type is None:
|
|
518
|
+
external_id: ExternIdentifier = ExternIdentifier(identifier_type, identifier_value)
|
|
519
|
+
external_id.identifier_id = identifier_id
|
|
520
|
+
result.add(external_id)
|
|
521
|
+
else:
|
|
522
|
+
if identifier_type == external_identifier_type:
|
|
523
|
+
external_id: ExternIdentifier = ExternIdentifier(identifier_type, identifier_value)
|
|
524
|
+
external_id.identifier_id = identifier_id
|
|
525
|
+
result.add(external_id)
|
|
526
|
+
return result
|
|
527
|
+
elif request.status_code == requests.codes.unauthorized:
|
|
528
|
+
self.token = self.__token__()
|
|
529
|
+
return self.entity_identifiers(entity)
|
|
530
|
+
else:
|
|
531
|
+
exception = HTTPException(entity.reference, request.status_code, request.url, "identifiers_for_entity",
|
|
532
|
+
request.content.decode('utf-8'))
|
|
533
|
+
logger.error(exception)
|
|
534
|
+
raise exception
|
|
489
535
|
|
|
490
536
|
def identifiers_for_entity(self, entity: Entity) -> set[Tuple]:
|
|
491
537
|
"""
|
|
@@ -524,16 +570,14 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
524
570
|
logger.error(exception)
|
|
525
571
|
raise exception
|
|
526
572
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
def identifier(self, identifier_type: str, identifier_value: str) -> set[Entity]:
|
|
573
|
+
def identifier(self, identifier_type: str, identifier_value: str) -> set[EntityT]:
|
|
530
574
|
"""
|
|
531
|
-
|
|
575
|
+
Get all entities which have the external identifier
|
|
532
576
|
|
|
533
|
-
|
|
577
|
+
Returns the set of entities which have the external identifier
|
|
534
578
|
|
|
535
|
-
|
|
536
|
-
|
|
579
|
+
:param identifier_type: The identifier type
|
|
580
|
+
:param identifier_value: The identifier value
|
|
537
581
|
"""
|
|
538
582
|
headers = {HEADER_TOKEN: self.token}
|
|
539
583
|
payload = {'type': identifier_type, 'value': identifier_value}
|
|
@@ -861,7 +905,7 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
861
905
|
logger.error(exception)
|
|
862
906
|
raise exception
|
|
863
907
|
|
|
864
|
-
def delete_metadata(self, entity:
|
|
908
|
+
def delete_metadata(self, entity: EntityT, schema: str) -> EntityT:
|
|
865
909
|
"""
|
|
866
910
|
Deletes all the metadata fragments on an entity which match the schema URI
|
|
867
911
|
|
|
@@ -887,7 +931,7 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
887
931
|
|
|
888
932
|
return self.entity(entity.entity_type, entity.reference)
|
|
889
933
|
|
|
890
|
-
def update_metadata(self, entity:
|
|
934
|
+
def update_metadata(self, entity: EntityT, schema: str, data: Any) -> EntityT:
|
|
891
935
|
"""
|
|
892
936
|
Update all existing metadata fragments which match the schema
|
|
893
937
|
|
|
@@ -933,7 +977,9 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
933
977
|
raise exception
|
|
934
978
|
return self.entity(entity.entity_type, entity.reference)
|
|
935
979
|
|
|
936
|
-
def add_metadata_as_fragment(
|
|
980
|
+
def add_metadata_as_fragment(
|
|
981
|
+
self, entity: EntityT, schema: str, xml_fragment: str
|
|
982
|
+
) -> EntityT:
|
|
937
983
|
"""
|
|
938
984
|
Add a metadata fragment with a given namespace URI to an Entity
|
|
939
985
|
|
|
@@ -969,8 +1015,7 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
969
1015
|
logger.error(exception)
|
|
970
1016
|
raise exception
|
|
971
1017
|
|
|
972
|
-
|
|
973
|
-
def add_metadata(self, entity: Entity, schema: str, data) -> Entity:
|
|
1018
|
+
def add_metadata(self, entity: EntityT, schema: str, data) -> EntityT:
|
|
974
1019
|
"""
|
|
975
1020
|
Add a metadata fragment with a given namespace URI
|
|
976
1021
|
|
|
@@ -1011,7 +1056,7 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
1011
1056
|
logger.error(exception)
|
|
1012
1057
|
raise exception
|
|
1013
1058
|
|
|
1014
|
-
def save(self, entity:
|
|
1059
|
+
def save(self, entity: EntityT) -> EntityT:
|
|
1015
1060
|
"""
|
|
1016
1061
|
Save the title and description of an entity
|
|
1017
1062
|
|
|
@@ -1109,7 +1154,6 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
1109
1154
|
def get_progress(self, pid: str) -> AsyncProgress:
|
|
1110
1155
|
return AsyncProgress[self.get_async_progress(pid)]
|
|
1111
1156
|
|
|
1112
|
-
|
|
1113
1157
|
def get_async_progress(self, pid: str) -> str:
|
|
1114
1158
|
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'text/plain'}
|
|
1115
1159
|
request = self.session.get(f"{self.protocol}://{self.server}/api/entity/progress/{pid}", headers=headers)
|
|
@@ -1129,7 +1173,7 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
1129
1173
|
logger.error(exception)
|
|
1130
1174
|
raise exception
|
|
1131
1175
|
|
|
1132
|
-
def move_sync(self, entity:
|
|
1176
|
+
def move_sync(self, entity: EntityT, dest_folder: Folder) -> EntityT:
|
|
1133
1177
|
"""
|
|
1134
1178
|
Move an Entity (Asset or Folder) to a new Folder
|
|
1135
1179
|
If dest_folder is None then the entity must be a Folder and will be moved to the root of the repository
|
|
@@ -1169,7 +1213,7 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
1169
1213
|
logger.error(exception)
|
|
1170
1214
|
raise exception
|
|
1171
1215
|
|
|
1172
|
-
def move(self, entity:
|
|
1216
|
+
def move(self, entity: EntityT, dest_folder: Folder) -> EntityT:
|
|
1173
1217
|
"""
|
|
1174
1218
|
Move an Entity (Asset or Folder) to a new Folder
|
|
1175
1219
|
If dest_folder is None then the entity must be a Folder and will be moved to the root of the repository
|
|
@@ -1274,7 +1318,7 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
1274
1318
|
else:
|
|
1275
1319
|
return xml_object.find(tag).text
|
|
1276
1320
|
|
|
1277
|
-
def security_tag_sync(self, entity:
|
|
1321
|
+
def security_tag_sync(self, entity: EntityT, new_tag: str) -> EntityT:
|
|
1278
1322
|
"""
|
|
1279
1323
|
Change the security tag for a folder or asset
|
|
1280
1324
|
|
|
@@ -1354,7 +1398,7 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
1354
1398
|
logger.error(exception)
|
|
1355
1399
|
raise exception
|
|
1356
1400
|
|
|
1357
|
-
def entity(self, entity_type: EntityType, reference: str) ->
|
|
1401
|
+
def entity(self, entity_type: EntityType, reference: str) -> EntityT:
|
|
1358
1402
|
"""
|
|
1359
1403
|
Retrieve an entity by its type and reference
|
|
1360
1404
|
|
|
@@ -1416,6 +1460,42 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
1416
1460
|
logger.error(exception)
|
|
1417
1461
|
raise exception
|
|
1418
1462
|
|
|
1463
|
+
def merge_folder(self, folder: Folder)-> str:
|
|
1464
|
+
"""
|
|
1465
|
+
Create a new Asset with the content from each Asset in the Folder
|
|
1466
|
+
|
|
1467
|
+
This call will create a new multi-part Asset which contains all the content from the Folder.
|
|
1468
|
+
|
|
1469
|
+
The new Asset which is created will have the same title, description and parent as the Folder.
|
|
1470
|
+
|
|
1471
|
+
The return value is the progress status of the merge operation.
|
|
1472
|
+
"""
|
|
1473
|
+
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8', 'accept': 'text/plain;charset=UTF-8'}
|
|
1474
|
+
payload = f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
1475
|
+
<MergeAction xmlns="{self.entity_ns}" xmlns:xip="{self.xip_ns}">
|
|
1476
|
+
<Title>{folder.title}</Title>
|
|
1477
|
+
<Description>{folder.description}</Description>
|
|
1478
|
+
<Entity excludeIdentifiers="true" excludeLinks="true" excludeMetadata="true" ref="{folder.reference}" type="SO"/>
|
|
1479
|
+
</MergeAction>"""
|
|
1480
|
+
request = self.session.post(
|
|
1481
|
+
f"{self.protocol}://{self.server}/api/entity/actions/merges", data=payload, headers=headers)
|
|
1482
|
+
if request.status_code == requests.codes.accepted:
|
|
1483
|
+
return request.content.decode('utf-8')
|
|
1484
|
+
elif request.status_code == requests.codes.unauthorized:
|
|
1485
|
+
self.token = self.__token__()
|
|
1486
|
+
return self.merge_folder(folder)
|
|
1487
|
+
else:
|
|
1488
|
+
exception = HTTPException(
|
|
1489
|
+
folder.reference,
|
|
1490
|
+
request.status_code,
|
|
1491
|
+
request.url,
|
|
1492
|
+
"merge_folder",
|
|
1493
|
+
request.content.decode("utf-8"),
|
|
1494
|
+
)
|
|
1495
|
+
logger.error(exception)
|
|
1496
|
+
raise exception
|
|
1497
|
+
|
|
1498
|
+
|
|
1419
1499
|
def asset(self, reference: str) -> Asset:
|
|
1420
1500
|
"""
|
|
1421
1501
|
Retrieve an Asset by its reference
|
|
@@ -2209,7 +2289,6 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
2209
2289
|
if "username" in kwargs:
|
|
2210
2290
|
params["username"] = kwargs.get("username")
|
|
2211
2291
|
|
|
2212
|
-
|
|
2213
2292
|
if next_page is None:
|
|
2214
2293
|
request = self.session.get(f'{self.protocol}://{self.server}/api/entity/events', params=params,
|
|
2215
2294
|
headers=headers)
|
|
@@ -2512,4 +2591,4 @@ class EntityAPI(AuthenticatedAPI):
|
|
|
2512
2591
|
exception = HTTPException(entity.reference, request.status_code, request.url,
|
|
2513
2592
|
"_delete_entity", request.content.decode('utf-8'))
|
|
2514
2593
|
logger.error(exception)
|
|
2515
|
-
raise exception
|
|
2594
|
+
raise exception
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyPreservica
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.2
|
|
4
4
|
Summary: Python library for the Preservica API
|
|
5
5
|
Home-page: https://pypreservica.readthedocs.io/
|
|
6
6
|
Author: James Carr
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
20
|
Classifier: Operating System :: OS Independent
|
|
20
21
|
Classifier: Topic :: System :: Archiving
|
|
21
22
|
Description-Content-Type: text/markdown
|
|
@@ -23,8 +24,8 @@ License-File: LICENSE.txt
|
|
|
23
24
|
Requires-Dist: requests
|
|
24
25
|
Requires-Dist: urllib3
|
|
25
26
|
Requires-Dist: certifi
|
|
26
|
-
Requires-Dist: boto3
|
|
27
|
-
Requires-Dist: botocore
|
|
27
|
+
Requires-Dist: boto3>=1.38.0
|
|
28
|
+
Requires-Dist: botocore>=1.38.0
|
|
28
29
|
Requires-Dist: s3transfer
|
|
29
30
|
Requires-Dist: azure-storage-blob
|
|
30
31
|
Requires-Dist: tqdm
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
pyPreservica/__init__.py,sha256=
|
|
1
|
+
pyPreservica/__init__.py,sha256=tLUJ6UX2RCmqbq_evT5ZlorC4T9sI7Opv83-E7Rvzj4,1250
|
|
2
2
|
pyPreservica/adminAPI.py,sha256=aMN2twcUZOFoGx2yapC6GVtBTdYHUJFA-5bdWVkCwS8,37773
|
|
3
3
|
pyPreservica/authorityAPI.py,sha256=jpf_m9i-IakyNVooi2yELuKt4yhX73hWqQNbPRHZx2g,9206
|
|
4
|
-
pyPreservica/common.py,sha256=
|
|
4
|
+
pyPreservica/common.py,sha256=CLfHI0Fec_wp1zngqw7-iIl2Yp3hG0ohjuhdl-K84hU,39030
|
|
5
5
|
pyPreservica/contentAPI.py,sha256=ZvX2aGQEaksmw-m-oEUI6daVSqFe_IcE1cGwCNbSCDQ,22286
|
|
6
|
-
pyPreservica/entityAPI.py,sha256=
|
|
6
|
+
pyPreservica/entityAPI.py,sha256=mELG2TxnFBCuDtsrZ2eRNc8EF-D6dI-dumzTfK976TQ,129974
|
|
7
7
|
pyPreservica/mdformsAPI.py,sha256=_hBjT4-OzgLQGDfYX7b_01P27wc-RmsCEu57VtyAdh8,19173
|
|
8
8
|
pyPreservica/monitorAPI.py,sha256=LJOUrynBOWKlNiYpZ1iH8qB1oIIuKX1Ms1SRBcuXohA,6274
|
|
9
9
|
pyPreservica/opex.py,sha256=ccra1S4ojUXS3PlbU8WfxajOkJrwG4OykBnNrYP_jus,4875
|
|
@@ -13,8 +13,8 @@ pyPreservica/settingsAPI.py,sha256=jXnMOCq3mimta6E-Os3J1I1if2pYsjLpOazAx8L-ZQI,1
|
|
|
13
13
|
pyPreservica/uploadAPI.py,sha256=uX67mW-2q7FmjtXQ759GwHPL6Zs7R-iE8-86PBApvbY,99823
|
|
14
14
|
pyPreservica/webHooksAPI.py,sha256=B3C6PV_3JLlJrr9PtsTzL-21M0msx8Mnj18Xb3Bv4RE,6814
|
|
15
15
|
pyPreservica/workflowAPI.py,sha256=OcOiiUdrQerbPllrkj1lWpmuW0jTuyyV0urwPSYcd_U,17561
|
|
16
|
-
pypreservica-3.2.
|
|
17
|
-
pypreservica-3.2.
|
|
18
|
-
pypreservica-3.2.
|
|
19
|
-
pypreservica-3.2.
|
|
20
|
-
pypreservica-3.2.
|
|
16
|
+
pypreservica-3.2.2.dist-info/licenses/LICENSE.txt,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
17
|
+
pypreservica-3.2.2.dist-info/METADATA,sha256=_sr1wxUFF5oIuFQB6A90sDUAw88cWb11jhQ-kJGSBrk,3077
|
|
18
|
+
pypreservica-3.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
pypreservica-3.2.2.dist-info/top_level.txt,sha256=iIBh6NAznYQHOV8mv_y_kGKSDITek9rANyFDwJsbU-c,13
|
|
20
|
+
pypreservica-3.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|