fmtr.tools 1.3.14__tar.gz → 1.3.16__tar.gz

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.

Files changed (91) hide show
  1. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/PKG-INFO +45 -45
  2. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/dns_tools/client.py +6 -2
  3. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/dns_tools/dm.py +44 -15
  4. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/dns_tools/proxy.py +13 -4
  5. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/dns_tools/server.py +16 -5
  6. fmtr_tools-1.3.16/fmtr/tools/version +1 -0
  7. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr.tools.egg-info/PKG-INFO +45 -45
  8. fmtr_tools-1.3.14/fmtr/tools/version +0 -1
  9. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/LICENSE +0 -0
  10. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/README.md +0 -0
  11. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/__init__.py +0 -0
  12. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/ai_tools/__init__.py +0 -0
  13. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
  14. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/ai_tools/inference_tools.py +0 -0
  15. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/api_tools.py +0 -0
  16. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/async_tools.py +0 -0
  17. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/augmentation_tools.py +0 -0
  18. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/caching_tools.py +0 -0
  19. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/constants.py +0 -0
  20. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/data_modelling_tools.py +0 -0
  21. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/dataclass_tools.py +0 -0
  22. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/datatype_tools.py +0 -0
  23. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/debugging_tools.py +0 -0
  24. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/dns_tools/__init__.py +0 -0
  25. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/docker_tools.py +0 -0
  26. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/entrypoints/__init__.py +0 -0
  27. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/entrypoints/cache_hfh.py +0 -0
  28. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/entrypoints/ep_test.py +0 -0
  29. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/entrypoints/install_yamlscript.py +0 -0
  30. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/entrypoints/remote_debug_test.py +0 -0
  31. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/entrypoints/shell_debug.py +0 -0
  32. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/environment_tools.py +0 -0
  33. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/function_tools.py +0 -0
  34. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/google_api_tools.py +0 -0
  35. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/hash_tools.py +0 -0
  36. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/hfh_tools.py +0 -0
  37. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/html_tools.py +0 -0
  38. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/http_tools.py +0 -0
  39. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/import_tools.py +0 -0
  40. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/inspection_tools.py +0 -0
  41. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/interface_tools.py +0 -0
  42. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/iterator_tools.py +0 -0
  43. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/json_fix_tools.py +0 -0
  44. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/json_tools.py +0 -0
  45. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/logging_tools.py +0 -0
  46. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/merging_tools.py +0 -0
  47. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/metric_tools.py +0 -0
  48. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/name_tools.py +0 -0
  49. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/netrc_tools.py +0 -0
  50. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/openai_tools.py +0 -0
  51. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/packaging_tools.py +0 -0
  52. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/parallel_tools.py +0 -0
  53. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/path_tools/__init__.py +0 -0
  54. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/path_tools/app_path_tools.py +0 -0
  55. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/path_tools/path_tools.py +0 -0
  56. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/path_tools/type_path_tools.py +0 -0
  57. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/pattern_tools.py +0 -0
  58. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/pdf_tools.py +0 -0
  59. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/platform_tools.py +0 -0
  60. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/process_tools.py +0 -0
  61. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/profiling_tools.py +0 -0
  62. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/random_tools.py +0 -0
  63. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/semantic_tools.py +0 -0
  64. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/settings_tools.py +0 -0
  65. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/setup_tools/__init__.py +0 -0
  66. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/setup_tools/setup_tools.py +0 -0
  67. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/spaces_tools.py +0 -0
  68. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/string_tools.py +0 -0
  69. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tabular_tools.py +0 -0
  70. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tests/__init__.py +0 -0
  71. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tests/conftest.py +0 -0
  72. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tests/helpers.py +0 -0
  73. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tests/test_datatype.py +0 -0
  74. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tests/test_environment.py +0 -0
  75. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tests/test_json.py +0 -0
  76. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tests/test_path.py +0 -0
  77. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tests/test_yaml.py +0 -0
  78. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tokenization_tools.py +0 -0
  79. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/tools.py +0 -0
  80. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/unicode_tools.py +0 -0
  81. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/version_tools/__init__.py +0 -0
  82. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/version_tools/version_tools.py +0 -0
  83. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr/tools/yaml_tools.py +0 -0
  84. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr.tools.egg-info/SOURCES.txt +0 -0
  85. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr.tools.egg-info/dependency_links.txt +0 -0
  86. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr.tools.egg-info/entry_points.txt +0 -0
  87. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr.tools.egg-info/requires.txt +44 -44
  88. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/fmtr.tools.egg-info/top_level.txt +0 -0
  89. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/pyproject.toml +0 -0
  90. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/setup.cfg +0 -0
  91. {fmtr_tools-1.3.14 → fmtr_tools-1.3.16}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.3.14
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: deepmerge; extra == "all"
130
- Requires-Dist: google-auth-httplib2; extra == "all"
131
- Requires-Dist: fastapi; extra == "all"
132
- Requires-Dist: bokeh; extra == "all"
133
- Requires-Dist: pydantic; extra == "all"
129
+ Requires-Dist: filetype; extra == "all"
130
+ Requires-Dist: pyyaml; extra == "all"
131
+ Requires-Dist: huggingface_hub; extra == "all"
132
+ Requires-Dist: tinynetrc; extra == "all"
133
+ Requires-Dist: flet[all]; extra == "all"
134
134
  Requires-Dist: tabulate; extra == "all"
