atomicshop 3.1.8__py3-none-any.whl → 3.1.10__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 atomicshop might be problematic. Click here for more details.
- atomicshop/__init__.py +1 -1
- atomicshop/dns.py +12 -2
- atomicshop/wrappers/githubw.py +37 -13
- atomicshop/wrappers/netshw.py +150 -0
- atomicshop/wrappers/winregw/winreg_network.py +6 -1
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.10.dist-info}/METADATA +1 -1
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.10.dist-info}/RECORD +10 -9
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.10.dist-info}/LICENSE.txt +0 -0
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.10.dist-info}/WHEEL +0 -0
- {atomicshop-3.1.8.dist-info → atomicshop-3.1.10.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/dns.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import socket
|
|
1
2
|
import argparse
|
|
2
3
|
|
|
3
4
|
# noinspection PyPackageRequirements
|
|
@@ -6,7 +7,7 @@ import dns.resolver
|
|
|
6
7
|
from . import print_api
|
|
7
8
|
from .permissions import permissions
|
|
8
9
|
from .wrappers.pywin32w.wmis import win32networkadapter
|
|
9
|
-
from .wrappers
|
|
10
|
+
from .wrappers import netshw
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
# Defining Dictionary of Numeric to String DNS Query Types.
|
|
@@ -73,7 +74,16 @@ def get_default_dns_gateway() -> tuple[bool, list[str]]:
|
|
|
73
74
|
:return: tuple(is dynamic boolean, list of DNS server IPv4s).
|
|
74
75
|
"""
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
interfaces_with_dns_settings: list[dict] = netshw.get_netsh_ipv4()
|
|
78
|
+
|
|
79
|
+
default_interface_ipv4 = socket.gethostbyname(socket.gethostname())
|
|
80
|
+
is_dynamic, dns_servers = None, None
|
|
81
|
+
for interface in interfaces_with_dns_settings:
|
|
82
|
+
if default_interface_ipv4 in interface['ip_addresses']:
|
|
83
|
+
is_dynamic = interface['dns_mode']
|
|
84
|
+
dns_servers = interface['dns_servers']
|
|
85
|
+
break
|
|
86
|
+
|
|
77
87
|
return is_dynamic, dns_servers
|
|
78
88
|
|
|
79
89
|
|
atomicshop/wrappers/githubw.py
CHANGED
|
@@ -389,12 +389,12 @@ class GitHubWrapper:
|
|
|
389
389
|
"""
|
|
390
390
|
return self.get_the_latest_release_json()['tag_name']
|
|
391
391
|
|
|
392
|
-
def
|
|
392
|
+
def get_latest_commit(self) -> dict:
|
|
393
393
|
"""
|
|
394
|
-
This function retrieves the
|
|
394
|
+
This function retrieves the latest commit on the specified branch.
|
|
395
395
|
It uses the GitHub API endpoint for commits.
|
|
396
396
|
|
|
397
|
-
:return:
|
|
397
|
+
:return: dict, the latest commit data.
|
|
398
398
|
"""
|
|
399
399
|
|
|
400
400
|
headers: dict = self._get_headers()
|
|
@@ -413,9 +413,22 @@ class GitHubWrapper:
|
|
|
413
413
|
|
|
414
414
|
commits = response.json()
|
|
415
415
|
if not commits:
|
|
416
|
-
return
|
|
416
|
+
return {}
|
|
417
|
+
|
|
418
|
+
latest_commit = commits[0]
|
|
419
|
+
return latest_commit
|
|
420
|
+
|
|
421
|
+
def get_latest_commit_message(self):
|
|
422
|
+
"""
|
|
423
|
+
This function retrieves the commit message (comment) of the latest commit on the specified branch.
|
|
424
|
+
It uses the GitHub API endpoint for commits.
|
|
417
425
|
|
|
418
|
-
|
|
426
|
+
:return: str, the commit message of the latest commit.
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
latest_commit: dict = self.get_latest_commit()
|
|
430
|
+
|
|
431
|
+
commit_message = latest_commit.get("commit", {}).get("message", "")
|
|
419
432
|
return commit_message
|
|
420
433
|
|
|
421
434
|
|
|
@@ -432,7 +445,7 @@ def parse_github_args():
|
|
|
432
445
|
parser.add_argument(
|
|
433
446
|
'-p', '--path', type=str, default=None,
|
|
434
447
|
help="The path to the file/folder inside the repo that we'll do certain actions on.\n"
|
|
435
|
-
"Available actions:
|
|
448
|
+
"Available actions: get_latest_commit_message, download_path_from_branch.")
|
|
436
449
|
parser.add_argument(
|
|
437
450
|
'-t', '--target_directory', type=str, default=None,
|
|
438
451
|
help='The target directory to download the file/folder.'
|
|
@@ -441,8 +454,11 @@ def parse_github_args():
|
|
|
441
454
|
'--pat', type=str, default=None,
|
|
442
455
|
help='The personal access token to the repo.')
|
|
443
456
|
parser.add_argument(
|
|
444
|
-
'-
|
|
445
|
-
help='Sets if the latest commit comment will be printed.')
|
|
457
|
+
'-glcm', '--get_latest_commit_message', action='store_true', default=False,
|
|
458
|
+
help='Sets if the latest commit message comment will be printed.')
|
|
459
|
+
parser.add_argument(
|
|
460
|
+
'-glcj', '--get_latest_commit_json', action='store_true', default=False,
|
|
461
|
+
help='Sets if the latest commit json will be printed.')
|
|
446
462
|
parser.add_argument(
|
|
447
463
|
'-db', '--download_branch', action='store_true', default=False,
|
|
448
464
|
help='Sets if the branch will be downloaded. In conjunction with path, only the path will be downloaded.')
|
|
@@ -456,7 +472,8 @@ def github_wrapper_main(
|
|
|
456
472
|
path: str = None,
|
|
457
473
|
target_directory: str = None,
|
|
458
474
|
pat: str = None,
|
|
459
|
-
|
|
475
|
+
get_latest_commit_json: bool = False,
|
|
476
|
+
get_latest_commit_message: bool = False,
|
|
460
477
|
download_branch: bool = False
|
|
461
478
|
):
|
|
462
479
|
"""
|
|
@@ -467,7 +484,8 @@ def github_wrapper_main(
|
|
|
467
484
|
:param path: str, the path to the file/folder for which the commit message should be retrieved.
|
|
468
485
|
:param target_directory: str, the target directory to download the file/folder.
|
|
469
486
|
:param pat: str, the personal access token to the repo.
|
|
470
|
-
:param
|
|
487
|
+
:param get_latest_commit_json: bool, sets if the latest commit json will be printed.
|
|
488
|
+
:param get_latest_commit_message: bool, sets if the latest commit message comment will be printed.
|
|
471
489
|
:param download_branch: bool, sets if the branch will be downloaded. In conjunction with path, only the path will be
|
|
472
490
|
downloaded.
|
|
473
491
|
:return:
|
|
@@ -475,11 +493,16 @@ def github_wrapper_main(
|
|
|
475
493
|
|
|
476
494
|
git_wrapper = GitHubWrapper(repo_url=repo_url, branch=branch, path=path, pat=pat)
|
|
477
495
|
|
|
478
|
-
if
|
|
479
|
-
commit_comment = git_wrapper.
|
|
496
|
+
if get_latest_commit_message:
|
|
497
|
+
commit_comment = git_wrapper.get_latest_commit_message()
|
|
480
498
|
print_api(commit_comment)
|
|
481
499
|
return 0
|
|
482
500
|
|
|
501
|
+
if get_latest_commit_json:
|
|
502
|
+
latest_commit_json = git_wrapper.get_latest_commit()
|
|
503
|
+
print_api(latest_commit_json)
|
|
504
|
+
return 0
|
|
505
|
+
|
|
483
506
|
if download_branch:
|
|
484
507
|
git_wrapper.download_and_extract_branch(
|
|
485
508
|
target_directory=target_directory, download_each_file=False, archive_remove_first_directory=True)
|
|
@@ -496,6 +519,7 @@ def github_wrapper_main_with_args():
|
|
|
496
519
|
path=args.path,
|
|
497
520
|
target_directory=args.target_directory,
|
|
498
521
|
pat=args.pat,
|
|
499
|
-
|
|
522
|
+
get_latest_commit_json=args.get_latest_commit_json,
|
|
523
|
+
get_latest_commit_message=args.get_latest_commit_message,
|
|
500
524
|
download_branch=args.download_branch
|
|
501
525
|
)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import re
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
|
|
5
|
+
# ── regex helpers ─────────────────────────────────────────────────────────
|
|
6
|
+
IP_PATTERN = r'(?:\d{1,3}\.){3}\d{1,3}'
|
|
7
|
+
RE_ADAPTER_HEADER = re.compile(r'Configuration for interface +"([^"]+)"', re.I)
|
|
8
|
+
RE_NUMERIC = re.compile(r'\d+')
|
|
9
|
+
RE_SUBNET = re.compile(rf'(?P<prefix>{IP_PATTERN}/\d+)\s+\(mask\s+(?P<mask>{IP_PATTERN})', re.I)
|
|
10
|
+
RE_IP = re.compile(IP_PATTERN)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _get_netsh_show_config() -> str:
|
|
14
|
+
"""Run `netsh interface ipv4 show config` and return the raw text."""
|
|
15
|
+
return subprocess.check_output(
|
|
16
|
+
["netsh", "interface", "ipv4", "show", "config"],
|
|
17
|
+
text=True, encoding="utf-8", errors="ignore"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_netsh_ipv4() -> List[Dict[str, Any]]:
|
|
22
|
+
"""
|
|
23
|
+
Parse *all* data from `netsh interface ipv4 show config`.
|
|
24
|
+
|
|
25
|
+
Returns a list of dicts – one per adapter – with keys:
|
|
26
|
+
interface, dhcp_enabled, ip_addresses, subnet_prefixes, subnet_masks,
|
|
27
|
+
default_gateways, gateway_metric, interface_metric,
|
|
28
|
+
dns_mode, dns_servers, wins_mode, wins_servers
|
|
29
|
+
"""
|
|
30
|
+
config_text = _get_netsh_show_config()
|
|
31
|
+
|
|
32
|
+
adapters: List[Dict[str, Any]] = []
|
|
33
|
+
adapter: Dict[str, Any] | None = None
|
|
34
|
+
|
|
35
|
+
# Track whether we’re in continuation lines of DNS / WINS lists
|
|
36
|
+
dns_list_type: str | None = None # 'static' | 'dynamic' | None
|
|
37
|
+
wins_list_type: str | None = None
|
|
38
|
+
|
|
39
|
+
for raw_line in config_text.splitlines():
|
|
40
|
+
line = raw_line.strip()
|
|
41
|
+
|
|
42
|
+
# 1) New adapter block ------------------------------------------------
|
|
43
|
+
header_match = RE_ADAPTER_HEADER.search(line)
|
|
44
|
+
if header_match:
|
|
45
|
+
# Flush the previous adapter, if any
|
|
46
|
+
if adapter:
|
|
47
|
+
adapters.append(adapter)
|
|
48
|
+
|
|
49
|
+
iface_name = header_match.group(1)
|
|
50
|
+
adapter = {
|
|
51
|
+
'interface_name' : iface_name,
|
|
52
|
+
'dhcp_enabled' : None,
|
|
53
|
+
'gateway_metric' : None,
|
|
54
|
+
'interface_metric' : None,
|
|
55
|
+
'dns_mode' : 'unknown',
|
|
56
|
+
'wins_mode' : 'unknown',
|
|
57
|
+
'dns_servers' : [],
|
|
58
|
+
'wins_servers' : [],
|
|
59
|
+
'ip_addresses' : [],
|
|
60
|
+
'subnet_prefixes' : [],
|
|
61
|
+
'subnet_masks' : [],
|
|
62
|
+
'default_gateways' : [],
|
|
63
|
+
}
|
|
64
|
+
dns_list_type = wins_list_type = None
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
if adapter is None: # skip prologue lines
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
# 2) DHCP flag -------------------------------------------------------
|
|
71
|
+
if line.startswith("DHCP enabled"):
|
|
72
|
+
adapter['dhcp_enabled'] = "yes" in line.lower()
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
# 3) IP addresses ----------------------------------------------------
|
|
76
|
+
if line.startswith("IP Address"):
|
|
77
|
+
adapter['ip_addresses'].extend(RE_IP.findall(line))
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
# 4) Subnet prefix & mask -------------------------------------------
|
|
81
|
+
if line.startswith("Subnet Prefix"):
|
|
82
|
+
subnet_match = RE_SUBNET.search(line)
|
|
83
|
+
if subnet_match:
|
|
84
|
+
adapter['subnet_prefixes'].append(subnet_match.group('prefix'))
|
|
85
|
+
adapter['subnet_masks'].append(subnet_match.group('mask'))
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
# 5) Gateway & metrics ----------------------------------------------
|
|
89
|
+
if line.startswith("Default Gateway"):
|
|
90
|
+
adapter['default_gateways'].extend(RE_IP.findall(line))
|
|
91
|
+
continue
|
|
92
|
+
if line.startswith("Gateway Metric"):
|
|
93
|
+
metric = RE_NUMERIC.search(line)
|
|
94
|
+
if metric:
|
|
95
|
+
adapter['gateway_metric'] = int(metric.group())
|
|
96
|
+
continue
|
|
97
|
+
if line.startswith("InterfaceMetric"):
|
|
98
|
+
metric = RE_NUMERIC.search(line)
|
|
99
|
+
if metric:
|
|
100
|
+
adapter['interface_metric'] = int(metric.group())
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
# 6) DNS header lines -----------------------------------------------
|
|
104
|
+
if "DNS servers configured through DHCP" in line:
|
|
105
|
+
adapter['dns_mode'] = 'dynamic'
|
|
106
|
+
adapter['dns_servers'].extend(RE_IP.findall(line))
|
|
107
|
+
dns_list_type = 'dynamic'
|
|
108
|
+
continue
|
|
109
|
+
if "Statically Configured DNS Servers" in line:
|
|
110
|
+
adapter['dns_mode'] = 'static'
|
|
111
|
+
adapter['dns_servers'].extend(RE_IP.findall(line))
|
|
112
|
+
dns_list_type = 'static'
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
# 7) WINS header lines ----------------------------------------------
|
|
116
|
+
if "WINS servers configured through DHCP" in line:
|
|
117
|
+
adapter['wins_mode'] = 'dynamic'
|
|
118
|
+
adapter['wins_servers'].extend(RE_IP.findall(line))
|
|
119
|
+
wins_list_type = 'dynamic'
|
|
120
|
+
continue
|
|
121
|
+
if line.startswith(("Primary WINS Server", "Secondary WINS Server")):
|
|
122
|
+
adapter['wins_mode'] = 'static'
|
|
123
|
+
adapter['wins_servers'].extend(RE_IP.findall(line))
|
|
124
|
+
wins_list_type = 'static'
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
# 8) Continuation lines for DNS / WINS -------------------------------
|
|
128
|
+
if dns_list_type and RE_IP.search(line):
|
|
129
|
+
adapter['dns_servers'].extend(RE_IP.findall(line))
|
|
130
|
+
continue
|
|
131
|
+
if wins_list_type and RE_IP.search(line):
|
|
132
|
+
adapter['wins_servers'].extend(RE_IP.findall(line))
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
# Flush the final adapter block
|
|
136
|
+
if adapter:
|
|
137
|
+
adapters.append(adapter)
|
|
138
|
+
|
|
139
|
+
# # ── post-process: detect “mixed” modes ----------------------------------
|
|
140
|
+
# NOT SURE THIS PART WORKS AS INTENDED!!!
|
|
141
|
+
# for ad in adapters:
|
|
142
|
+
# if ad['dns_mode'] == 'dynamic' and ad['dns_servers']:
|
|
143
|
+
# # If both headers appeared the last one wins; treat that as mixed
|
|
144
|
+
# if any(k in ad['dns_servers'] for k in ad['default_gateways']):
|
|
145
|
+
# ad['dns_mode'] = 'mixed'
|
|
146
|
+
# if ad['wins_mode'] == 'dynamic' and ad['wins_servers']:
|
|
147
|
+
# if any(ip not in ad['wins_servers'] for ip in ad['wins_servers']):
|
|
148
|
+
# ad['wins_mode'] = 'mixed'
|
|
149
|
+
|
|
150
|
+
return adapters
|
|
@@ -137,8 +137,13 @@ def get_network_connections_details(get_enum_info: bool = True) -> dict:
|
|
|
137
137
|
return adapter_details
|
|
138
138
|
|
|
139
139
|
|
|
140
|
-
def
|
|
140
|
+
def _get_default_dns_gateway() -> tuple[bool, list[str]]:
|
|
141
141
|
"""
|
|
142
|
+
NOTICE: This stopped working from the last Windows update on 11.06.2025.
|
|
143
|
+
They moved it to 'ProfileNameServer', anyway Since Windows 8 the recommended API has been the WMI
|
|
144
|
+
NetTCPIP CIM provider (MSFT_DNSClientServerAddress) - didn't test it though.
|
|
145
|
+
Just moved to netsh wrapping for now.
|
|
146
|
+
|
|
142
147
|
Get the default DNS gateway from the Windows registry.
|
|
143
148
|
|
|
144
149
|
:return: tuple(is dynamic boolean, list of DNS server IPv4s).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=4UwVcmMtjfVbDhhZfkGYNJuONJup1-ZKYf0TXOv0NFU,123
|
|
2
2
|
atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
|
|
3
3
|
atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
|
|
4
4
|
atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
|
|
@@ -10,7 +10,7 @@ atomicshop/console_output.py,sha256=AOSJjrRryE97PAGtgDL03IBtWSi02aNol8noDnW3k6M,
|
|
|
10
10
|
atomicshop/console_user_response.py,sha256=OHcjuzWAys6WmfRnMIU_nkJA634kKmJh6T8w1VtUTJM,2714
|
|
11
11
|
atomicshop/datetimes.py,sha256=IQZ66lmta-ZqxYbyHzm_9eugbJFSilXK1e0kfMgoXGg,18371
|
|
12
12
|
atomicshop/diff_check.py,sha256=vxTDccVbGZHEge6Ja9_ArLWwslOUgIoJAdYPylh4cZg,27176
|
|
13
|
-
atomicshop/dns.py,sha256=
|
|
13
|
+
atomicshop/dns.py,sha256=XB0tijVi1bxWLWKV9hPzpt75jK4SrGbZCV5VJbiiQ74,7185
|
|
14
14
|
atomicshop/domains.py,sha256=Rxu6JhhMqFZRcoFs69IoEd1PtYca0lMCG6F1AomP7z4,3197
|
|
15
15
|
atomicshop/emails.py,sha256=I0KyODQpIMEsNRi9YWSOL8EUPBiWyon3HRdIuSj3AEU,1410
|
|
16
16
|
atomicshop/file_types.py,sha256=-0jzQMRlmU1AP9DARjk-HJm1tVE22E6ngP2mRblyEjY,763
|
|
@@ -197,8 +197,9 @@ atomicshop/wrappers/astw.py,sha256=VkYfkfyc_PJLIOxByT6L7B8uUmKY6-I8XGZl4t_z828,4
|
|
|
197
197
|
atomicshop/wrappers/configparserw.py,sha256=JwDTPjZoSrv44YKwIRcjyUnpN-FjgXVfMqMK_tJuSgU,22800
|
|
198
198
|
atomicshop/wrappers/cryptographyw.py,sha256=LfzTnwvJE03G6WZryOOf43VKhhnyMakzHpn8DPPCoy4,13252
|
|
199
199
|
atomicshop/wrappers/ffmpegw.py,sha256=wcq0ZnAe0yajBOuTKZCCaKI7CDBjkq7FAgdW5IsKcVE,6031
|
|
200
|
-
atomicshop/wrappers/githubw.py,sha256=
|
|
200
|
+
atomicshop/wrappers/githubw.py,sha256=DrFF_oN-rulPQV1iKgVzZadCjuYuCC5eKAjZp_3YD0g,23476
|
|
201
201
|
atomicshop/wrappers/msiw.py,sha256=GQLqud72nfex3kvO1bJSruNriCYTYX1_G1gSf1MPkIA,6118
|
|
202
|
+
atomicshop/wrappers/netshw.py,sha256=8WE_576XiiHykwFuE-VkCx5CydMpFlztX4frlEteCtI,6350
|
|
202
203
|
atomicshop/wrappers/numpyw.py,sha256=sBV4gSKyr23kXTalqAb1oqttzE_2XxBooCui66jbAqc,1025
|
|
203
204
|
atomicshop/wrappers/olefilew.py,sha256=biD5m58rogifCYmYhJBrAFb9O_Bn_spLek_9HofLeYE,2051
|
|
204
205
|
atomicshop/wrappers/pipw.py,sha256=mu4jnHkSaYNfpBiLZKMZxEX_E2LqW5BVthMZkblPB_c,1317
|
|
@@ -335,9 +336,9 @@ atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0L
|
|
|
335
336
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=WcNyaqEZ82S5-f3kzqi1nllNT2Nd2P_zg8HqCc7vW4s,4120
|
|
336
337
|
atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
337
338
|
atomicshop/wrappers/winregw/winreg_installed_software.py,sha256=Qzmyktvob1qp6Tjk2DjLfAqr_yXV0sgWzdMW_9kwNjY,2345
|
|
338
|
-
atomicshop/wrappers/winregw/winreg_network.py,sha256=
|
|
339
|
-
atomicshop-3.1.
|
|
340
|
-
atomicshop-3.1.
|
|
341
|
-
atomicshop-3.1.
|
|
342
|
-
atomicshop-3.1.
|
|
343
|
-
atomicshop-3.1.
|
|
339
|
+
atomicshop/wrappers/winregw/winreg_network.py,sha256=ih0BVNwByLvf9F_Lac4EdmDYYJA3PzMvmG0PieDZrsE,9905
|
|
340
|
+
atomicshop-3.1.10.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
341
|
+
atomicshop-3.1.10.dist-info/METADATA,sha256=leFjMfEKSkTJWSkmGTX8QxNL2t-j2IXQc4yvijOi6YY,10663
|
|
342
|
+
atomicshop-3.1.10.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
343
|
+
atomicshop-3.1.10.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
344
|
+
atomicshop-3.1.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|