fmtr.tools 1.3.14__py3-none-any.whl → 1.3.16__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/client.py +6 -2
- fmtr/tools/dns_tools/dm.py +44 -15
- fmtr/tools/dns_tools/proxy.py +13 -4
- fmtr/tools/dns_tools/server.py +16 -5
- fmtr/tools/version +1 -1
- {fmtr_tools-1.3.14.dist-info → fmtr_tools-1.3.16.dist-info}/METADATA +44 -44
- {fmtr_tools-1.3.14.dist-info → fmtr_tools-1.3.16.dist-info}/RECORD +11 -11
- {fmtr_tools-1.3.14.dist-info → fmtr_tools-1.3.16.dist-info}/WHEEL +0 -0
- {fmtr_tools-1.3.14.dist-info → fmtr_tools-1.3.16.dist-info}/entry_points.txt +0 -0
- {fmtr_tools-1.3.14.dist-info → fmtr_tools-1.3.16.dist-info}/licenses/LICENSE +0 -0
- {fmtr_tools-1.3.14.dist-info → fmtr_tools-1.3.16.dist-info}/top_level.txt +0 -0
fmtr/tools/dns_tools/client.py
CHANGED
|
@@ -3,6 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from dns import query
|
|
4
4
|
from functools import cached_property
|
|
5
5
|
from httpx_retries import Retry, RetryTransport
|
|
6
|
+
from typing import Optional
|
|
6
7
|
|
|
7
8
|
from fmtr.tools import http_tools as http
|
|
8
9
|
from fmtr.tools.dns_tools.dm import Exchange, Response
|
|
@@ -38,14 +39,17 @@ class Plain:
|
|
|
38
39
|
"""
|
|
39
40
|
host: str
|
|
40
41
|
port: int = 53
|
|
42
|
+
ttl_min: Optional[int] = None
|
|
41
43
|
|
|
42
44
|
def resolve(self, exchange: Exchange):
|
|
43
45
|
|
|
44
46
|
with logger.span(f'UDP {self.host}:{self.port}'):
|
|
45
47
|
response_plain = query.udp(q=exchange.query_last, where=self.host, port=self.port)
|
|
46
48
|
response = Response.from_message(response_plain)
|
|
49
|
+
for answer in response.message.answer:
|
|
50
|
+
answer.ttl = max(answer.ttl, self.ttl_min or answer.ttl)
|
|
47
51
|
|
|
48
|
-
exchange.response
|
|
52
|
+
exchange.response = response
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
@dataclass
|
|
@@ -86,7 +90,7 @@ class HTTP:
|
|
|
86
90
|
response_doh = self.CLIENT.post(url, headers=headers, content=exchange.query_last.to_wire())
|
|
87
91
|
response_doh.raise_for_status()
|
|
88
92
|
response = Response.from_http(response_doh)
|
|
89
|
-
exchange.response
|
|
93
|
+
exchange.response = response
|
|
90
94
|
|
|
91
95
|
except Exception as exception:
|
|
92
96
|
exchange.response.message.set_rcode(dnspython.rcode.SERVFAIL)
|
fmtr/tools/dns_tools/dm.py
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import dns
|
|
2
2
|
import httpx
|
|
3
|
-
from dataclasses import dataclass
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
4
|
from dns import rcode as dnsrcode
|
|
5
5
|
from dns import reversename
|
|
6
6
|
from dns.message import Message, QueryMessage
|
|
7
7
|
from dns.rrset import RRset
|
|
8
8
|
from functools import cached_property
|
|
9
|
-
from typing import Self, Optional
|
|
9
|
+
from typing import Self, Optional, List
|
|
10
10
|
|
|
11
11
|
from fmtr.tools.string_tools import join
|
|
12
12
|
|
|
13
|
+
TTL_CODE_DEFAULTS = {
|
|
14
|
+
dnsrcode.NOERROR: 300, # Successful query
|
|
15
|
+
dnsrcode.FORMERR: 60, # Format error
|
|
16
|
+
dnsrcode.SERVFAIL: 10, # Server failure
|
|
17
|
+
dnsrcode.NXDOMAIN: 60 * 60, # Non-existent domain
|
|
18
|
+
dnsrcode.NOTIMP: 60, # Not implemented
|
|
19
|
+
dnsrcode.REFUSED: 60, # Refused
|
|
20
|
+
dnsrcode.YXDOMAIN: 600, # Name exists when it should not
|
|
21
|
+
dnsrcode.YXRRSET: 600, # RR Set exists when it should not
|
|
22
|
+
dnsrcode.NXRRSET: 300, # RR Set that should exist does not
|
|
23
|
+
dnsrcode.NOTAUTH: 60, # Not authorized
|
|
24
|
+
dnsrcode.NOTZONE: 60 # Name not contained in zone
|
|
25
|
+
}
|
|
13
26
|
|
|
14
27
|
@dataclass
|
|
15
28
|
class BaseDNSData:
|
|
@@ -37,7 +50,6 @@ class Response(BaseDNSData):
|
|
|
37
50
|
"""
|
|
38
51
|
|
|
39
52
|
http: Optional[httpx.Response] = None
|
|
40
|
-
is_complete: bool = False
|
|
41
53
|
blocked_by: Optional[str] = None
|
|
42
54
|
|
|
43
55
|
@classmethod
|
|
@@ -69,6 +81,24 @@ class Response(BaseDNSData):
|
|
|
69
81
|
def rcode_text(self) -> str:
|
|
70
82
|
return dnsrcode.to_text(self.rcode)
|
|
71
83
|
|
|
84
|
+
@property
|
|
85
|
+
def ttl(self) -> int:
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
Get median TTL from answers, falling back to authority, then to error-code defaults.
|
|
89
|
+
|
|
90
|
+
"""
|
|
91
|
+
answers = self.message.answer or self.message.authority
|
|
92
|
+
if answers:
|
|
93
|
+
ttls = [answer.ttl for answer in answers]
|
|
94
|
+
ttl = min(ttls)
|
|
95
|
+
return ttl
|
|
96
|
+
|
|
97
|
+
ttl = TTL_CODE_DEFAULTS.get(self.rcode, dnsrcode.NXDOMAIN)
|
|
98
|
+
return ttl
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
72
102
|
def __str__(self):
|
|
73
103
|
"""
|
|
74
104
|
|
|
@@ -144,9 +174,10 @@ class Exchange:
|
|
|
144
174
|
|
|
145
175
|
request: Request
|
|
146
176
|
response: Optional[Response] = None
|
|
177
|
+
answers_pre: List[RRset] = field(default_factory=list)
|
|
147
178
|
is_internal: bool = False
|
|
148
179
|
client_name: Optional[str] = None
|
|
149
|
-
|
|
180
|
+
is_complete: bool = False
|
|
150
181
|
|
|
151
182
|
|
|
152
183
|
@classmethod
|
|
@@ -163,27 +194,25 @@ class Exchange:
|
|
|
163
194
|
def question_last(self) -> RRset:
|
|
164
195
|
"""
|
|
165
196
|
|
|
166
|
-
|
|
197
|
+
Create an RRset surrogate representing the latest/current question. This can be the original question - or a hybrid one if we've injected our own answers into the Exchange.
|
|
167
198
|
|
|
168
199
|
"""
|
|
169
|
-
if self.
|
|
170
|
-
rrset = self.
|
|
171
|
-
|
|
200
|
+
if self.answers_pre:
|
|
201
|
+
rrset = self.answers_pre[-1]
|
|
202
|
+
rdtype = self.request.type
|
|
172
203
|
ttl = self.request.question.ttl
|
|
173
204
|
rdclass = self.request.question.rdclass
|
|
174
205
|
name = next(iter(rrset.items.keys())).to_text()
|
|
175
|
-
|
|
176
|
-
rrset_contrived = dns.rrset.from_text(
|
|
206
|
+
rrset_surrogate = dns.rrset.from_text(
|
|
177
207
|
name=name,
|
|
178
208
|
ttl=ttl,
|
|
179
|
-
rdtype=
|
|
209
|
+
rdtype=rdtype,
|
|
180
210
|
rdclass=rdclass,
|
|
181
|
-
|
|
182
211
|
)
|
|
183
212
|
|
|
184
|
-
return
|
|
213
|
+
return rrset_surrogate
|
|
185
214
|
else:
|
|
186
|
-
return self.request.question
|
|
215
|
+
return self.request.question
|
|
187
216
|
|
|
188
217
|
@property
|
|
189
218
|
def query_last(self) -> QueryMessage:
|
|
@@ -194,7 +223,7 @@ class Exchange:
|
|
|
194
223
|
"""
|
|
195
224
|
|
|
196
225
|
question_last = self.question_last
|
|
197
|
-
query = dns.message.make_query(qname=question_last.name, rdclass=question_last.rdclass, rdtype=question_last.rdtype)
|
|
226
|
+
query = dns.message.make_query(qname=question_last.name, rdclass=question_last.rdclass, rdtype=question_last.rdtype, id=self.request.message.id)
|
|
198
227
|
return query
|
|
199
228
|
|
|
200
229
|
@property
|
fmtr/tools/dns_tools/proxy.py
CHANGED
|
@@ -31,6 +31,14 @@ class Proxy(server.Plain):
|
|
|
31
31
|
"""
|
|
32
32
|
return
|
|
33
33
|
|
|
34
|
+
def finalize(self, exchange: Exchange):
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
Finalize a still open exchange.
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
exchange.is_complete = True
|
|
41
|
+
|
|
34
42
|
def resolve(self, exchange: Exchange) -> Exchange:
|
|
35
43
|
"""
|
|
36
44
|
|
|
@@ -38,20 +46,21 @@ class Proxy(server.Plain):
|
|
|
38
46
|
Subclasses can override the relevant processing methods to implement custom behaviour.
|
|
39
47
|
|
|
40
48
|
"""
|
|
41
|
-
|
|
42
49
|
with logger.span(f'Processing question...'):
|
|
43
50
|
self.process_question(exchange)
|
|
44
|
-
if exchange.
|
|
51
|
+
if exchange.is_complete:
|
|
45
52
|
return exchange
|
|
46
53
|
|
|
47
54
|
with logger.span(f'Making upstream request...'):
|
|
48
55
|
self.client.resolve(exchange)
|
|
49
|
-
if exchange.
|
|
56
|
+
if exchange.is_complete:
|
|
50
57
|
return exchange
|
|
51
58
|
|
|
52
59
|
with logger.span(f'Processing upstream response...'):
|
|
53
60
|
self.process_upstream(exchange)
|
|
54
|
-
if exchange.
|
|
61
|
+
if exchange.is_complete:
|
|
55
62
|
return exchange
|
|
56
63
|
|
|
64
|
+
self.finalize(exchange)
|
|
65
|
+
|
|
57
66
|
return exchange
|
fmtr/tools/dns_tools/server.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import dns
|
|
1
2
|
import socket
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from datetime import timedelta
|
|
@@ -66,7 +67,7 @@ class Plain:
|
|
|
66
67
|
logger.info(f'Request found in cache.')
|
|
67
68
|
exchange.response = self.cache[exchange.key]
|
|
68
69
|
exchange.response.message.id = exchange.request.message.id
|
|
69
|
-
exchange.
|
|
70
|
+
exchange.is_complete = True
|
|
70
71
|
|
|
71
72
|
def get_span(self, exchange: Exchange):
|
|
72
73
|
"""
|
|
@@ -90,10 +91,20 @@ class Plain:
|
|
|
90
91
|
response = exchange.response
|
|
91
92
|
|
|
92
93
|
logger.info(
|
|
93
|
-
f'Resolution complete {exchange.client_name=} {request.message.id=} {request.type_text} {request.name_text} {request.question=} {
|
|
94
|
+
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=}...'
|
|
94
95
|
)
|
|
95
96
|
|
|
97
|
+
def log_dns_errors(self, exchange: Exchange):
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
Warn about any errors
|
|
101
|
+
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
if exchange.response.rcode == dns.rcode.NOERROR:
|
|
105
|
+
return
|
|
96
106
|
|
|
107
|
+
logger.warning(f'Error {exchange.response.rcode_text=}')
|
|
97
108
|
|
|
98
109
|
def handle(self, exchange: Exchange):
|
|
99
110
|
"""
|
|
@@ -116,11 +127,11 @@ class Plain:
|
|
|
116
127
|
with logger.span(f'Checking cache...'):
|
|
117
128
|
self.check_cache(exchange)
|
|
118
129
|
|
|
119
|
-
if not exchange.
|
|
130
|
+
if not exchange.is_complete:
|
|
120
131
|
exchange = self.resolve(exchange)
|
|
121
|
-
exchange.
|
|
132
|
+
self.cache[exchange.key] = exchange.response
|
|
122
133
|
|
|
123
|
-
self.
|
|
134
|
+
self.log_dns_errors(exchange)
|
|
124
135
|
self.log_response(exchange)
|
|
125
136
|
|
|
126
137
|
return exchange
|
fmtr/tools/version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.3.
|
|
1
|
+
1.3.16
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.16
|
|
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: Unidecode; extra == "all"
|
|
131
|
-
Requires-Dist: peft; extra == "all"
|
|
132
|
-
Requires-Dist: tinynetrc; extra == "all"
|
|
133
|
-
Requires-Dist: contexttimer; extra == "all"
|
|
129
|
+
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
134
130
|
Requires-Dist: torchaudio; extra == "all"
|
|
135
|
-
Requires-Dist:
|
|
136
|
-
Requires-Dist: openpyxl; extra == "all"
|
|
137
|
-
Requires-Dist: pydantic-settings; extra == "all"
|
|
138
|
-
Requires-Dist: sre_yield; extra == "all"
|
|
139
|
-
Requires-Dist: distributed; extra == "all"
|
|
140
|
-
Requires-Dist: semver; extra == "all"
|
|
141
|
-
Requires-Dist: tokenizers; extra == "all"
|
|
131
|
+
Requires-Dist: flet-webview; extra == "all"
|
|
142
132
|
Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
|
|
143
|
-
Requires-Dist:
|
|
144
|
-
Requires-Dist:
|
|
145
|
-
Requires-Dist:
|
|
146
|
-
Requires-Dist:
|
|
147
|
-
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"
|
|
137
|
+
Requires-Dist: docker; extra == "all"
|
|
138
|
+
Requires-Dist: faker; extra == "all"
|
|
139
|
+
Requires-Dist: torchvision; extra == "all"
|
|
140
|
+
Requires-Dist: flet-video; extra == "all"
|
|
141
|
+
Requires-Dist: pandas; extra == "all"
|
|
148
142
|
Requires-Dist: filetype; extra == "all"
|
|
149
|
-
Requires-Dist:
|
|
150
|
-
Requires-Dist:
|
|
143
|
+
Requires-Dist: tokenizers; extra == "all"
|
|
144
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
145
|
+
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
146
|
+
Requires-Dist: pymupdf4llm; extra == "all"
|
|
147
|
+
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
151
|
Requires-Dist: pymupdf; extra == "all"
|
|
152
|
+
Requires-Dist: bokeh; extra == "all"
|
|
153
|
+
Requires-Dist: httpx_retries; extra == "all"
|
|
152
154
|
Requires-Dist: json_repair; extra == "all"
|
|
153
|
-
Requires-Dist: openai; extra == "all"
|
|
154
|
-
Requires-Dist: google-auth; extra == "all"
|
|
155
|
-
Requires-Dist: huggingface_hub; extra == "all"
|
|
156
155
|
Requires-Dist: ollama; extra == "all"
|
|
157
|
-
Requires-Dist:
|
|
156
|
+
Requires-Dist: html2text; extra == "all"
|
|
158
157
|
Requires-Dist: dnspython[doh]; extra == "all"
|
|
159
|
-
Requires-Dist:
|
|
160
|
-
Requires-Dist:
|
|
158
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
159
|
+
Requires-Dist: logfire; extra == "all"
|
|
161
160
|
Requires-Dist: google-api-python-client; extra == "all"
|
|
162
|
-
Requires-Dist: docker; extra == "all"
|
|
163
|
-
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
164
|
-
Requires-Dist: torchvision; extra == "all"
|
|
165
|
-
Requires-Dist: regex; extra == "all"
|
|
166
|
-
Requires-Dist: dask[bag]; extra == "all"
|
|
167
|
-
Requires-Dist: appdirs; extra == "all"
|
|
168
|
-
Requires-Dist: html2text; extra == "all"
|
|
169
|
-
Requires-Dist: deepmerge; extra == "all"
|
|
170
161
|
Requires-Dist: logfire[httpx]; extra == "all"
|
|
171
|
-
Requires-Dist:
|
|
172
|
-
Requires-Dist:
|
|
162
|
+
Requires-Dist: deepmerge; extra == "all"
|
|
163
|
+
Requires-Dist: google-auth; extra == "all"
|
|
164
|
+
Requires-Dist: sentence_transformers; extra == "all"
|
|
165
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
166
|
+
Requires-Dist: sre_yield; extra == "all"
|
|
167
|
+
Requires-Dist: distributed; extra == "all"
|
|
168
|
+
Requires-Dist: dask[bag]; extra == "all"
|
|
169
|
+
Requires-Dist: huggingface_hub; extra == "all"
|
|
170
|
+
Requires-Dist: pydantic; extra == "all"
|
|
171
|
+
Requires-Dist: regex; extra == "all"
|
|
172
|
+
Requires-Dist: pydantic-settings; extra == "all"
|
|
173
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
174
|
+
Requires-Dist: flet[all]; extra == "all"
|
|
175
|
+
Requires-Dist: semver; extra == "all"
|
|
176
|
+
Requires-Dist: setuptools; extra == "all"
|
|
177
|
+
Requires-Dist: tinynetrc; extra == "all"
|
|
173
178
|
Requires-Dist: tabulate; extra == "all"
|
|
174
|
-
Requires-Dist:
|
|
175
|
-
Requires-Dist: bokeh; extra == "all"
|
|
176
|
-
Requires-Dist: logfire; extra == "all"
|
|
179
|
+
Requires-Dist: diskcache; extra == "all"
|
|
177
180
|
Requires-Dist: cachetools; extra == "all"
|
|
178
|
-
Requires-Dist:
|
|
181
|
+
Requires-Dist: httpx; extra == "all"
|
|
182
|
+
Requires-Dist: Unidecode; extra == "all"
|
|
179
183
|
Requires-Dist: yamlscript; extra == "all"
|
|
180
|
-
Requires-Dist: pydantic; extra == "all"
|
|
181
|
-
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
182
|
-
Requires-Dist: pyyaml; extra == "all"
|
|
183
|
-
Requires-Dist: diskcache; 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=BGvCUIvG-zhY8XPNIcDdYZpZkRSi1ad9zjGx1qmjmTU,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
|
-
fmtr/tools/dns_tools/client.py,sha256=
|
|
54
|
-
fmtr/tools/dns_tools/dm.py,sha256=
|
|
55
|
-
fmtr/tools/dns_tools/proxy.py,sha256=
|
|
56
|
-
fmtr/tools/dns_tools/server.py,sha256=
|
|
53
|
+
fmtr/tools/dns_tools/client.py,sha256=uUFoFRwoPtetPVH6PZ3ssHmx3WEquT6UW4FCBKq4n74,2722
|
|
54
|
+
fmtr/tools/dns_tools/dm.py,sha256=KKIcxJ5Dni2PFtOEHNNSi1Y4NCZ0bp3tifpSZRXtMTM,6261
|
|
55
|
+
fmtr/tools/dns_tools/proxy.py,sha256=0lgn1pq5KLoGA4644ZXYs2lPjXjRB-ibFb7JHzlMY9o,1618
|
|
56
|
+
fmtr/tools/dns_tools/server.py,sha256=c2ZxhF3EUgYLl1fnBEfLbM_URP3t7GdXeYOc6PQEmd0,3890
|
|
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.16.dist-info/licenses/LICENSE,sha256=FW9aa6vVN5IjRQWLT43hs4_koYSmpcbIovlKeAJ0_cI,10757
|
|
80
|
+
fmtr_tools-1.3.16.dist-info/METADATA,sha256=MunCjEPB35a78qpSMM10MHjcuJjl3gcGHjC3JvVwA4I,15938
|
|
81
|
+
fmtr_tools-1.3.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
82
|
+
fmtr_tools-1.3.16.dist-info/entry_points.txt,sha256=h-r__Xh5njtFqreMLg6cGuTFS4Qh-QqJPU1HB-_BS-Q,357
|
|
83
|
+
fmtr_tools-1.3.16.dist-info/top_level.txt,sha256=LXem9xCgNOD72tE2gRKESdiQTL902mfFkwWb6-dlwEE,5
|
|
84
|
+
fmtr_tools-1.3.16.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|