135
- Requires-Dist: dask[bag]; extra == "all"
135
+ Requires-Dist: pydantic-settings; extra == "all"
136
+ Requires-Dist: flet-webview; extra == "all"
137
+ Requires-Dist: docker; extra == "all"
138
+ Requires-Dist: Unidecode; extra == "all"
136
139
  Requires-Dist: openpyxl; extra == "all"
137
- Requires-Dist: filetype; extra == "all"
140
+ Requires-Dist: sentence_transformers; extra == "all"
141
+ Requires-Dist: deepmerge; extra == "all"
138
142
  Requires-Dist: pymupdf; extra == "all"
139
- Requires-Dist: docker; extra == "all"
140
- Requires-Dist: flet-webview; extra == "all"
141
- Requires-Dist: dnspython[doh]; extra == "all"
143
+ Requires-Dist: google-auth-oauthlib; extra == "all"
144
+ Requires-Dist: torchaudio; extra == "all"
142
145
  Requires-Dist: logfire; extra == "all"
143
- Requires-Dist: contexttimer; extra == "all"
144
- Requires-Dist: torchvision; extra == "all"
145
- Requires-Dist: huggingface_hub; extra == "all"
146
- Requires-Dist: transformers[sentencepiece]; extra == "all"
146
+ Requires-Dist: diskcache; extra == "all"
147
147
  Requires-Dist: cachetools; extra == "all"
148
- Requires-Dist: httpx; extra == "all"
149
- Requires-Dist: semver; extra == "all"
150
- Requires-Dist: sre_yield; extra == "all"
151
- Requires-Dist: html2text; extra == "all"
152
- Requires-Dist: pyyaml; extra == "all"
153
- Requires-Dist: google-auth; extra == "all"
154
- Requires-Dist: regex; extra == "all"
155
- Requires-Dist: httpx_retries; extra == "all"
148
+ Requires-Dist: setuptools; extra == "all"
149
+ Requires-Dist: distributed; extra == "all"
156
150
  Requires-Dist: tokenizers; extra == "all"
157
- Requires-Dist: sentence_transformers; extra == "all"
158
- Requires-Dist: flet-video; extra == "all"
159
- Requires-Dist: pymupdf4llm; extra == "all"
160
- Requires-Dist: google-auth-oauthlib; extra == "all"
161
- Requires-Dist: pytest-cov; extra == "all"
162
- Requires-Dist: logfire[httpx]; extra == "all"
163
- Requires-Dist: ollama; extra == "all"
164
151
  Requires-Dist: faker; extra == "all"
165
- Requires-Dist: torchaudio; extra == "all"
166
- Requires-Dist: pandas; extra == "all"
167
- Requires-Dist: google-api-python-client; extra == "all"
168
- Requires-Dist: uvicorn[standard]; extra == "all"
169
- Requires-Dist: appdirs; extra == "all"
170
- Requires-Dist: flet[all]; extra == "all"
171
- Requires-Dist: setuptools; extra == "all"
172
152
  Requires-Dist: logfire[fastapi]; extra == "all"
