atomicshop 3.1.8__py3-none-any.whl → 3.1.9__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '3.1.8'
4
+ __version__ = '3.1.9'
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.winregw import winreg_network
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
- is_dynamic, dns_servers = winreg_network.get_default_dns_gateway()
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
 
@@ -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 get_default_dns_gateway() -> tuple[bool, list[str]]:
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 3.1.8
3
+ Version: 3.1.9
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=EsaT4vSXGAXzBfTEAtjwOzP6zvlGXekljCTQOrMqSTA,122
1
+ atomicshop/__init__.py,sha256=EQJIgq1QRIHAmPvbH_Qo_f07M3JEn4xL2sUgoWRi8Q4,122
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=FVrXYk-r3zH6HbUyB3wvhhWM-J0RKmd_CCO50TXVq1A,6831
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
@@ -199,6 +199,7 @@ atomicshop/wrappers/cryptographyw.py,sha256=LfzTnwvJE03G6WZryOOf43VKhhnyMakzHpn8
199
199
  atomicshop/wrappers/ffmpegw.py,sha256=wcq0ZnAe0yajBOuTKZCCaKI7CDBjkq7FAgdW5IsKcVE,6031
200
200
  atomicshop/wrappers/githubw.py,sha256=vaVQg0pkHY63_TfIbCoWmHDPtI2rDPAqMYwsOl3GN78,22559
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=3Ts1sVqSUiCDsHRHwJCbiZ9EYvv2ELGxF0Y_pibGU4k,9596
339
- atomicshop-3.1.8.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
340
- atomicshop-3.1.8.dist-info/METADATA,sha256=evAROFKYJtONn4b-_ij9PzDB6jKZW3hMlXGRHP_j4Gk,10662
341
- atomicshop-3.1.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
342
- atomicshop-3.1.8.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
343
- atomicshop-3.1.8.dist-info/RECORD,,
339
+ atomicshop/wrappers/winregw/winreg_network.py,sha256=ih0BVNwByLvf9F_Lac4EdmDYYJA3PzMvmG0PieDZrsE,9905
340
+ atomicshop-3.1.9.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
341
+ atomicshop-3.1.9.dist-info/METADATA,sha256=-CO4-bPxF4SKM5Gi0aC0Sbqkl0s974zx0o5bR5gnBWE,10662
342
+ atomicshop-3.1.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
+ atomicshop-3.1.9.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
344
+ atomicshop-3.1.9.dist-info/RECORD,,