fmtr.tools 1.3.7__py3-none-any.whl → 1.3.8__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.

@@ -1,6 +1,8 @@
1
+ import cachetools
2
+ from datetime import timedelta, datetime
1
3
  from diskcache import Cache
2
4
 
3
- from fmtr.tools import logger, Path
5
+ from fmtr.tools import logger, Path, Constants
4
6
 
5
7
 
6
8
  class Dump(dict):
@@ -31,9 +33,9 @@ class Disk(Cache):
31
33
  if not path.parent.exists():
32
34
  raise FileNotFoundError(f"Directory {path.parent=} does not exist")
33
35
  if path and not path.exists():
34
- logger.warning(f'Cache does not exist. Will be created. "{path=}"...')
36
+ logger.warning(f'Cache does not exist. Will be created. {str(path)=}...')
35
37
 
36
- logger.info(f'Initializing Disk Cache at path "{path=}"...')
38
+ logger.info(f'Initializing Disk Cache {str(path)=}...')
37
39
 
38
40
  super().__init__(directory=str(path / self.ROOT_KEY), **settings)
39
41
 
@@ -95,7 +97,101 @@ class Disk(Cache):
95
97
  return self.dump()
96
98
 
97
99
 
100
+ class TLRU(cachetools.TLRUCache):
101
+ """
102
+
103
+ Subclass to include logging and simplify global TTU
104
+
105
+ """
106
+ MASK_MAPPING = '{key} ' + Constants.ARROW + ' {value}'
107
+
108
+ def __init__(self, maxsize=1_024, timer=datetime.now, getsizeof=None, ttu_static=None, desc=None):
109
+ """
110
+
111
+ Add overridable TTU method
112
+
113
+ """
114
+ super().__init__(maxsize=maxsize, ttu=self.get_ttu, timer=timer, getsizeof=getsizeof)
115
+ self.ttu_static = ttu_static
116
+ self.desc = desc
117
+
118
+ @property
119
+ def cache_desc(self):
120
+ """
121
+
122
+ Friendly description of cache
123
+
124
+ """
125
+ desc = self.desc or self.__class__.__name__
126
+ return desc
127
+
128
+ def get_ttu(self, _key, value, now) -> float | timedelta:
129
+ """
130
+
131
+ Default implementation just adds on the static TTU
132
+
133
+ """
134
+ return now + self.ttu_static
135
+
136
+ def expire(self, time=None):
137
+ """
138
+
139
+ Log expiry
140
+
141
+ """
142
+ items = super().expire(time)
143
+ if not items:
144
+ return items
145
+
146
+ with logger.span(f'{self.desc} cache expiry {len(items)=}...'):
147
+ for key, value in items:
148
+ logger.debug(self.MASK_MAPPING.format(key=key, value=value))
149
+
150
+ return items
151
+
152
+ def popitem(self):
153
+ """
154
+
155
+ Log eviction
156
+
157
+ """
158
+ key, value = super().popitem()
159
+ logger.debug(f'{self.desc} cache eviction: {self.MASK_MAPPING.format(key=key, value=value)}')
160
+ return key, value
161
+
162
+ def dump(self):
163
+ """
164
+
165
+ Dump contents
166
+
167
+ """
168
+ data = Dump(self.items())
169
+ return data
170
+
171
+ @property
172
+ def data(self):
173
+ """
174
+
175
+ Dump as property
176
+
177
+ """
178
+ return self.dump()
179
+
180
+
181
+
98
182
  if __name__ == '__main__':
183
+ sec10 = timedelta(seconds=10)
184
+ c = TLRU(ttu_static=sec10, maxsize=2, desc='Test Data')
185
+ c['test'] = 'val'
186
+ c['test2'] = 'val2'
187
+ c['test3'] = 'val3'
188
+ c
189
+
190
+
191
+
192
+
193
+
194
+
99
195
  path_tmp_cache = Path.cwd().parent.parent / 'data' / 'cache'