153
+ Requires-Dist: google-auth; extra == "all"
154
+ Requires-Dist: fastapi; extra == "all"
155
+ Requires-Dist: peft; extra == "all"
156
+ Requires-Dist: transformers[sentencepiece]; extra == "all"
173
157
  Requires-Dist: yamlscript; extra == "all"
158
+ Requires-Dist: contexttimer; extra == "all"
159
+ Requires-Dist: torchvision; extra == "all"
160
+ Requires-Dist: appdirs; extra == "all"
161
+ Requires-Dist: google-auth-httplib2; extra == "all"
162
+ Requires-Dist: logfire[httpx]; extra == "all"
163
+ Requires-Dist: pytest-cov; extra == "all"
164
+ Requires-Dist: regex; extra == "all"
165
+ Requires-Dist: pydantic; extra == "all"
174
166
  Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
175
- Requires-Dist: tinynetrc; extra == "all"
176
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
177
- Requires-Dist: Unidecode; extra == "all"
178
- Requires-Dist: pydantic-settings; extra == "all"
179
- Requires-Dist: json_repair; extra == "all"
180
- Requires-Dist: peft; extra == "all"
181
- Requires-Dist: distributed; extra == "all"
182
167
  Requires-Dist: openai; extra == "all"
183
- Requires-Dist: diskcache; extra == "all"
168
+ Requires-Dist: dask[bag]; extra == "all"
169
+ Requires-Dist: pandas; extra == "all"
170
+ Requires-Dist: json_repair; extra == "all"
171
+ Requires-Dist: uvicorn[standard]; extra == "all"
172
+ Requires-Dist: html2text; extra == "all"
173
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
174
+ Requires-Dist: semver; extra == "all"
175
+ Requires-Dist: dnspython[doh]; extra == "all"
176
+ Requires-Dist: ollama; extra == "all"
177
+ Requires-Dist: flet-video; extra == "all"
178
+ Requires-Dist: google-api-python-client; extra == "all"
179
+ Requires-Dist: httpx; extra == "all"
180
+ Requires-Dist: httpx_retries; extra == "all"
181
+ Requires-Dist: sre_yield; extra == "all"
182
+ Requires-Dist: bokeh; extra == "all"
183
+ Requires-Dist: pymupdf4llm; extra == "all"
184
184
  Dynamic: author
185
185
  Dynamic: author-email
186
186
  Dynamic: description
@@ -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.message.answer += response.message.answer
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.message.answer += response.message.answer
93
+ exchange.response = response
90
94
 
91
95
  except Exception as exception:
92
96
  exchange.response.message.set_rcode(dnspython.rcode.SERVFAIL)
@@ -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
- 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.
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.response.answer:
170
- rrset = self.response.answer
171
- ty = self.request.type
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=ty,
209
+ rdtype=rdtype,
180
210
  rdclass=rdclass,
181
-
182
211
  )
183
212
 
184
- return rrset_contrived
213
+ return rrset_surrogate
185
214
  else:
186
- return self.request.question # Solves the issue of digging out the name.
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
@@ -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.response.is_complete:
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.response.is_complete:
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.response.is_complete:
61
+ if exchange.is_complete:
55
62
  return exchange
56
63
 
64
+ self.finalize(exchange)
65
+
57
66
  return exchange
@@ -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.response.is_complete = True
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=} {response.is_complete=} {response.rcode=} {response.rcode_text=} {response.answer=} {response.blocked_by=}...'
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.response.is_complete:
130
+ if not exchange.is_complete:
120
131
  exchange = self.resolve(exchange)
121
- exchange.response.is_complete = True
132
+ self.cache[exchange.key] = exchange.response
122
133
 
123
- self.cache[exchange.key] = exchange.response
134
+ self.log_dns_errors(exchange)
124
135
  self.log_response(exchange)
125
136
 
126
137
  return exchange
@@ -0,0 +1 @@
1
+ 1.3.16
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.3.14
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: deepmerge; extra == "all"
130
- Requires-Dist: google-auth-httplib2; extra == "all"
131
- Requires-Dist: fastapi; extra == "all"
132
- Requires-Dist: bokeh; extra == "all"
133
- Requires-Dist: pydantic; extra == "all"
129
+ Requires-Dist: filetype; extra == "all"
130
+ Requires-Dist: pyyaml; extra == "all"
131
+ Requires-Dist: huggingface_hub; extra == "all"
132
+ Requires-Dist: tinynetrc; extra == "all"
133
+ Requires-Dist: flet[all]; extra == "all"
134
134
  Requires-Dist: tabulate; extra == "all"
