fmtr.tools 1.3.16__py3-none-any.whl → 1.3.18__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 fmtr.tools might be problematic. Click here for more details.
- fmtr/tools/dns_tools/dm.py +12 -3
- fmtr/tools/dns_tools/proxy.py +1 -1
- fmtr/tools/dns_tools/server.py +37 -39
- fmtr/tools/version +1 -1
- {fmtr_tools-1.3.16.dist-info → fmtr_tools-1.3.18.dist-info}/METADATA +44 -44
- {fmtr_tools-1.3.16.dist-info → fmtr_tools-1.3.18.dist-info}/RECORD +10 -10
- {fmtr_tools-1.3.16.dist-info → fmtr_tools-1.3.18.dist-info}/WHEEL +0 -0
- {fmtr_tools-1.3.16.dist-info → fmtr_tools-1.3.18.dist-info}/entry_points.txt +0 -0
- {fmtr_tools-1.3.16.dist-info → fmtr_tools-1.3.18.dist-info}/licenses/LICENSE +0 -0
- {fmtr_tools-1.3.16.dist-info → fmtr_tools-1.3.18.dist-info}/top_level.txt +0 -0
fmtr/tools/dns_tools/dm.py
CHANGED
|
@@ -179,6 +179,9 @@ class Exchange:
|
|
|
179
179
|
client_name: Optional[str] = None
|
|
180
180
|
is_complete: bool = False
|
|
181
181
|
|
|
182
|
+
@property
|
|
183
|
+
def addr(self):
|
|
184
|
+
return self.ip, self.port
|
|
182
185
|
|
|
183
186
|
@classmethod
|
|
184
187
|
def from_wire(cls, wire: bytes, **kwargs) -> Self:
|
|
@@ -194,11 +197,17 @@ class Exchange:
|
|
|
194
197
|
def question_last(self) -> RRset:
|
|
195
198
|
"""
|
|
196
199
|
|
|
197
|
-
Create an RRset surrogate representing the latest/current question.
|
|
200
|
+
Create an RRset surrogate representing the latest/current question.
|
|
201
|
+
This can be the original question - or a hybrid one if we've injected our own answers into the Exchange.
|
|
202
|
+
If there's a response, use its answers, else fall back to answers_pre, else to the original question.
|
|
198
203
|
|
|
199
204
|
"""
|
|
200
|
-
|
|
201
|
-
|
|
205
|
+
answers = self.answers_pre
|
|
206
|
+
if self.response:
|
|
207
|
+
answers = self.response.message.answer or answers
|
|
208
|
+
|
|
209
|
+
if answers:
|
|
210
|
+
rrset = answers[-1]
|
|
202
211
|
rdtype = self.request.type
|
|
203
212
|
ttl = self.request.question.ttl
|
|
204
213
|
rdclass = self.request.question.rdclass
|
fmtr/tools/dns_tools/proxy.py
CHANGED
|
@@ -39,7 +39,7 @@ class Proxy(server.Plain):
|
|
|
39
39
|
"""
|
|
40
40
|
exchange.is_complete = True
|
|
41
41
|
|
|
42
|
-
def resolve(self, exchange: Exchange) -> Exchange:
|
|
42
|
+
async def resolve(self, exchange: Exchange) -> Exchange:
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
45
|
Resolve a request, processing each stage, initial question, upstream response etc.
|
fmtr/tools/dns_tools/server.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
from dataclasses import dataclass
|
|
1
|
+
import asyncio
|
|
2
|
+
import dns.rcode
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
4
|
from datetime import timedelta
|
|
5
5
|
from functools import cached_property
|
|
6
|
+
from typing import Optional
|
|
6
7
|
|
|
7
8
|
from fmtr.tools import caching_tools as caching
|
|
8
9
|
from fmtr.tools.dns_tools.dm import Exchange
|
|
@@ -10,59 +11,61 @@ from fmtr.tools.logging_tools import logger
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@dataclass(kw_only=True, eq=False)
|
|
13
|
-
class Plain:
|
|
14
|
+
class Plain(asyncio.DatagramProtocol):
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
Async base class for a plain DNS server using asyncio DatagramProtocol.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
host: str
|
|
21
21
|
port: int
|
|
22
|
+
transport: Optional[asyncio.DatagramTransport] = field(default=None, init=False)
|
|
22
23
|
|
|
23
24
|
@cached_property
|
|
24
|
-
def
|
|
25
|
-
return
|
|
25
|
+
def loop(self):
|
|
26
|
+
return asyncio.get_event_loop()
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
@cached_property
|
|
28
30
|
def cache(self):
|
|
29
31
|
"""
|
|
30
32
|
|
|
31
33
|
Overridable cache.
|
|
32
|
-
|
|
33
34
|
"""
|
|
34
35
|
cache = caching.TLRU(maxsize=1_024, ttu_static=timedelta(hours=1), desc='DNS Request')
|
|
35
36
|
return cache
|
|
36
37
|
|
|
37
|
-
def
|
|
38
|
-
|
|
38
|
+
def connection_made(self, transport: asyncio.DatagramTransport):
|
|
39
|
+
self.transport = transport
|
|
40
|
+
logger.info(f'Listening on {self.host}:{self.port}')
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
def datagram_received(self, data: bytes, addr):
|
|
43
|
+
ip, port = addr
|
|
44
|
+
exchange = Exchange.from_wire(data, ip=ip, port=port)
|
|
45
|
+
asyncio.create_task(self.handle(exchange))
|
|
41
46
|
|
|
47
|
+
async def start(self):
|
|
42
48
|
"""
|
|
43
|
-
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
44
|
-
sock.bind((self.host, self.port))
|
|
45
|
-
logger.info(f'Listening on {self.host}:{self.port}')
|
|
46
|
-
while True:
|
|
47
|
-
data, (ip, port) = sock.recvfrom(512)
|
|
48
|
-
exchange = Exchange.from_wire(data, ip=ip, port=port)
|
|
49
|
-
self.handle(exchange)
|
|
50
|
-
sock.sendto(exchange.response.message.to_wire(), (ip, port))
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
Start the async UDP server.
|
|
53
51
|
"""
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
logger.info(f'Starting async DNS server on {self.host}:{self.port}...')
|
|
54
|
+
await self.loop.create_datagram_endpoint(
|
|
55
|
+
lambda: self,
|
|
56
|
+
local_addr=(self.host, self.port)
|
|
57
|
+
)
|
|
58
|
+
await asyncio.Future() # Prevent exit by blocking forever
|
|
56
59
|
|
|
60
|
+
async def resolve(self, exchange: Exchange) -> Exchange:
|
|
57
61
|
"""
|
|
58
|
-
raise NotImplemented
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
Check cache, patch in in new ID and mark complete
|
|
63
|
+
To be defined in subclasses.
|
|
64
64
|
|
|
65
65
|
"""
|
|
66
|
+
raise NotImplementedError
|
|
67
|
+
|
|
68
|
+
def check_cache(self, exchange: Exchange):
|
|
66
69
|
if exchange.key in self.cache:
|
|
67
70
|
logger.info(f'Request found in cache.')
|
|
68
71
|
exchange.response = self.cache[exchange.key]
|
|
@@ -89,7 +92,6 @@ class Plain:
|
|
|
89
92
|
"""
|
|
90
93
|
request = exchange.request
|
|
91
94
|
response = exchange.response
|
|
92
|
-
|
|
93
95
|
logger.info(
|
|
94
96
|
f'Resolution complete {exchange.client_name=} {request.message.id=} {request.type_text} {request.name_text} {request.question=} {exchange.is_complete=} {response.rcode=} {response.rcode_text=} {response.answer=} {response.blocked_by=}...'
|
|
95
97
|
)
|
|
@@ -100,24 +102,20 @@ class Plain:
|
|
|
100
102
|
Warn about any errors
|
|
101
103
|
|
|
102
104
|
"""
|
|
105
|
+
if exchange.response.rcode != dns.rcode.NOERROR:
|
|
106
|
+
logger.warning(f'Error {exchange.response.rcode_text=}')
|
|
103
107
|
|
|
104
|
-
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
logger.warning(f'Error {exchange.response.rcode_text=}')
|
|
108
|
-
|
|
109
|
-
def handle(self, exchange: Exchange):
|
|
108
|
+
async def handle(self, exchange: Exchange):
|
|
110
109
|
"""
|
|
111
110
|
|
|
112
|
-
|
|
111
|
+
Warn about any errors
|
|
113
112
|
|
|
114
113
|
"""
|
|
115
|
-
|
|
116
114
|
if not exchange.request.is_valid:
|
|
117
115
|
raise ValueError(f'Only one question per request is supported. Got {len(exchange.request.question)} questions.')
|
|
118
116
|
|
|
119
117
|
if not exchange.is_internal:
|
|
120
|
-
self.handle(exchange.reverse)
|
|
118
|
+
await self.handle(exchange.reverse)
|
|
121
119
|
client_name = exchange.reverse.question_last.name.to_text()
|
|
122
120
|
if not exchange.reverse.response.answer:
|
|
123
121
|
logger.warning(f'Client name could not be resolved {client_name=}.')
|
|
@@ -128,10 +126,10 @@ class Plain:
|
|
|
128
126
|
self.check_cache(exchange)
|
|
129
127
|
|
|
130
128
|
if not exchange.is_complete:
|
|
131
|
-
exchange = self.resolve(exchange)
|
|
129
|
+
exchange = await self.resolve(exchange)
|
|
132
130
|
self.cache[exchange.key] = exchange.response
|
|
133
131
|
|
|
134
132
|
self.log_dns_errors(exchange)
|
|
135
133
|
self.log_response(exchange)
|
|
136
134
|
|
|
137
|
-
|
|
135
|
+
self.transport.sendto(exchange.response.message.to_wire(), exchange.addr)
|
fmtr/tools/version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.3.
|
|
1
|
+
1.3.18
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.18
|
|
4
4
|
Summary: Collection of high-level tools to simplify everyday development tasks, with a focus on AI/ML
|
|
5
5
|
Home-page: https://github.com/fmtr/fmtr.tools
|
|
6
6
|
Author: Frontmatter
|
|
@@ -126,61 +126,61 @@ Requires-Dist: logfire[httpx]; extra == "http"
|
|
|
126
126
|
Provides-Extra: setup
|
|
127
127
|
Requires-Dist: setuptools; extra == "setup"
|
|
128
128
|
Provides-Extra: all
|
|
129
|
-
Requires-Dist:
|
|
130
|
-
Requires-Dist:
|
|
131
|
-
Requires-Dist:
|
|
132
|
-
Requires-Dist:
|
|
133
|
-
Requires-Dist: appdirs; extra == "all"
|
|
134
|
-
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
135
|
-
Requires-Dist: openai; extra == "all"
|
|
136
|
-
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
129
|
+
Requires-Dist: flet[all]; extra == "all"
|
|
130
|
+
Requires-Dist: httpx_retries; extra == "all"
|
|
131
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
132
|
+
Requires-Dist: bokeh; extra == "all"
|
|
137
133
|
Requires-Dist: docker; extra == "all"
|
|
138
|
-
Requires-Dist:
|
|
139
|
-
Requires-Dist:
|
|
140
|
-
Requires-Dist:
|
|
141
|
-
Requires-Dist:
|
|
142
|
-
Requires-Dist: filetype; extra == "all"
|
|
134
|
+
Requires-Dist: json_repair; extra == "all"
|
|
135
|
+
Requires-Dist: regex; extra == "all"
|
|
136
|
+
Requires-Dist: appdirs; extra == "all"
|
|
137
|
+
Requires-Dist: pydantic; extra == "all"
|
|
143
138
|
Requires-Dist: tokenizers; extra == "all"
|
|
144
|
-
Requires-Dist: logfire[
|
|
145
|
-
Requires-Dist:
|
|
146
|
-
Requires-Dist:
|
|
139
|
+
Requires-Dist: logfire[httpx]; extra == "all"
|
|
140
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
141
|
+
Requires-Dist: google-auth; extra == "all"
|
|
142
|
+
Requires-Dist: html2text; extra == "all"
|
|
143
|
+
Requires-Dist: Unidecode; extra == "all"
|
|
144
|
+
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
145
|
+
Requires-Dist: yamlscript; extra == "all"
|
|
147
146
|
Requires-Dist: openpyxl; extra == "all"
|
|
148
|
-
Requires-Dist: fastapi; extra == "all"
|
|
149
|
-
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
150
|
-
Requires-Dist: peft; extra == "all"
|
|
151
147
|
Requires-Dist: pymupdf; extra == "all"
|
|
152
|
-
Requires-Dist:
|
|
153
|
-
Requires-Dist: httpx_retries; extra == "all"
|
|
154
|
-
Requires-Dist: json_repair; extra == "all"
|
|
155
|
-
Requires-Dist: ollama; extra == "all"
|
|
156
|
-
Requires-Dist: html2text; extra == "all"
|
|
148
|
+
Requires-Dist: openai; extra == "all"
|
|
157
149
|
Requires-Dist: dnspython[doh]; extra == "all"
|
|
158
|
-
Requires-Dist:
|
|
159
|
-
Requires-Dist:
|
|
160
|
-
Requires-Dist:
|
|
161
|
-
Requires-Dist:
|
|
162
|
-
Requires-Dist: deepmerge; extra == "all"
|
|
163
|
-
Requires-Dist: google-auth; extra == "all"
|
|
150
|
+
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
151
|
+
Requires-Dist: pandas; extra == "all"
|
|
152
|
+
Requires-Dist: setuptools; extra == "all"
|
|
153
|
+
Requires-Dist: fastapi; extra == "all"
|
|
164
154
|
Requires-Dist: sentence_transformers; extra == "all"
|
|
165
|
-
Requires-Dist:
|
|
166
|
-
Requires-Dist:
|
|
167
|
-
Requires-Dist: distributed; extra == "all"
|
|
155
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
156
|
+
Requires-Dist: torchaudio; extra == "all"
|
|
168
157
|
Requires-Dist: dask[bag]; extra == "all"
|
|
158
|
+
Requires-Dist: sre_yield; extra == "all"
|
|
159
|
+
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
160
|
+
Requires-Dist: deepmerge; extra == "all"
|
|
169
161
|
Requires-Dist: huggingface_hub; extra == "all"
|
|
170
|
-
Requires-Dist: pydantic; extra == "all"
|
|
171
|
-
Requires-Dist:
|
|
172
|
-
Requires-Dist:
|
|
173
|
-
Requires-Dist:
|
|
174
|
-
Requires-Dist:
|
|
162
|
+
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
163
|
+
Requires-Dist: flet-video; extra == "all"
|
|
164
|
+
Requires-Dist: pymupdf4llm; extra == "all"
|
|
165
|
+
Requires-Dist: flet-webview; extra == "all"
|
|
166
|
+
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
175
167
|
Requires-Dist: semver; extra == "all"
|
|
176
|
-
Requires-Dist:
|
|
177
|
-
Requires-Dist:
|
|
168
|
+
Requires-Dist: distributed; extra == "all"
|
|
169
|
+
Requires-Dist: pydantic-settings; extra == "all"
|
|
178
170
|
Requires-Dist: tabulate; extra == "all"
|
|
179
|
-
Requires-Dist: diskcache; extra == "all"
|
|
180
171
|
Requires-Dist: cachetools; extra == "all"
|
|
172
|
+
Requires-Dist: google-api-python-client; extra == "all"
|
|
173
|
+
Requires-Dist: tinynetrc; extra == "all"
|
|
174
|
+
Requires-Dist: peft; extra == "all"
|
|
175
|
+
Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
|
|
176
|
+
Requires-Dist: faker; extra == "all"
|
|
177
|
+
Requires-Dist: torchvision; extra == "all"
|
|
178
|
+
Requires-Dist: ollama; extra == "all"
|
|
181
179
|
Requires-Dist: httpx; extra == "all"
|
|
182
|
-
Requires-Dist:
|
|
183
|
-
Requires-Dist:
|
|
180
|
+
Requires-Dist: logfire; extra == "all"
|
|
181
|
+
Requires-Dist: diskcache; extra == "all"
|
|
182
|
+
Requires-Dist: filetype; extra == "all"
|
|
183
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
184
184
|
Dynamic: author
|
|
185
185
|
Dynamic: author-email
|
|
186
186
|
Dynamic: description
|
|
@@ -44,16 +44,16 @@ fmtr/tools/tabular_tools.py,sha256=tpIpZzYku1HcJrHZJL6BC39LmN3WUWVhFbK2N7nDVmE,1
|
|
|
44
44
|
fmtr/tools/tokenization_tools.py,sha256=me-IBzSLyNYejLybwjO9CNB6Mj2NYfKPaOVThXyaGNg,4268
|
|
45
45
|
fmtr/tools/tools.py,sha256=CAsApa1YwVdNE6H66Vjivs_mXYvOas3rh7fPELAnTpk,795
|
|
46
46
|
fmtr/tools/unicode_tools.py,sha256=yS_9wpu8ogNoiIL7s1G_8bETFFO_YQlo4LNPv1NLDeY,52
|
|
47
|
-
fmtr/tools/version,sha256=
|
|
47
|
+
fmtr/tools/version,sha256=1HhblnKFU7qvP6wf1fM-3EpDM8YGR1sNW7I8m0TTfYc,6
|
|
48
48
|
fmtr/tools/yaml_tools.py,sha256=Bhhyd6GQVKO72Lp8ky7bAUjIB_65Hdh0Q45SKIEe6S8,1901
|
|
49
49
|
fmtr/tools/ai_tools/__init__.py,sha256=JZrLuOFNV1A3wvJgonxOgz_4WS-7MfCuowGWA5uYCjs,372
|
|
50
50
|
fmtr/tools/ai_tools/agentic_tools.py,sha256=acSEPFS-aguDXanWGs3fAAlRyJSYPZW7L-Kb2qDLm-I,4300
|
|
51
51
|
fmtr/tools/ai_tools/inference_tools.py,sha256=2UP2gXEyOJUjyyV6zmFIYmIxUsh1rXkRH0IbFvr2bRs,11908
|
|
52
52
|
fmtr/tools/dns_tools/__init__.py,sha256=PjD3Og6D5yvDVpKmsUsrnSpz_rjXpl4zBtvMqm8xKWU,237
|
|
53
53
|
fmtr/tools/dns_tools/client.py,sha256=uUFoFRwoPtetPVH6PZ3ssHmx3WEquT6UW4FCBKq4n74,2722
|
|
54
|
-
fmtr/tools/dns_tools/dm.py,sha256=
|
|
55
|
-
fmtr/tools/dns_tools/proxy.py,sha256=
|
|
56
|
-
fmtr/tools/dns_tools/server.py,sha256=
|
|
54
|
+
fmtr/tools/dns_tools/dm.py,sha256=DOXcySjcnwviz8tQXjl34W_hz1bh0ue-ew7aNylvOfo,6553
|
|
55
|
+
fmtr/tools/dns_tools/proxy.py,sha256=gCWfH1pyWjj8xnJ8Pm4id7bsBWqcar3dQ9PeP5XjRXY,1624
|
|
56
|
+
fmtr/tools/dns_tools/server.py,sha256=7XqG2csuYjXmsEzcHVCd5LBytXkefCPiK3HCRA5v1zQ,4180
|
|
57
57
|
fmtr/tools/entrypoints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
58
|
fmtr/tools/entrypoints/cache_hfh.py,sha256=fQNs4J9twQuZH_Yj98-oOvEX7-LrSUP3kO8nzw2HrHs,60
|
|
59
59
|
fmtr/tools/entrypoints/ep_test.py,sha256=B8HfWISfSgw_xVX475CbJGh_QnpOe9MH65H8qGjTWbY,46
|
|
@@ -76,9 +76,9 @@ fmtr/tools/tests/test_path.py,sha256=AkZQa6_8BQ-VaCyL_J-iKmdf2ZaM-xFYR37Kun3k4_g
|
|
|
76
76
|
fmtr/tools/tests/test_yaml.py,sha256=jc0TwwKu9eC0LvFGNMERdgBue591xwLxYXFbtsRwXVM,287
|
|
77
77
|
fmtr/tools/version_tools/__init__.py,sha256=pg4iLtmIr5HtyEW_j0fMFoIdzqi_w9xH8-grQaXLB28,318
|
|
78
78
|
fmtr/tools/version_tools/version_tools.py,sha256=Hcc6yferZS1hHbugRTdiHhSNmXEEG0hjCiTTXKna-YY,1127
|
|
79
|
-
fmtr_tools-1.3.
|
|
80
|
-
fmtr_tools-1.3.
|
|
81
|
-
fmtr_tools-1.3.
|
|
82
|
-
fmtr_tools-1.3.
|
|
83
|
-
fmtr_tools-1.3.
|
|
84
|
-
fmtr_tools-1.3.
|
|
79
|
+
fmtr_tools-1.3.18.dist-info/licenses/LICENSE,sha256=FW9aa6vVN5IjRQWLT43hs4_koYSmpcbIovlKeAJ0_cI,10757
|
|
80
|
+
fmtr_tools-1.3.18.dist-info/METADATA,sha256=HRh5NKxOfbIObmQOmHgef02WijAE-teUrSEuOPuU4e8,15938
|
|
81
|
+
fmtr_tools-1.3.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
82
|
+
fmtr_tools-1.3.18.dist-info/entry_points.txt,sha256=h-r__Xh5njtFqreMLg6cGuTFS4Qh-QqJPU1HB-_BS-Q,357
|
|
83
|
+
fmtr_tools-1.3.18.dist-info/top_level.txt,sha256=LXem9xCgNOD72tE2gRKESdiQTL902mfFkwWb6-dlwEE,5
|
|
84
|
+
fmtr_tools-1.3.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|