100
196
  tc = Disk(path_tmp_cache)
101
197
 
fmtr/tools/constants.py CHANGED
@@ -14,6 +14,9 @@ class Constants:
14
14
  DATETIME_NOW_STR = DATETIME_NOW.strftime(DATETIME_FILENAME_FORMAT)
15
15
  SERIALIZATION_INDENT = 4
16
16
 
17
+ ARROW = '→'
18
+ ARROW_SEP = f' {ARROW} '
19
+
17
20
  FMTR_LOG_LEVEL_KEY = 'FMTR_LOG_LEVEL'
18
21
  FMTR_OBS_API_KEY_KEY = 'FMTR_OBS_API_KEY'
19
22
  FMTR_OBS_HOST = 'obs.sv.fmtr.dev'
@@ -31,6 +34,7 @@ class Constants:
31
34
  FILENAME_CONFIG = 'settings.yaml'
32
35
  DIR_NAME_REPO = 'repo'
33
36
  DIR_NAME_DATA = 'data'
37
+ DIR_NAME_CACHE = 'cache'
34
38
  DIR_NAME_ARTIFACT = 'artifact'
35
39
  DIR_NAME_SOURCE = 'source'
36
40
  FILENAME_VERSION = 'version'
@@ -52,7 +52,7 @@ def get_logger(name, version=None, host=Constants.FMTR_OBS_HOST, key=None, org=C
52
52
  lev_name_otel = logfire._internal.constants.NUMBER_TO_LEVEL[lev_num_otel]
53
53
 
54
54
  console_opts = logfire.ConsoleOptions(
55
- colors='always' if environment_tools.IS_DEBUG else 'auto',
55
+ colors='always',
56
56
  min_log_level=lev_name_otel,
57
57
  )
58
58
 
@@ -305,6 +305,16 @@ class PackagePaths(FromCallerMixin):
305
305
 
306
306
  return self.dev / Constants.DIR_NAME_REPO / self.name_ns / self.dir_name_data
307
307
 
308
+ @property
309
+ def cache(self) -> Path:
310
+ """
311
+
312
+ Path of cache directory.
313
+
314
+ """
315
+
316
+ return self.data / Constants.DIR_NAME_CACHE
317
+
308
318
  @property
309
319
  def artifact(self) -> Path:
310
320
  """
@@ -1,9 +1,9 @@
1
+ import regex as re
1
2
  from dataclasses import dataclass, asdict
2
3
  from functools import cached_property
3
4
  from typing import List, Any
4
5
 
5
- import regex as re
6
-
6
+ from fmtr.tools import Constants
7
7
  from fmtr.tools.logging_tools import logger
8
8
  from fmtr.tools.string_tools import join
9
9
 
@@ -210,7 +210,7 @@ class Transformer:
210
210
  previous = key
211
211
 
212
212
  def get_history_str():
213
- return join(history, sep=' → ')
213
+ return join(history, sep=Constants.ARROW_SEP)
214
214
 
215
215
  while True:
216
216
  if previous in history:
fmtr/tools/version CHANGED
@@ -1 +1 @@
1
- 1.3.7
1
+ 1.3.8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmtr.tools
3
- Version: 1.3.7
3
+ Version: 1.3.8
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
@@ -97,6 +97,7 @@ Requires-Dist: google-auth-httplib2; extra == "google-api"
97
97
  Requires-Dist: google-api-python-client; extra == "google-api"
98
98
  Provides-Extra: caching
99
99
  Requires-Dist: diskcache; extra == "caching"
100
+ Requires-Dist: cachetools; extra == "caching"
100
101
  Provides-Extra: pdf
101
102
  Requires-Dist: pymupdf; extra == "pdf"
102
103
  Requires-Dist: pydantic; extra == "pdf"
@@ -130,60 +131,61 @@ Requires-Dist: logfire[httpx]; extra == "http"
130
131
  Provides-Extra: setup
131
132
  Requires-Dist: setuptools; extra == "setup"
132
133
  Provides-Extra: all
134
+ Requires-Dist: html2text; extra == "all"
135
+ Requires-Dist: appdirs; extra == "all"
136
+ Requires-Dist: httpx_retries; extra == "all"
137
+ Requires-Dist: diskcache; extra == "all"
138
+ Requires-Dist: google-auth; extra == "all"
139
+ Requires-Dist: docker; extra == "all"
140
+ Requires-Dist: faker; extra == "all"
141
+ Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
142
+ Requires-Dist: sre_yield; extra == "all"
133
143
  Requires-Dist: fastapi; extra == "all"
134
- Requires-Dist: flet-video; extra == "all"
135
144
  Requires-Dist: bokeh; extra == "all"
136
- Requires-Dist: google-auth-oauthlib; extra == "all"
137
- Requires-Dist: pydantic-settings; extra == "all"
138
- Requires-Dist: json_repair; extra == "all"
139
- Requires-Dist: html2text; extra == "all"
140
- Requires-Dist: filetype; extra == "all"
141
- Requires-Dist: torchaudio; extra == "all"
145
+ Requires-Dist: huggingface_hub; extra == "all"
146
+ Requires-Dist: pymupdf; extra == "all"
147
+ Requires-Dist: google-api-python-client; extra == "all"
148
+ Requires-Dist: pydantic; extra == "all"
142
149
  Requires-Dist: dnspython[doh]; extra == "all"
143
150
  Requires-Dist: pandas; extra == "all"
144
- Requires-Dist: google-api-python-client; extra == "all"
145
- Requires-Dist: diskcache; extra == "all"
151
+ Requires-Dist: yamlscript; extra == "all"
152
+ Requires-Dist: openai; extra == "all"
153
+ Requires-Dist: deepmerge; extra == "all"
154
+ Requires-Dist: filetype; extra == "all"
155
+ Requires-Dist: regex; extra == "all"
146
156
  Requires-Dist: peft; extra == "all"
157
+ Requires-Dist: transformers[sentencepiece]; extra == "all"
158
+ Requires-Dist: tinynetrc; extra == "all"
159
+ Requires-Dist: flet-video; extra == "all"
160
+ Requires-Dist: logfire; extra == "all"
161
+ Requires-Dist: google-auth-oauthlib; extra == "all"
162
+ Requires-Dist: logfire[fastapi]; extra == "all"
163
+ Requires-Dist: httpx; extra == "all"
164
+ Requires-Dist: pytest-cov; extra == "all"
165
+ Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
166
+ Requires-Dist: distributed; extra == "all"
167
+ Requires-Dist: contexttimer; extra == "all"
147
168
  Requires-Dist: pymupdf4llm; extra == "all"
148
169
  Requires-Dist: setuptools; extra == "all"
149
- Requires-Dist: pymupdf; extra == "all"
150
- Requires-Dist: flet[all]; extra == "all"
151
- Requires-Dist: tabulate; extra == "all"
152
- Requires-Dist: tokenizers; extra == "all"
153
- Requires-Dist: huggingface_hub; extra == "all"
154
- Requires-Dist: sentence_transformers; extra == "all"
155
- Requires-Dist: flet-webview; extra == "all"
170
+ Requires-Dist: uvicorn[standard]; extra == "all"
156
171
  Requires-Dist: logfire[httpx]; extra == "all"
172
+ Requires-Dist: json_repair; extra == "all"
173
+ Requires-Dist: pyyaml; extra == "all"
174
+ Requires-Dist: openpyxl; extra == "all"
157
175
  Requires-Dist: semver; extra == "all"
158
- Requires-Dist: httpx_retries; extra == "all"
159
- Requires-Dist: ollama; extra == "all"
160
- Requires-Dist: regex; extra == "all"
161
- Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
162
- Requires-Dist: distributed; extra == "all"
163
- Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
176
+ Requires-Dist: torchaudio; extra == "all"
177
+ Requires-Dist: pydantic-settings; extra == "all"
164
178
  Requires-Dist: dask[bag]; extra == "all"
165
- Requires-Dist: deepmerge; extra == "all"
166
- Requires-Dist: openai; extra == "all"
167
- Requires-Dist: pytest-cov; extra == "all"
168
- Requires-Dist: faker; extra == "all"
179
+ Requires-Dist: tokenizers; extra == "all"
180
+ Requires-Dist: tabulate; extra == "all"
181
+ Requires-Dist: cachetools; extra == "all"
182
+ Requires-Dist: torchvision; extra == "all"
169
183
  Requires-Dist: Unidecode; extra == "all"
170
- Requires-Dist: logfire[fastapi]; extra == "all"
171
- Requires-Dist: appdirs; extra == "all"
172
- Requires-Dist: transformers[sentencepiece]; extra == "all"
184
+ Requires-Dist: sentence_transformers; extra == "all"
173
185
  Requires-Dist: google-auth-httplib2; extra == "all"
174
- Requires-Dist: tinynetrc; extra == "all"
175
- Requires-Dist: pyyaml; extra == "all"
176
- Requires-Dist: pydantic; extra == "all"
177
- Requires-Dist: openpyxl; extra == "all"
178
- Requires-Dist: uvicorn[standard]; extra == "all"
179
- Requires-Dist: torchvision; extra == "all"
180
- Requires-Dist: yamlscript; extra == "all"
181
- Requires-Dist: contexttimer; extra == "all"
182
- Requires-Dist: docker; extra == "all"
183
- Requires-Dist: logfire; extra == "all"
184
- Requires-Dist: sre_yield; extra == "all"
185
- Requires-Dist: httpx; extra == "all"
186
- Requires-Dist: google-auth; extra == "all"
186
+ Requires-Dist: flet[all]; extra == "all"
187
+ Requires-Dist: flet-webview; extra == "all"
188
+ Requires-Dist: ollama; extra == "all"
187
189
  Dynamic: author
188
190
  Dynamic: author-email
189
191
  Dynamic: description
@@ -2,8 +2,8 @@ fmtr/tools/__init__.py,sha256=vCxKR0SymygpO-EtCGPmGZEQv5O1goM7Jr8P4m-eJsQ,5676
2
2
  fmtr/tools/api_tools.py,sha256=w8Zrp_EwN5KlUghwLoTCXo4z1irg5tAsReCqDLjASfE,2133
3
3
  fmtr/tools/async_tools.py,sha256=ewz757WcveQJd-G5SVr2JDOQVbdLGecCgl-tsBGVZz4,284
4
4
  fmtr/tools/augmentation_tools.py,sha256=-6ESbO4CDlKqVOV1J1V6qBeoBMzbFIinkDHRHnCBej0,55
5
- fmtr/tools/caching_tools.py,sha256=UOCYUNvLQ-NofR_dhqBmZF96-HRPf4At5MmxVk3gAIk,2943
6
- fmtr/tools/constants.py,sha256=FOHvjovW_pHIvym9OXKZnztPvPVvudyG5S01i7XtXKE,1352
5
+ fmtr/tools/caching_tools.py,sha256=BZ5HSzcyCSnvJ6h5xtu5jELPSPDazVsMU7q7OX_oKBE,4886
6
+ fmtr/tools/constants.py,sha256=QHJWu2UZwdV1ceGKT-pq-eYGPw4t7aFxb_W8r4G9VVk,1429
7
7
  fmtr/tools/data_modelling_tools.py,sha256=0BFm-F_cYzVTxftWQwORkPd0FM2BTLVh9-s0-rTTFoo,1744
8
8
  fmtr/tools/dataclass_tools.py,sha256=0Gt6KeLhtPgubo_2tYkIVqB8oQ91Qzag8OAGZDdjvMU,1209
9
9
  fmtr/tools/datatype_tools.py,sha256=3P4AWIFGkJ-UqvXlj0Jc9IvkIIgTOE9jRrOk3NVbpH8,1508
@@ -22,7 +22,7 @@ fmtr/tools/interface_tools.py,sha256=JVYUV7wuMr24BORuUtNaBlTeBFt8VDGJ3HPRajd7Y_4
22
22
  fmtr/tools/iterator_tools.py,sha256=xj5f0c7LgLK53dddRRRJxBoLaBzlZoQS3_GfmpDPMoo,1311
23
23
  fmtr/tools/json_fix_tools.py,sha256=vNSlswVQnujPmKEqDjFJcO901mjMyv59q3awsT7mlhs,477
24
24
  fmtr/tools/json_tools.py,sha256=WkFc5q7oqMtcFejhN1K5zQFULa9TdLOup83Fr0saDRY,348
25
- fmtr/tools/logging_tools.py,sha256=XYAkthur0Nrc9uCuw7QYupfRWt-MSdcb9Y1YH1GcTKM,2374
25
+ fmtr/tools/logging_tools.py,sha256=rVNoq46hsO3wF1dnpaGZxu_usFV_SLy16MlKbhivN_M,2332
26
26
  fmtr/tools/merging_tools.py,sha256=KDxCEFJEQJEwGw1qGKAgR55uUE2X2S5NWLKcfHRmX_k,227
27
27
  fmtr/tools/metric_tools.py,sha256=Lvia5CGFRIfrDFA8s37btIfTU5zHbo04cPJdAMtbndQ,272
28
28
  fmtr/tools/name_tools.py,sha256=5CB_phqhHjl66iI8oLxOGPF2odC1apdul-M8Fv2xBhs,5514
@@ -30,7 +30,7 @@ fmtr/tools/netrc_tools.py,sha256=PpNpz_mWlQi6VHGromKwFfTyLpHUXsd4LY6-OKLCbeI,376
30
30
  fmtr/tools/openai_tools.py,sha256=6SUgejgzUzmlKKct2_ePXntvMegu3FJgfk9x7aqtqYc,742
31
31
  fmtr/tools/packaging_tools.py,sha256=FlgOTnDRHZWQL2iR-wucTsyGEHRE-MlddKL30MPmUqE,253
32
32
  fmtr/tools/parallel_tools.py,sha256=QEb_gN1StkxsqYaH4HSjiJX8Y3gpb2uKNsOzG4uFpaM,3071
33
- fmtr/tools/pattern_tools.py,sha256=EYKtBPnoBwTpvTPZWLYGrRFIGnSg2R6Tv2VmxvoJDog,5959
33
+ fmtr/tools/pattern_tools.py,sha256=Rmengjn_Ya246mlkAVR9fxVxXJUqEHWdDYu8Hsl9pXw,6003
34
34
  fmtr/tools/pdf_tools.py,sha256=xvv9B84uAF81rFJRnXhSsxYuP42vY9ZdPVFrSMVe8G8,4069
35
35
  fmtr/tools/platform_tools.py,sha256=7p69CmAHe_sF68Fx9uVhns1k5EewTHTWgUYzkl6ZQKA,308
36
36
  fmtr/tools/process_tools.py,sha256=Ysh5Dk2QFBhXQerArjKdt7xZd3JrN5Ho02AaOjH0Nnw,1425
@@ -44,7 +44,7 @@ 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=D6_5-XKSRJ7uMrgd-I-BK8oDjUQ8WiJUgC32EIRj8P4,5
47
+ fmtr/tools/version,sha256=TcdU53CiqvG9NX8A4sH_s0eyfztzmjd5EWmCXTCwpQE,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
@@ -63,7 +63,7 @@ fmtr/tools/entrypoints/remote_debug_test.py,sha256=wmKg9o2pQq7eqeHmaO8oviujNgtns
63
63
  fmtr/tools/entrypoints/shell_debug.py,sha256=0No3tAg9Ri4_vvSlQCUtAY-11HR0nE45i0VVtiAViwY,106
64
64
  fmtr/tools/path_tools/__init__.py,sha256=v5CpmzXq5Ii90FtcmxAJKiLxmguZMrPnQ_HdT872Np0,443
65
65
  fmtr/tools/path_tools/app_path_tools.py,sha256=JrJvtTDd_gkCKcZtBCDTMktsM77PZwGV_hzQX0g5GU8,1722
66
- fmtr/tools/path_tools/path_tools.py,sha256=8WBzNctpds5tVT1-FcizAORrBCGlkUbPcfOel-Js7ps,7878
66
+ fmtr/tools/path_tools/path_tools.py,sha256=eh30PpmH0wopy0wNWuPT84cmXY1EvqsTSDT7AV_GPOY,8034
67
67
  fmtr/tools/path_tools/type_path_tools.py,sha256=Zgs-ek-GXRKDIlVDGdg3muB0PIxTg2ba0NeHw6y8FWQ,40
68
68
  fmtr/tools/setup_tools/__init__.py,sha256=fUCjzyE4JBPEOZhC4B--VPj_eOCkQb1QG5PIgiGj03U,386
69
69
  fmtr/tools/setup_tools/setup_tools.py,sha256=7Z6jlU6UE8P4cntGQ_hJR7hGvoqwh15xzZY63cnxG7E,10363
@@ -75,9 +75,9 @@ fmtr/tools/tests/test_environment.py,sha256=iHaiMQfECYZPkPKwfuIZV9uHuWe3aE-p_dN_
75
75
  fmtr/tools/tests/test_json.py,sha256=IeSP4ziPvRcmS8kq7k9tHonC9rN5YYq9GSNT2ul6Msk,287
76
76
  fmtr/tools/tests/test_path.py,sha256=AkZQa6_8BQ-VaCyL_J-iKmdf2ZaM-xFYR37Kun3k4_g,2188
77
77
  fmtr/tools/tests/test_yaml.py,sha256=jc0TwwKu9eC0LvFGNMERdgBue591xwLxYXFbtsRwXVM,287
78
- fmtr_tools-1.3.7.dist-info/licenses/LICENSE,sha256=FW9aa6vVN5IjRQWLT43hs4_koYSmpcbIovlKeAJ0_cI,10757
79
- fmtr_tools-1.3.7.dist-info/METADATA,sha256=k4qiny1GqJwYqNMhP27A0fmtJMk_z70R8KisCzcms-c,16060
80
- fmtr_tools-1.3.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
81
- fmtr_tools-1.3.7.dist-info/entry_points.txt,sha256=h-r__Xh5njtFqreMLg6cGuTFS4Qh-QqJPU1HB-_BS-Q,357
82
- fmtr_tools-1.3.7.dist-info/top_level.txt,sha256=LXem9xCgNOD72tE2gRKESdiQTL902mfFkwWb6-dlwEE,5
83
- fmtr_tools-1.3.7.dist-info/RECORD,,
78
+ fmtr_tools-1.3.8.dist-info/licenses/LICENSE,sha256=FW9aa6vVN5IjRQWLT43hs4_koYSmpcbIovlKeAJ0_cI,10757
79
+ fmtr_tools-1.3.8.dist-info/METADATA,sha256=m6mbZy-IYtew40xeEmDdyBYhxDLdfiLg_hhR_H2xMEc,16148
80
+ fmtr_tools-1.3.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
81
+ fmtr_tools-1.3.8.dist-info/entry_points.txt,sha256=h-r__Xh5njtFqreMLg6cGuTFS4Qh-QqJPU1HB-_BS-Q,357
82
+ fmtr_tools-1.3.8.dist-info/top_level.txt,sha256=LXem9xCgNOD72tE2gRKESdiQTL902mfFkwWb6-dlwEE,5
83
+ fmtr_tools-1.3.8.dist-info/RECORD,,