135
- Requires-Dist: dask[bag]; extra == "all"
135
+ Requires-Dist: pydantic-settings; extra == "all"
136
+ Requires-Dist: flet-webview; extra == "all"
137
+ Requires-Dist: docker; extra == "all"
138
+ Requires-Dist: Unidecode; extra == "all"
136
139
  Requires-Dist: openpyxl; extra == "all"
137
- Requires-Dist: filetype; extra == "all"
140
+ Requires-Dist: sentence_transformers; extra == "all"
141
+ Requires-Dist: deepmerge; extra == "all"
138
142
  Requires-Dist: pymupdf; extra == "all"
139
- Requires-Dist: docker; extra == "all"
140
- Requires-Dist: flet-webview; extra == "all"
141
- Requires-Dist: dnspython[doh]; extra == "all"
143
+ Requires-Dist: google-auth-oauthlib; extra == "all"
144
+ Requires-Dist: torchaudio; extra == "all"
142
145
  Requires-Dist: logfire; extra == "all"
143
- Requires-Dist: contexttimer; extra == "all"
144
- Requires-Dist: torchvision; extra == "all"
145
- Requires-Dist: huggingface_hub; extra == "all"
146
- Requires-Dist: transformers[sentencepiece]; extra == "all"
146
+ Requires-Dist: diskcache; extra == "all"
147
147
  Requires-Dist: cachetools; extra == "all"
148
- Requires-Dist: httpx; extra == "all"
149
- Requires-Dist: semver; extra == "all"
150
- Requires-Dist: sre_yield; extra == "all"
151
- Requires-Dist: html2text; extra == "all"
152
- Requires-Dist: pyyaml; extra == "all"
153
- Requires-Dist: google-auth; extra == "all"
154
- Requires-Dist: regex; extra == "all"
155
- Requires-Dist: httpx_retries; extra == "all"
148
+ Requires-Dist: setuptools; extra == "all"
149
+ Requires-Dist: distributed; extra == "all"
156
150
  Requires-Dist: tokenizers; extra == "all"
157
- Requires-Dist: sentence_transformers; extra == "all"
158
- Requires-Dist: flet-video; extra == "all"
159
- Requires-Dist: pymupdf4llm; extra == "all"
160
- Requires-Dist: google-auth-oauthlib; extra == "all"
161
- Requires-Dist: pytest-cov; extra == "all"
162
- Requires-Dist: logfire[httpx]; extra == "all"
163
- Requires-Dist: ollama; extra == "all"
164
151
  Requires-Dist: faker; extra == "all"
165
- Requires-Dist: torchaudio; extra == "all"
166
- Requires-Dist: pandas; extra == "all"
167
- Requires-Dist: google-api-python-client; extra == "all"
168
- Requires-Dist: uvicorn[standard]; extra == "all"
169
- Requires-Dist: appdirs; extra == "all"
170
- Requires-Dist: flet[all]; extra == "all"
171
- Requires-Dist: setuptools; extra == "all"
172
152
  Requires-Dist: logfire[fastapi]; extra == "all"
153
+ Requires-Dist: google-auth; extra == "all"
154
+ Requires-Dist: fastapi; extra == "all"
155
+ Requires-Dist: peft; extra == "all"
156
+ Requires-Dist: transformers[sentencepiece]; extra == "all"
173
157
  Requires-Dist: yamlscript; extra == "all"
158
+ Requires-Dist: contexttimer; extra == "all"
159
+ Requires-Dist: torchvision; extra == "all"
160
+ Requires-Dist: appdirs; extra == "all"
161
+ Requires-Dist: google-auth-httplib2; extra == "all"
162
+ Requires-Dist: logfire[httpx]; extra == "all"
163
+ Requires-Dist: pytest-cov; extra == "all"
164
+ Requires-Dist: regex; extra == "all"
165
+ Requires-Dist: pydantic; extra == "all"
174
166
  Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
