anatools 5.1.23__py3-none-any.whl → 5.1.25__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.
- anatools/__init__.py +1 -1
- anatools/anaclient/api/volumes.py +7 -1
- anatools/anaclient/volumes.py +6 -6
- anatools/lib/transfer.py +121 -4
- {anatools-5.1.23.data → anatools-5.1.25.data}/scripts/anadeploy +3 -1
- {anatools-5.1.23.dist-info → anatools-5.1.25.dist-info}/METADATA +1 -1
- {anatools-5.1.23.dist-info → anatools-5.1.25.dist-info}/RECORD +18 -18
- {anatools-5.1.23.data → anatools-5.1.25.data}/scripts/ana +0 -0
- {anatools-5.1.23.data → anatools-5.1.25.data}/scripts/anamount +0 -0
- {anatools-5.1.23.data → anatools-5.1.25.data}/scripts/anaprofile +0 -0
- {anatools-5.1.23.data → anatools-5.1.25.data}/scripts/anarules +0 -0
- {anatools-5.1.23.data → anatools-5.1.25.data}/scripts/anaserver +0 -0
- {anatools-5.1.23.data → anatools-5.1.25.data}/scripts/anatransfer +0 -0
- {anatools-5.1.23.data → anatools-5.1.25.data}/scripts/anautils +0 -0
- {anatools-5.1.23.dist-info → anatools-5.1.25.dist-info}/WHEEL +0 -0
- {anatools-5.1.23.dist-info → anatools-5.1.25.dist-info}/entry_points.txt +0 -0
- {anatools-5.1.23.dist-info → anatools-5.1.25.dist-info}/licenses/LICENSE +0 -0
- {anatools-5.1.23.dist-info → anatools-5.1.25.dist-info}/top_level.txt +0 -0
anatools/__init__.py
CHANGED
|
@@ -46,10 +46,16 @@ def getVolumeData(self, volumeId, keys=[], dir=None, recursive=False, cursor=Non
|
|
|
46
46
|
"cursor": cursor,
|
|
47
47
|
"filters": filters
|
|
48
48
|
},
|
|
49
|
-
"query": f"""query
|
|
49
|
+
"query": f"""query
|
|
50
50
|
getVolumeData($volumeId: String!, $keys: [String], $dir: String, $recursive: Boolean, $limit: Int, $cursor: String, $filters: VolumeDataFilter) {{
|
|
51
51
|
getVolumeData(volumeId: $volumeId, keys: $keys, dir: $dir, recursive: $recursive, limit: $limit, cursor: $cursor, filters: $filters) {{
|
|
52
52
|
keys {{{fields}}}
|
|
53
|
+
pageInfo {{
|
|
54
|
+
totalItems
|
|
55
|
+
cursor
|
|
56
|
+
offset
|
|
57
|
+
limit
|
|
58
|
+
}}
|
|
53
59
|
}}
|
|
54
60
|
}}"""})
|
|
55
61
|
return self.errorhandler(response, "getVolumeData")
|
anatools/anaclient/volumes.py
CHANGED
|
@@ -144,17 +144,17 @@ def get_volume_data(self, volumeId, files=None, dir=None, recursive=False, curso
|
|
|
144
144
|
self.check_logout()
|
|
145
145
|
if volumeId is None: raise Exception('The volumeId parameter is required!')
|
|
146
146
|
if limit is not None and limit <= 100: items = limit
|
|
147
|
+
else: items = 100
|
|
147
148
|
if files is None: files = []
|
|
148
149
|
if dir is None: dir = ''
|
|
149
|
-
if cursor is None: cursor = 0
|
|
150
|
-
else: items = 100
|
|
151
150
|
volumedata = []
|
|
152
151
|
while True:
|
|
153
152
|
if limit and len(volumedata) + items > limit: items = limit - len(volumedata)
|
|
154
|
-
ret = self.ana_api.getVolumeData(volumeId=volumeId, keys=files, dir=dir, recursive=recursive, cursor=
|
|
155
|
-
volumedata.extend(ret)
|
|
156
|
-
if len(ret) < items or len(volumedata) == limit: break
|
|
157
|
-
cursor
|
|
153
|
+
ret = self.ana_api.getVolumeData(volumeId=volumeId, keys=files, dir=dir, recursive=recursive, cursor=cursor, limit=items)
|
|
154
|
+
volumedata.extend(ret['keys'])
|
|
155
|
+
if len(ret['keys']) < items or len(volumedata) == limit: break
|
|
156
|
+
cursor = ret['pageInfo']['cursor'] if ret.get('pageInfo') and ret['pageInfo'].get('cursor') else None
|
|
157
|
+
if cursor is None: break
|
|
158
158
|
return volumedata
|
|
159
159
|
|
|
160
160
|
|
anatools/lib/transfer.py
CHANGED
|
@@ -17,11 +17,13 @@ from threading import Lock
|
|
|
17
17
|
try:
|
|
18
18
|
import boto3
|
|
19
19
|
from botocore.exceptions import ClientError
|
|
20
|
+
from boto3.s3.transfer import TransferConfig
|
|
20
21
|
BOTO3_AVAILABLE = True
|
|
21
22
|
except ImportError:
|
|
22
23
|
BOTO3_AVAILABLE = False
|
|
23
24
|
boto3 = None
|
|
24
|
-
ClientError = Exception
|
|
25
|
+
ClientError = Exception
|
|
26
|
+
TransferConfig = None # Placeholder
|
|
25
27
|
|
|
26
28
|
from anatools.lib.print import print_color
|
|
27
29
|
|
|
@@ -479,9 +481,16 @@ def upload_file_to_s3(s3_client, local_file, bucket, key, max_retries=3):
|
|
|
479
481
|
Returns:
|
|
480
482
|
bool: True if successful, False otherwise
|
|
481
483
|
"""
|
|
484
|
+
config = TransferConfig(
|
|
485
|
+
multipart_threshold=100 * 1024 * 1024,
|
|
486
|
+
max_concurrency=10,
|
|
487
|
+
multipart_chunksize=100 * 1024 * 1024,
|
|
488
|
+
use_threads=True
|
|
489
|
+
)
|
|
490
|
+
|
|
482
491
|
for attempt in range(max_retries):
|
|
483
492
|
try:
|
|
484
|
-
s3_client.upload_file(local_file, bucket, key)
|
|
493
|
+
s3_client.upload_file(local_file, bucket, key, Config=config)
|
|
485
494
|
return True
|
|
486
495
|
except ClientError as e:
|
|
487
496
|
if attempt < max_retries - 1:
|
|
@@ -509,9 +518,16 @@ def download_file_from_s3(s3_client, bucket, key, local_file, max_retries=3):
|
|
|
509
518
|
# Create directory if it doesn't exist
|
|
510
519
|
os.makedirs(os.path.dirname(local_file), exist_ok=True)
|
|
511
520
|
|
|
521
|
+
config = TransferConfig(
|
|
522
|
+
multipart_threshold=100 * 1024 * 1024,
|
|
523
|
+
max_concurrency=10,
|
|
524
|
+
multipart_chunksize=100 * 1024 * 1024,
|
|
525
|
+
use_threads=True
|
|
526
|
+
)
|
|
527
|
+
|
|
512
528
|
for attempt in range(max_retries):
|
|
513
529
|
try:
|
|
514
|
-
s3_client.download_file(bucket, key, local_file)
|
|
530
|
+
s3_client.download_file(bucket, key, local_file, Config=config)
|
|
515
531
|
return True
|
|
516
532
|
except ClientError as e:
|
|
517
533
|
if attempt < max_retries - 1:
|
|
@@ -525,6 +541,7 @@ def download_file_from_s3(s3_client, bucket, key, local_file, max_retries=3):
|
|
|
525
541
|
def copy_s3_object(s3_client, source_bucket, source_key, dest_bucket, dest_key, max_retries=3):
|
|
526
542
|
"""
|
|
527
543
|
Copy an object within S3 (server-side copy).
|
|
544
|
+
Uses multipart copy for files >5GB to handle large files efficiently.
|
|
528
545
|
|
|
529
546
|
Args:
|
|
530
547
|
s3_client: boto3 S3 client
|
|
@@ -539,6 +556,18 @@ def copy_s3_object(s3_client, source_bucket, source_key, dest_bucket, dest_key,
|
|
|
539
556
|
"""
|
|
540
557
|
copy_source = {'Bucket': source_bucket, 'Key': source_key}
|
|
541
558
|
|
|
559
|
+
try:
|
|
560
|
+
head_response = s3_client.head_object(Bucket=source_bucket, Key=source_key)
|
|
561
|
+
file_size = head_response['ContentLength']
|
|
562
|
+
except ClientError as e:
|
|
563
|
+
print_color(f"Failed to get object metadata for s3://{source_bucket}/{source_key}: {e}", 'error')
|
|
564
|
+
return False
|
|
565
|
+
|
|
566
|
+
MULTIPART_THRESHOLD = 5 * 1024 * 1024 * 1024
|
|
567
|
+
|
|
568
|
+
if file_size >= MULTIPART_THRESHOLD:
|
|
569
|
+
return _multipart_copy_s3_object(s3_client, source_bucket, source_key, dest_bucket, dest_key, file_size, max_retries)
|
|
570
|
+
|
|
542
571
|
for attempt in range(max_retries):
|
|
543
572
|
try:
|
|
544
573
|
s3_client.copy_object(
|
|
@@ -549,13 +578,101 @@ def copy_s3_object(s3_client, source_bucket, source_key, dest_bucket, dest_key,
|
|
|
549
578
|
return True
|
|
550
579
|
except ClientError as e:
|
|
551
580
|
if attempt < max_retries - 1:
|
|
552
|
-
time.sleep(2 ** attempt)
|
|
581
|
+
time.sleep(2 ** attempt)
|
|
553
582
|
else:
|
|
554
583
|
print_color(f"Failed to copy s3://{source_bucket}/{source_key} to s3://{dest_bucket}/{dest_key}: {e}", 'error')
|
|
555
584
|
return False
|
|
556
585
|
return False
|
|
557
586
|
|
|
558
587
|
|
|
588
|
+
def _multipart_copy_s3_object(s3_client, source_bucket, source_key, dest_bucket, dest_key, file_size, max_retries=3):
|
|
589
|
+
"""
|
|
590
|
+
Perform multipart copy for large S3 objects (>5GB).
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
s3_client: boto3 S3 client
|
|
594
|
+
source_bucket: Source S3 bucket
|
|
595
|
+
source_key: Source S3 key
|
|
596
|
+
dest_bucket: Destination S3 bucket
|
|
597
|
+
dest_key: Destination S3 key
|
|
598
|
+
file_size: Size of the source object in bytes
|
|
599
|
+
max_retries: Maximum number of retry attempts
|
|
600
|
+
|
|
601
|
+
Returns:
|
|
602
|
+
bool: True if successful, False otherwise
|
|
603
|
+
"""
|
|
604
|
+
PART_SIZE = 500 * 1024 * 1024
|
|
605
|
+
|
|
606
|
+
try:
|
|
607
|
+
mpu = s3_client.create_multipart_upload(
|
|
608
|
+
Bucket=dest_bucket,
|
|
609
|
+
Key=dest_key
|
|
610
|
+
)
|
|
611
|
+
upload_id = mpu['UploadId']
|
|
612
|
+
|
|
613
|
+
parts = []
|
|
614
|
+
part_number = 1
|
|
615
|
+
bytes_copied = 0
|
|
616
|
+
|
|
617
|
+
while bytes_copied < file_size:
|
|
618
|
+
start_byte = bytes_copied
|
|
619
|
+
end_byte = min(bytes_copied + PART_SIZE - 1, file_size - 1)
|
|
620
|
+
|
|
621
|
+
for attempt in range(max_retries):
|
|
622
|
+
try:
|
|
623
|
+
part_response = s3_client.upload_part_copy(
|
|
624
|
+
Bucket=dest_bucket,
|
|
625
|
+
Key=dest_key,
|
|
626
|
+
PartNumber=part_number,
|
|
627
|
+
UploadId=upload_id,
|
|
628
|
+
CopySource={
|
|
629
|
+
'Bucket': source_bucket,
|
|
630
|
+
'Key': source_key
|
|
631
|
+
},
|
|
632
|
+
CopySourceRange=f'bytes={start_byte}-{end_byte}'
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
parts.append({
|
|
636
|
+
'ETag': part_response['CopyPartResult']['ETag'],
|
|
637
|
+
'PartNumber': part_number
|
|
638
|
+
})
|
|
639
|
+
break
|
|
640
|
+
except ClientError as e:
|
|
641
|
+
if attempt < max_retries - 1:
|
|
642
|
+
time.sleep(2 ** attempt)
|
|
643
|
+
else:
|
|
644
|
+
s3_client.abort_multipart_upload(
|
|
645
|
+
Bucket=dest_bucket,
|
|
646
|
+
Key=dest_key,
|
|
647
|
+
UploadId=upload_id
|
|
648
|
+
)
|
|
649
|
+
print_color(f"Failed to copy part {part_number} of s3://{source_bucket}/{source_key}: {e}", 'error')
|
|
650
|
+
return False
|
|
651
|
+
|
|
652
|
+
bytes_copied = end_byte + 1
|
|
653
|
+
part_number += 1
|
|
654
|
+
|
|
655
|
+
s3_client.complete_multipart_upload(
|
|
656
|
+
Bucket=dest_bucket,
|
|
657
|
+
Key=dest_key,
|
|
658
|
+
UploadId=upload_id,
|
|
659
|
+
MultipartUpload={'Parts': parts}
|
|
660
|
+
)
|
|
661
|
+
return True
|
|
662
|
+
|
|
663
|
+
except ClientError as e:
|
|
664
|
+
print_color(f"Failed multipart copy of s3://{source_bucket}/{source_key} to s3://{dest_bucket}/{dest_key}: {e}", 'error')
|
|
665
|
+
try:
|
|
666
|
+
s3_client.abort_multipart_upload(
|
|
667
|
+
Bucket=dest_bucket,
|
|
668
|
+
Key=dest_key,
|
|
669
|
+
UploadId=upload_id
|
|
670
|
+
)
|
|
671
|
+
except:
|
|
672
|
+
pass
|
|
673
|
+
return False
|
|
674
|
+
|
|
675
|
+
|
|
559
676
|
def local_to_s3_transfer(client, source_path, dest_volume_id, dest_prefix='',
|
|
560
677
|
recursive=True, parallel=10, dry_run=False, verbose=False):
|
|
561
678
|
"""
|
|
@@ -321,6 +321,7 @@ try:
|
|
|
321
321
|
if selection == str(options-2): remotechannel = create_channel(client=client, organization=remoteorganization, name=args.channel.split('/')[-1].split('.')[0], volumes=volumes)
|
|
322
322
|
else:
|
|
323
323
|
channels = sorted(client.get_channels(organizationId=remoteorganization['organizationId']), key=lambda x: x['name'].lower())
|
|
324
|
+
channels = [c for c in channels if c['organizationId'] == remoteorganization['organizationId']]
|
|
324
325
|
if len(channels) > 0:
|
|
325
326
|
print_color("Choose a channel to deploy to: ", color='brand')
|
|
326
327
|
for i, channel in enumerate(channels):
|
|
@@ -417,7 +418,7 @@ try:
|
|
|
417
418
|
if localservice.remotes and len(localservice.remotes) > 0:
|
|
418
419
|
for i, remote in enumerate(localservice.remotes):
|
|
419
420
|
try:
|
|
420
|
-
if 'environment' not in remote: remote['environment']
|
|
421
|
+
if 'environment' not in remote: remote['environment'] = 'prod'
|
|
421
422
|
if client.environment == remote['environment']:
|
|
422
423
|
s = client.get_services(serviceId=remote['serviceId'])[0]
|
|
423
424
|
organization = client.get_organizations(organizationId=s['organizationId'])[0]
|
|
@@ -480,6 +481,7 @@ try:
|
|
|
480
481
|
if selection == str(options-2): remoteservice = create_service(client=client, organization=remoteorganization, name=localservice.name, volumes=volumes)
|
|
481
482
|
else:
|
|
482
483
|
services = sorted(client.get_services(organizationId=remoteorganization['organizationId']), key=lambda x: x['name'].lower())
|
|
484
|
+
services = [s for s in services if s['organizationId'] == remoteorganization['organizationId']]
|
|
483
485
|
if len(services) > 0:
|
|
484
486
|
print_color("Choose a service to deploy to: ", color='brand')
|
|
485
487
|
for i, service in enumerate(services):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
anatools/__init__.py,sha256=
|
|
1
|
+
anatools/__init__.py,sha256=NS0kdcxltaz_gtuNFFGIX74ZBHX6_4od1uo05xuipng,954
|
|
2
2
|
anatools/anacreate.py,sha256=wK1GKxGSzFdeQtqBkGhpai1sFjSJaKZ2686zrVJPBd0,5622
|
|
3
3
|
anatools/anaclient/__init__.py,sha256=tjCd-MMkWOjhjY1mlbGtHNhEdHLj-W687GiYJHsf_J0,50
|
|
4
4
|
anatools/anaclient/_menu.py,sha256=WSD8h7_SPRq9F6I0ohzkA4L0aLUx7L9YtIihk9eiD_o,5965
|
|
@@ -22,7 +22,7 @@ anatools/anaclient/preview.py,sha256=524-If_otuRAuwJtVRcQxL8G0wB-1i_DbgYoi_yePXI
|
|
|
22
22
|
anatools/anaclient/rules.py,sha256=BA5KHLfyh4ddQYRdGmRwX-hGITL3GolMVzDuBX-hZno,3903
|
|
23
23
|
anatools/anaclient/services.py,sha256=sRRdxM7ZEUfTDKU4700C9KcKsWY8AUTaw210pulwML4,18367
|
|
24
24
|
anatools/anaclient/umap.py,sha256=Dy37yqoNb8HQWPmCh8ZqEIBMijSTqv9tcRcfO0_IXWQ,4144
|
|
25
|
-
anatools/anaclient/volumes.py,sha256=
|
|
25
|
+
anatools/anaclient/volumes.py,sha256=7NOBC1g_UV5miVLgaQ_LRgoyWiSWBvA-3381f0gVV6A,21442
|
|
26
26
|
anatools/anaclient/workspaces.py,sha256=4aUBfqfW7jzlb6P55LcA0IHmZoNzIWej9tL0DY2pNog,7956
|
|
27
27
|
anatools/anaclient/api/__init__.py,sha256=SrGDhIFobFBhOlE-hOtnJvdrnBAuypG-Im_oyzSPa3E,39
|
|
28
28
|
anatools/anaclient/api/analytics.py,sha256=AFeJj_pl1h70oki5teFDGYRgPIOwHn7yJB-TYNouZsc,3666
|
|
@@ -46,7 +46,7 @@ anatools/anaclient/api/preview.py,sha256=OUN4MkYvC0HK6_3ZDxq7jayjPyJpxtgkHhEytmY
|
|
|
46
46
|
anatools/anaclient/api/rules.py,sha256=yvxbk48m0ZZrWoHpmwS6kC4E2m7iSaY7iaLp0d6xmoc,4780
|
|
47
47
|
anatools/anaclient/api/services.py,sha256=htSjiRz7-kiDivt-xl5wtCSBZPLjDrPjKMKVHcdAM_o,11459
|
|
48
48
|
anatools/anaclient/api/umap.py,sha256=iMRE_z4Umg4-U_uEvdxho93QXOocF3-lN96qHfgbu64,3490
|
|
49
|
-
anatools/anaclient/api/volumes.py,sha256=
|
|
49
|
+
anatools/anaclient/api/volumes.py,sha256=gOc-RPg7I988uKWk38w-DJIPM1Ltgby_wkrpwL_f-fE,9555
|
|
50
50
|
anatools/anaclient/api/workspaces.py,sha256=kIXQAkznBGab38v5aaYcGwP1AnOp4PgID33lPMseRvA,8711
|
|
51
51
|
anatools/anaclient/tests/__init__.py,sha256=uEOcjK2hof0s7RItreGywkCIULWxgcdKbI95JjrEGzk,104
|
|
52
52
|
anatools/anaclient/tests/agents_test.py,sha256=dUrVzb4WmfAE_0Uu5V1zSdW4MUA4I1t1IfNgXTdP7rA,361
|
|
@@ -120,7 +120,7 @@ anatools/lib/scene.py,sha256=ANTcP_UVNQQ_6WjgmqHF6u_9HpJsElKpqszSUiN75u4,8339
|
|
|
120
120
|
anatools/lib/search_utils.py,sha256=DUu7fOIgTWPXItAjPlzfiMazrN9J8ytSjjKW2oWCLbw,3757
|
|
121
121
|
anatools/lib/service.py,sha256=4HfNBKIj3cHqKxD2fJcxlP8MMD1FozHayAsuoRGWURI,2720
|
|
122
122
|
anatools/lib/tools.py,sha256=C1vnbGU6yZJ9q1xsaUP6RDHTohfmm69KZRz7nIjrMLo,5847
|
|
123
|
-
anatools/lib/transfer.py,sha256=
|
|
123
|
+
anatools/lib/transfer.py,sha256=QZ47zD5MsjhBY5jaQMNI84WELUm4QGwFvCjFDy9LOJU,32788
|
|
124
124
|
anatools/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
125
125
|
anatools/nodes/constants.py,sha256=D0nikvblG1XR5umQFqAgbHjUeh9m8eEDiaYOqfInegk,1115
|
|
126
126
|
anatools/nodes/constants.yml,sha256=MZJjhUOFth5Mg8EPU-_wnQkJ9DqPJEzyiLsXyS9UVME,1285
|
|
@@ -140,17 +140,17 @@ anatools/nodes/volume_directory.py,sha256=oe721h7qOplRj6N6NpGuyY1HCM277NSYA-6Uo3
|
|
|
140
140
|
anatools/nodes/volume_directory.yml,sha256=MbyuLUlcqWIlQadYcn4Rvf6roypqh5IiP3dP57TilbY,901
|
|
141
141
|
anatools/nodes/volume_file.py,sha256=YA4zCyRvVzF_9mMefGx29JLE7o9i6-NPaC40BFGv4_c,557
|
|
142
142
|
anatools/nodes/volume_file.yml,sha256=i8bo9QeQmTLeWjv9Rh4EDoDqwOBULNPV7SMLO5AK8DI,862
|
|
143
|
-
anatools-5.1.
|
|
144
|
-
anatools-5.1.
|
|
145
|
-
anatools-5.1.
|
|
146
|
-
anatools-5.1.
|
|
147
|
-
anatools-5.1.
|
|
148
|
-
anatools-5.1.
|
|
149
|
-
anatools-5.1.
|
|
150
|
-
anatools-5.1.
|
|
151
|
-
anatools-5.1.
|
|
152
|
-
anatools-5.1.
|
|
153
|
-
anatools-5.1.
|
|
154
|
-
anatools-5.1.
|
|
155
|
-
anatools-5.1.
|
|
156
|
-
anatools-5.1.
|
|
143
|
+
anatools-5.1.25.data/scripts/ana,sha256=qe7LDHRgJRPyomzAumdcYy0D5SuaUVag0N8SVevpxcU,5739
|
|
144
|
+
anatools-5.1.25.data/scripts/anadeploy,sha256=GW0J6YzEvGY2TMbyzkfi-4Ov_gsw4qz2hHJwRx2o7nc,38216
|
|
145
|
+
anatools-5.1.25.data/scripts/anamount,sha256=AiaUgaaVVREFY2FLYRmSA8xgSwbfiF9NJYi91SWRA1I,6186
|
|
146
|
+
anatools-5.1.25.data/scripts/anaprofile,sha256=1YUUwHiSa4ORQsxVf2HaSakayNTwMTNeF2snEMSJQAM,9779
|
|
147
|
+
anatools-5.1.25.data/scripts/anarules,sha256=vT2V-77DoL-o6mApSMVXjqeXRIWm44X3jPXdcNK29N0,8972
|
|
148
|
+
anatools-5.1.25.data/scripts/anaserver,sha256=QB1k_vhXAFGMOi9SNIFwgzkzN5LzJeVLtIVkp1oHq4I,8343
|
|
149
|
+
anatools-5.1.25.data/scripts/anatransfer,sha256=GbMLjgA3TP4Oo2mbUxWnkkSC4nKpw1DWta-WVfcNftw,14564
|
|
150
|
+
anatools-5.1.25.data/scripts/anautils,sha256=fziapZuKuBO0VKRgb4C4Js8p9zxxh8OHQmmkNdo3t3E,9530
|
|
151
|
+
anatools-5.1.25.dist-info/licenses/LICENSE,sha256=aw0uaPvFzrHLJxBvuRqUcE2_srfM32-1suya9HbZCY8,1072
|
|
152
|
+
anatools-5.1.25.dist-info/METADATA,sha256=Pg2TjtE7TGhheLDoVYgyLS3qbZdcc5Iltr969jmhMC0,8218
|
|
153
|
+
anatools-5.1.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
154
|
+
anatools-5.1.25.dist-info/entry_points.txt,sha256=KsZUmvbH3HXC2CdVpE2GZNR2u_cJNVIbm6BnD658FgM,54
|
|
155
|
+
anatools-5.1.25.dist-info/top_level.txt,sha256=p7xa5bG7NX8pSMJOvRunSz1d7rGPGsBd5-A4gzD4r6w,9
|
|
156
|
+
anatools-5.1.25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|