fmtr.tools 1.3.1__py3-none-any.whl → 1.3.2__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/__init__.py +2 -2
- fmtr/tools/dns_tools/client.py +28 -19
- fmtr/tools/dns_tools/dm.py +59 -10
- fmtr/tools/dns_tools/proxy.py +68 -0
- fmtr/tools/dns_tools/server.py +2 -53
- fmtr/tools/version +1 -1
- {fmtr_tools-1.3.1.dist-info → fmtr_tools-1.3.2.dist-info}/METADATA +44 -44
- {fmtr_tools-1.3.1.dist-info → fmtr_tools-1.3.2.dist-info}/RECORD +12 -11
- {fmtr_tools-1.3.1.dist-info → fmtr_tools-1.3.2.dist-info}/WHEEL +0 -0
- {fmtr_tools-1.3.1.dist-info → fmtr_tools-1.3.2.dist-info}/entry_points.txt +0 -0
- {fmtr_tools-1.3.1.dist-info → fmtr_tools-1.3.2.dist-info}/licenses/LICENSE +0 -0
- {fmtr_tools-1.3.1.dist-info → fmtr_tools-1.3.2.dist-info}/top_level.txt +0 -0
fmtr/tools/dns_tools/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from fmtr.tools.import_tools import MissingExtraMockModule
|
|
2
2
|
|
|
3
3
|
try:
|
|
4
|
-
from fmtr.tools.dns_tools import server, client, dm
|
|
4
|
+
from fmtr.tools.dns_tools import server, client, dm, proxy
|
|
5
5
|
except ImportError as exception:
|
|
6
|
-
server = client = dm = MissingExtraMockModule('dns', exception)
|
|
6
|
+
server = client = dm = proxy = MissingExtraMockModule('dns', exception)
|
fmtr/tools/dns_tools/client.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import dns
|
|
2
1
|
from dataclasses import dataclass
|
|
3
|
-
from dns import query
|
|
4
2
|
from functools import cached_property
|
|
3
|
+
|
|
4
|
+
import dns as dnspython
|
|
5
|
+
from dns import query
|
|
5
6
|
from httpx_retries import Retry, RetryTransport
|
|
6
7
|
|
|
7
8
|
from fmtr.tools import http_tools as http
|
|
8
|
-
from fmtr.tools.dns_tools.dm import Exchange, Response
|
|
9
|
+
from fmtr.tools.dns_tools.dm import Exchange, Response
|
|
9
10
|
from fmtr.tools.logging_tools import logger
|
|
10
11
|
|
|
11
12
|
RETRY_STRATEGY = Retry(
|
|
@@ -29,28 +30,36 @@ class HTTPClientDoH(http.Client):
|
|
|
29
30
|
TRANSPORT = RetryTransport(retry=RETRY_STRATEGY)
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
@dataclass
|
|
34
|
+
class Plain:
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
Plain DNS
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
host: str
|
|
41
|
+
port: int = 53
|
|
36
42
|
|
|
37
43
|
def resolve(self, exchange: Exchange):
|
|
44
|
+
|
|
38
45
|
with logger.span(f'UDP {self.host}:{self.port}'):
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
response_plain = query.udp(q=exchange.query_last, where=self.host, port=self.port)
|
|
47
|
+
response = Response.from_message(response_plain)
|
|
48
|
+
|
|
49
|
+
exchange.response.message.answer += response.message.answer
|
|
41
50
|
|
|
42
51
|
|
|
43
52
|
@dataclass
|
|
44
|
-
class
|
|
53
|
+
class HTTP:
|
|
45
54
|
"""
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
DNS over HTTP
|
|
48
57
|
|
|
49
58
|
"""
|
|
50
59
|
|
|
51
60
|
HEADERS = {"Content-Type": "application/dns-message"}
|
|
52
61
|
CLIENT = HTTPClientDoH()
|
|
53
|
-
BOOTSTRAP =
|
|
62
|
+
BOOTSTRAP = Plain('8.8.8.8')
|
|
54
63
|
|
|
55
64
|
host: str
|
|
56
65
|
url: str
|
|
@@ -58,11 +67,10 @@ class ClientDoH:
|
|
|
58
67
|
|
|
59
68
|
@cached_property
|
|
60
69
|
def ip(self):
|
|
61
|
-
message =
|
|
62
|
-
|
|
63
|
-
exchange = Exchange(request=request, ip=None, port=None)
|
|
70
|
+
message = dnspython.message.make_query(self.host, dnspython.rdatatype.A, flags=0)
|
|
71
|
+
exchange = Exchange.from_wire(message.to_wire(), ip=None, port=None)
|
|
64
72
|
self.BOOTSTRAP.resolve(exchange)
|
|
65
|
-
ip = next(iter(exchange.
|
|
73
|
+
ip = next(iter(exchange.response.answer.items.keys())).address
|
|
66
74
|
return ip
|
|
67
75
|
|
|
68
76
|
def resolve(self, exchange: Exchange):
|
|
@@ -71,10 +79,11 @@ class ClientDoH:
|
|
|
71
79
|
Resolve via DoH
|
|
72
80
|
|
|
73
81
|
"""
|
|
74
|
-
|
|
82
|
+
|
|
75
83
|
headers = self.HEADERS | dict(Host=self.host)
|
|
76
84
|
url = self.url.format(host=self.ip)
|
|
77
|
-
response_doh = self.CLIENT.post(url, headers=headers, content=
|
|
85
|
+
response_doh = self.CLIENT.post(url, headers=headers, content=exchange.query_last.to_wire())
|
|
78
86
|
response_doh.raise_for_status()
|
|
79
87
|
response = Response.from_http(response_doh)
|
|
80
|
-
|
|
88
|
+
|
|
89
|
+
exchange.response.message.answer += response.message.answer
|
fmtr/tools/dns_tools/dm.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import dns
|
|
2
|
-
import httpx
|
|
3
1
|
from dataclasses import dataclass
|
|
4
|
-
from dns.message import Message
|
|
5
2
|
from functools import cached_property
|
|
6
3
|
from typing import Self, Optional
|
|
7
4
|
|
|
5
|
+
import dns
|
|
6
|
+
import httpx
|
|
7
|
+
from dns.message import Message, QueryMessage
|
|
8
|
+
from dns.rrset import RRset
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
@dataclass
|
|
10
12
|
class BaseDNSData:
|
|
@@ -33,14 +35,17 @@ class Response(BaseDNSData):
|
|
|
33
35
|
"""
|
|
34
36
|
|
|
35
37
|
http: Optional[httpx.Response] = None
|
|
38
|
+
is_complete: bool = False
|
|
36
39
|
|
|
37
40
|
@classmethod
|
|
38
41
|
def from_http(cls, response: httpx.Response) -> Self:
|
|
39
42
|
self = cls(response.content, http=response)
|
|
40
43
|
return self
|
|
41
44
|
|
|
42
|
-
@
|
|
43
|
-
def answer(self):
|
|
45
|
+
@property
|
|
46
|
+
def answer(self) -> Optional[RRset]:
|
|
47
|
+
if not self.message.answer:
|
|
48
|
+
return None
|
|
44
49
|
return self.message.answer[-1]
|
|
45
50
|
|
|
46
51
|
|
|
@@ -54,7 +59,7 @@ class Request(BaseDNSData):
|
|
|
54
59
|
wire: bytes
|
|
55
60
|
|
|
56
61
|
@cached_property
|
|
57
|
-
def question(self):
|
|
62
|
+
def question(self) -> RRset:
|
|
58
63
|
return self.message.question[0]
|
|
59
64
|
|
|
60
65
|
@cached_property
|
|
@@ -77,10 +82,14 @@ class Request(BaseDNSData):
|
|
|
77
82
|
def name_text(self):
|
|
78
83
|
return self.name.to_text()
|
|
79
84
|
|
|
85
|
+
def get_response_template(self):
|
|
86
|
+
message = dns.message.make_response(self.message)
|
|
87
|
+
message.flags |= dns.flags.RA
|
|
88
|
+
return message
|
|
89
|
+
|
|
80
90
|
@cached_property
|
|
81
91
|
def blackhole(self) -> Response:
|
|
82
|
-
blackhole =
|
|
83
|
-
blackhole.flags |= dns.flags.RA
|
|
92
|
+
blackhole = self.get_response_template()
|
|
84
93
|
blackhole.set_rcode(dns.rcode.NXDOMAIN)
|
|
85
94
|
response = Response.from_message(blackhole)
|
|
86
95
|
return response
|
|
@@ -98,13 +107,53 @@ class Exchange:
|
|
|
98
107
|
|
|
99
108
|
request: Request
|
|
100
109
|
response: Optional[Response] = None
|
|
101
|
-
|
|
110
|
+
|
|
102
111
|
|
|
103
112
|
@classmethod
|
|
104
113
|
def from_wire(cls, wire: bytes, ip: str, port: int) -> Self:
|
|
105
114
|
request = Request(wire)
|
|
106
|
-
|
|
115
|
+
response = Response.from_message(request.get_response_template())
|
|
116
|
+
|
|
117
|
+
return cls(request=request, response=response, ip=ip, port=port)
|
|
107
118
|
|
|
108
119
|
@cached_property
|
|
109
120
|
def client(self):
|
|
110
121
|
return f'{self.ip}:{self.port}'
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def question_last(self) -> RRset:
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
Contrive an RRset 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.
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
if self.response.answer:
|
|
131
|
+
rrset = self.response.answer
|
|
132
|
+
ty = self.request.type
|
|
133
|
+
ttl = self.request.question.ttl
|
|
134
|
+
rdclass = self.request.question.rdclass
|
|
135
|
+
name = next(iter(rrset.items.keys())).to_text()
|
|
136
|
+
|
|
137
|
+
rrset_contrived = dns.rrset.from_text(
|
|
138
|
+
name=name,
|
|
139
|
+
ttl=ttl,
|
|
140
|
+
rdtype=ty,
|
|
141
|
+
rdclass=rdclass,
|
|
142
|
+
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return rrset_contrived
|
|
146
|
+
else:
|
|
147
|
+
return self.request.question # Solves the issue of digging out the name.
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def query_last(self) -> QueryMessage:
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
Create a query (e.g. for use by upstream) based on the last question.
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
question_last = self.question_last
|
|
158
|
+
query = dns.message.make_query(qname=question_last.name, rdclass=question_last.rdclass, rdtype=question_last.rdtype)
|
|
159
|
+
return query
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from fmtr.tools import logger
|
|
4
|
+
from fmtr.tools.dns_tools import server, client
|
|
5
|
+
from fmtr.tools.dns_tools.dm import Exchange
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Proxy(server.Plain):
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
Base for a DNS Proxy server (plain server) TODO: Allow subclassing of any server type.
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
client: client.HTTP
|
|
17
|
+
|
|
18
|
+
def process_question(self, exchange: Exchange):
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
Modify exchange based on initial question.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
def process_upstream(self, exchange: Exchange):
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
Modify exchange after upstream response.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
def resolve(self, exchange: Exchange):
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
Resolve a request, processing each stage, initial question, upstream response etc.
|
|
38
|
+
Subclasses can override the relevant processing methods to implement custom behaviour.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
request = exchange.request
|
|
43
|
+
|
|
44
|
+
with logger.span(f'Handling request ID {request.message.id} for {request.name_text} from {exchange.client}...'):
|
|
45
|
+
|
|
46
|
+
if not request.is_valid:
|
|
47
|
+
raise ValueError(f'Only one question per request is supported. Got {len(request.question)} questions.')
|
|
48
|
+
|
|
49
|
+
with logger.span(f'Processing question...'):
|
|
50
|
+
self.process_question(exchange)
|
|
51
|
+
if exchange.response.is_complete:
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
with logger.span(f'Making upstream request for {request.name_text}...'):
|
|
55
|
+
self.client.resolve(exchange)
|
|
56
|
+
if exchange.response.is_complete:
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
with logger.span(f'Processing upstream response...'):
|
|
60
|
+
self.process_upstream(exchange)
|
|
61
|
+
if exchange.response.is_complete:
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
if exchange.response:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
exchange.response.is_complete = True
|
|
68
|
+
return
|
fmtr/tools/dns_tools/server.py
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import socket
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
|
|
4
|
-
from fmtr.tools import logger
|
|
5
|
-
from fmtr.tools.dns_tools.client import ClientDoH
|
|
6
4
|
from fmtr.tools.dns_tools.dm import Exchange
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
@dataclass
|
|
10
|
-
class
|
|
8
|
+
class Plain:
|
|
11
9
|
"""
|
|
12
10
|
|
|
13
11
|
Base for starting a plain DNS server
|
|
@@ -37,53 +35,4 @@ class ServerBasePlain:
|
|
|
37
35
|
data, (ip, port) = sock.recvfrom(512)
|
|
38
36
|
exchange = Exchange.from_wire(data, ip=ip, port=port)
|
|
39
37
|
self.resolve(exchange)
|
|
40
|
-
sock.sendto(exchange.response.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@dataclass
|
|
44
|
-
class ServerBaseDoHProxy(ServerBasePlain):
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
Base for a DNS Proxy server
|
|
48
|
-
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
client: ClientDoH
|
|
52
|
-
|
|
53
|
-
def process_question(self, exchange: Exchange):
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
def process_upstream(self, exchange: Exchange):
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
def resolve(self, exchange: Exchange):
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
Resolve a request, processing each stage, initial question, upstream response etc.
|
|
63
|
-
Subclasses can override the relevant processing methods to implement custom behaviour.
|
|
64
|
-
|
|
65
|
-
"""
|
|
66
|
-
|
|
67
|
-
request = exchange.request
|
|
68
|
-
|
|
69
|
-
with logger.span(f'Handling request ID {request.message.id} for {request.name_text} from {exchange.client}...'):
|
|
70
|
-
|
|
71
|
-
if not request.is_valid:
|
|
72
|
-
raise ValueError(f'Only one question per request is supported. Got {len(request.question)} questions.')
|
|
73
|
-
|
|
74
|
-
with logger.span(f'Processing question...'):
|
|
75
|
-
self.process_question(exchange)
|
|
76
|
-
if exchange.response:
|
|
77
|
-
return
|
|
78
|
-
|
|
79
|
-
with logger.span(f'Making upstream request for {request.name_text}...'):
|
|
80
|
-
self.client.resolve(exchange)
|
|
81
|
-
|
|
82
|
-
with logger.span(f'Processing upstream response...'):
|
|
83
|
-
self.process_upstream(exchange)
|
|
84
|
-
|
|
85
|
-
if exchange.response:
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
exchange.response = exchange.response_upstream
|
|
89
|
-
return
|
|
38
|
+
sock.sendto(exchange.response.message.to_wire(), (ip, port))
|
fmtr/tools/version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.3.
|
|
1
|
+
1.3.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
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
|
|
@@ -130,60 +130,60 @@ Requires-Dist: logfire[httpx]; extra == "http"
|
|
|
130
130
|
Provides-Extra: setup
|
|
131
131
|
Requires-Dist: setuptools; extra == "setup"
|
|
132
132
|
Provides-Extra: all
|
|
133
|
-
Requires-Dist: openai; extra == "all"
|
|
134
133
|
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
135
|
-
Requires-Dist:
|
|
136
|
-
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
137
|
-
Requires-Dist: distributed; extra == "all"
|
|
138
|
-
Requires-Dist: pymupdf4llm; extra == "all"
|
|
139
|
-
Requires-Dist: pandas; extra == "all"
|
|
140
|
-
Requires-Dist: tinynetrc; extra == "all"
|
|
141
|
-
Requires-Dist: google-api-python-client; extra == "all"
|
|
142
|
-
Requires-Dist: Unidecode; extra == "all"
|
|
143
|
-
Requires-Dist: flet[all]; extra == "all"
|
|
144
|
-
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
145
|
-
Requires-Dist: pydevd-pycharm; extra == "all"
|
|
134
|
+
Requires-Dist: dask[bag]; extra == "all"
|
|
146
135
|
Requires-Dist: tabulate; extra == "all"
|
|
136
|
+
Requires-Dist: flet[all]; extra == "all"
|
|
137
|
+
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
138
|
+
Requires-Dist: tinynetrc; extra == "all"
|
|
139
|
+
Requires-Dist: html2text; extra == "all"
|
|
140
|
+
Requires-Dist: deepmerge; extra == "all"
|
|
141
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
142
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
143
|
+
Requires-Dist: dnspython[doh]; extra == "all"
|
|
144
|
+
Requires-Dist: setuptools; extra == "all"
|
|
145
|
+
Requires-Dist: peft; extra == "all"
|
|
146
|
+
Requires-Dist: filetype; extra == "all"
|
|
147
|
+
Requires-Dist: flet-webview; extra == "all"
|
|
148
|
+
Requires-Dist: flet-video; extra == "all"
|
|
147
149
|
Requires-Dist: tokenizers; extra == "all"
|
|
148
|
-
Requires-Dist: json_repair; extra == "all"
|
|
149
|
-
Requires-Dist: yamlscript; extra == "all"
|
|
150
|
-
Requires-Dist: dask[bag]; extra == "all"
|
|
151
|
-
Requires-Dist: bokeh; extra == "all"
|
|
152
150
|
Requires-Dist: sre_yield; extra == "all"
|
|
153
|
-
Requires-Dist:
|
|
154
|
-
Requires-Dist:
|
|
155
|
-
Requires-Dist:
|
|
156
|
-
Requires-Dist: html2text; extra == "all"
|
|
157
|
-
Requires-Dist: faker; extra == "all"
|
|
158
|
-
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
159
|
-
Requires-Dist: pydantic-settings; extra == "all"
|
|
151
|
+
Requires-Dist: semver; extra == "all"
|
|
152
|
+
Requires-Dist: httpx; extra == "all"
|
|
153
|
+
Requires-Dist: google-auth; extra == "all"
|
|
160
154
|
Requires-Dist: openpyxl; extra == "all"
|
|
161
|
-
Requires-Dist:
|
|
162
|
-
Requires-Dist: httpx_retries; extra == "all"
|
|
163
|
-
Requires-Dist: pymupdf; extra == "all"
|
|
155
|
+
Requires-Dist: pymupdf4llm; extra == "all"
|
|
164
156
|
Requires-Dist: appdirs; extra == "all"
|
|
165
|
-
Requires-Dist:
|
|
166
|
-
Requires-Dist: filetype; extra == "all"
|
|
167
|
-
Requires-Dist: logfire[httpx]; extra == "all"
|
|
168
|
-
Requires-Dist: torchaudio; extra == "all"
|
|
169
|
-
Requires-Dist: pydantic; extra == "all"
|
|
157
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
170
158
|
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
171
|
-
Requires-Dist:
|
|
172
|
-
Requires-Dist:
|
|
173
|
-
Requires-Dist: httpx; extra == "all"
|
|
159
|
+
Requires-Dist: httpx_retries; extra == "all"
|
|
160
|
+
Requires-Dist: pydantic; extra == "all"
|
|
174
161
|
Requires-Dist: sentence_transformers; extra == "all"
|
|
175
|
-
Requires-Dist: deepmerge; extra == "all"
|
|
176
|
-
Requires-Dist: google-auth; extra == "all"
|
|
177
|
-
Requires-Dist: regex; extra == "all"
|
|
178
|
-
Requires-Dist: docker; extra == "all"
|
|
179
|
-
Requires-Dist: semver; extra == "all"
|
|
180
|
-
Requires-Dist: peft; extra == "all"
|
|
181
|
-
Requires-Dist: logfire; extra == "all"
|
|
182
162
|
Requires-Dist: ollama; extra == "all"
|
|
163
|
+
Requires-Dist: distributed; extra == "all"
|
|
164
|
+
Requires-Dist: huggingface_hub; extra == "all"
|
|
165
|
+
Requires-Dist: logfire[httpx]; extra == "all"
|
|
166
|
+
Requires-Dist: torchaudio; extra == "all"
|
|
167
|
+
Requires-Dist: docker; extra == "all"
|
|
168
|
+
Requires-Dist: openai; extra == "all"
|
|
169
|
+
Requires-Dist: yamlscript; extra == "all"
|
|
170
|
+
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
171
|
+
Requires-Dist: Unidecode; extra == "all"
|
|
183
172
|
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
184
|
-
Requires-Dist:
|
|
185
|
-
Requires-Dist:
|
|
173
|
+
Requires-Dist: pandas; extra == "all"
|
|
174
|
+
Requires-Dist: bokeh; extra == "all"
|
|
175
|
+
Requires-Dist: diskcache; extra == "all"
|
|
176
|
+
Requires-Dist: json_repair; extra == "all"
|
|
177
|
+
Requires-Dist: logfire; extra == "all"
|
|
178
|
+
Requires-Dist: regex; extra == "all"
|
|
186
179
|
Requires-Dist: fastapi; extra == "all"
|
|
180
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
181
|
+
Requires-Dist: pydevd-pycharm; extra == "all"
|
|
182
|
+
Requires-Dist: faker; extra == "all"
|
|
183
|
+
Requires-Dist: pymupdf; extra == "all"
|
|
184
|
+
Requires-Dist: google-api-python-client; extra == "all"
|
|
185
|
+
Requires-Dist: torchvision; extra == "all"
|
|
186
|
+
Requires-Dist: pydantic-settings; extra == "all"
|
|
187
187
|
Dynamic: author
|
|
188
188
|
Dynamic: author-email
|
|
189
189
|
Dynamic: description
|
|
@@ -44,16 +44,17 @@ 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=exPSrztkeQgMZ8VTDFGwb8f-z8T_NbtvtmOtm7IiqvA,5
|
|
48
48
|
fmtr/tools/version_tools.py,sha256=yNs_CGqWpqE4jbK9wsPIi14peJVXYbhIcMqHAFOw3yE,1480
|
|
49
49
|
fmtr/tools/yaml_tools.py,sha256=9kuYChqJelWQIjGlSnK4iDdOWWH06P0gp9jIcRrC3UI,1903
|
|
50
50
|
fmtr/tools/ai_tools/__init__.py,sha256=JZrLuOFNV1A3wvJgonxOgz_4WS-7MfCuowGWA5uYCjs,372
|
|
51
51
|
fmtr/tools/ai_tools/agentic_tools.py,sha256=acSEPFS-aguDXanWGs3fAAlRyJSYPZW7L-Kb2qDLm-I,4300
|
|
52
52
|
fmtr/tools/ai_tools/inference_tools.py,sha256=2UP2gXEyOJUjyyV6zmFIYmIxUsh1rXkRH0IbFvr2bRs,11908
|
|
53
|
-
fmtr/tools/dns_tools/__init__.py,sha256=
|
|
54
|
-
fmtr/tools/dns_tools/client.py,sha256=
|
|
55
|
-
fmtr/tools/dns_tools/dm.py,sha256=
|
|
56
|
-
fmtr/tools/dns_tools/
|
|
53
|
+
fmtr/tools/dns_tools/__init__.py,sha256=PjD3Og6D5yvDVpKmsUsrnSpz_rjXpl4zBtvMqm8xKWU,237
|
|
54
|
+
fmtr/tools/dns_tools/client.py,sha256=c2vzWBDZSxijwL1KvWUoDGc8wqk_KTAFxCr0P1rNjy8,2367
|
|
55
|
+
fmtr/tools/dns_tools/dm.py,sha256=KWQeeZhsDyrKoXzAD5zEOoHH3aiD4uKRqwnD8fFP1nI,3725
|
|
56
|
+
fmtr/tools/dns_tools/proxy.py,sha256=0TULVnD3FlZNAsNINy8bcbimTNvmkwvzXeMnOkdUqvw,1879
|
|
57
|
+
fmtr/tools/dns_tools/server.py,sha256=0RLsyVH8C-15PAJgqMNqxbHTPbTWkq_thh8nnS1clAg,892
|
|
57
58
|
fmtr/tools/entrypoints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
59
|
fmtr/tools/entrypoints/cache_hfh.py,sha256=fQNs4J9twQuZH_Yj98-oOvEX7-LrSUP3kO8nzw2HrHs,60
|
|
59
60
|
fmtr/tools/entrypoints/ep_test.py,sha256=B8HfWISfSgw_xVX475CbJGh_QnpOe9MH65H8qGjTWbY,46
|
|
@@ -73,9 +74,9 @@ fmtr/tools/tests/test_environment.py,sha256=iHaiMQfECYZPkPKwfuIZV9uHuWe3aE-p_dN_
|
|
|
73
74
|
fmtr/tools/tests/test_json.py,sha256=IeSP4ziPvRcmS8kq7k9tHonC9rN5YYq9GSNT2ul6Msk,287
|
|
74
75
|
fmtr/tools/tests/test_path.py,sha256=AkZQa6_8BQ-VaCyL_J-iKmdf2ZaM-xFYR37Kun3k4_g,2188
|
|
75
76
|
fmtr/tools/tests/test_yaml.py,sha256=jc0TwwKu9eC0LvFGNMERdgBue591xwLxYXFbtsRwXVM,287
|
|
76
|
-
fmtr_tools-1.3.
|
|
77
|
-
fmtr_tools-1.3.
|
|
78
|
-
fmtr_tools-1.3.
|
|
79
|
-
fmtr_tools-1.3.
|
|
80
|
-
fmtr_tools-1.3.
|
|
81
|
-
fmtr_tools-1.3.
|
|
77
|
+
fmtr_tools-1.3.2.dist-info/licenses/LICENSE,sha256=FW9aa6vVN5IjRQWLT43hs4_koYSmpcbIovlKeAJ0_cI,10757
|
|
78
|
+
fmtr_tools-1.3.2.dist-info/METADATA,sha256=KlWOZL7_0eJCCM3bZtnujinv89YPLmtU4LcDg7JZx9I,16030
|
|
79
|
+
fmtr_tools-1.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
80
|
+
fmtr_tools-1.3.2.dist-info/entry_points.txt,sha256=fSQrDGNctdQXbUxpMWYVfVQ0mhZMDyaEDG3D3a0zOSc,278
|
|
81
|
+
fmtr_tools-1.3.2.dist-info/top_level.txt,sha256=LXem9xCgNOD72tE2gRKESdiQTL902mfFkwWb6-dlwEE,5
|
|
82
|
+
fmtr_tools-1.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|