175
- Requires-Dist: tinynetrc; extra == "all"
176
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
177
- Requires-Dist: Unidecode; extra == "all"
178
- Requires-Dist: pydantic-settings; extra == "all"
179
- Requires-Dist: json_repair; extra == "all"
180
- Requires-Dist: peft; extra == "all"
181
- Requires-Dist: distributed; extra == "all"
182
167
  Requires-Dist: openai; extra == "all"
183
- Requires-Dist: diskcache; extra == "all"
168
+ Requires-Dist: dask[bag]; extra == "all"
169
+ Requires-Dist: pandas; extra == "all"
170
+ Requires-Dist: json_repair; extra == "all"
171
+ Requires-Dist: uvicorn[standard]; extra == "all"
172
+ Requires-Dist: html2text; extra == "all"
173
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
174
+ Requires-Dist: semver; extra == "all"
175
+ Requires-Dist: dnspython[doh]; extra == "all"
176
+ Requires-Dist: ollama; extra == "all"
177
+ Requires-Dist: flet-video; extra == "all"
178
+ Requires-Dist: google-api-python-client; extra == "all"
179
+ Requires-Dist: httpx; extra == "all"
180
+ Requires-Dist: httpx_retries; extra == "all"
181
+ Requires-Dist: sre_yield; extra == "all"
182
+ Requires-Dist: bokeh; extra == "all"
183
+ Requires-Dist: pymupdf4llm; extra == "all"
184
184
  Dynamic: author
185
185
  Dynamic: author-email
186
186
  Dynamic: description
@@ -1 +0,0 @@
1
- 1.3.14
File without changes
File without changes
@@ -14,61 +14,61 @@ pydantic-ai[logfire,openai]
14
14
  ollama
15
15
 
16
16
  [all]
17
- deepmerge
18
- google-auth-httplib2
19
- fastapi
20
- bokeh
21
- pydantic
17
+ filetype
18
+ pyyaml
19
+ huggingface_hub
20
+ tinynetrc
21
+ flet[all]
22
22
  tabulate
23
- dask[bag]
23
+ pydantic-settings
24
+ flet-webview
25
+ docker
26
+ Unidecode
24
27
  openpyxl
25
- filetype
28
+ sentence_transformers
29
+ deepmerge
26
30
  pymupdf
27
- docker
28
- flet-webview
29
- dnspython[doh]
31
+ google-auth-oauthlib
32
+ torchaudio
30
33
  logfire
31
- contexttimer
32
- torchvision
33
- huggingface_hub
34
- transformers[sentencepiece]
34
+ diskcache
35
35
  cachetools
36
- httpx
37
- semver
38
- sre_yield
39
- html2text
40
- pyyaml
41
- google-auth
42
- regex
43
- httpx_retries
36
+ setuptools
37
+ distributed
44
38
  tokenizers
45
- sentence_transformers
46
- flet-video
47
- pymupdf4llm
48
- google-auth-oauthlib
49
- pytest-cov
50
- logfire[httpx]
51
- ollama
52
39
  faker
53
- torchaudio
54
- pandas
55
- google-api-python-client
56
- uvicorn[standard]
57
- appdirs
58
- flet[all]
59
- setuptools
60
40
  logfire[fastapi]
41
+ google-auth
42
+ fastapi
43
+ peft
44
+ transformers[sentencepiece]
61
45
  yamlscript
46
+ contexttimer
47
+ torchvision
48
+ appdirs
49
+ google-auth-httplib2
50
+ logfire[httpx]
51
+ pytest-cov
52
+ regex
53
+ pydantic
62
54
  pydevd-pycharm~=251.25410.159
63
- tinynetrc
64
- pydantic-ai[logfire,openai]
65
- Unidecode
66
- pydantic-settings
67
- json_repair
68
- peft
69
- distributed
70
55
  openai
71
- diskcache
56
+ dask[bag]
57
+ pandas
58
+ json_repair
59
+ uvicorn[standard]
60
+ html2text
61
+ pydantic-ai[logfire,openai]
62
+ semver
63
+ dnspython[doh]
64
+ ollama
65
+ flet-video
66
+ google-api-python-client
67
+ httpx
68
+ httpx_retries
69
+ sre_yield
70
+ bokeh
71
+ pymupdf4llm
72
72
 
73
73
  [api]
74
74
  fastapi
File without changes
File without changes
File without changes