fal 1.0.1__py3-none-any.whl → 1.0.3__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 fal might be problematic. Click here for more details.
- fal/_fal_version.py +2 -2
- fal/_serialization.py +4 -1
- fal/api.py +0 -1
- fal/cli/parser.py +23 -0
- fal/toolkit/utils/download_utils.py +53 -14
- fal/utils.py +0 -1
- {fal-1.0.1.dist-info → fal-1.0.3.dist-info}/METADATA +3 -3
- {fal-1.0.1.dist-info → fal-1.0.3.dist-info}/RECORD +11 -11
- {fal-1.0.1.dist-info → fal-1.0.3.dist-info}/WHEEL +0 -0
- {fal-1.0.1.dist-info → fal-1.0.3.dist-info}/entry_points.txt +0 -0
- {fal-1.0.1.dist-info → fal-1.0.3.dist-info}/top_level.txt +0 -0
fal/_fal_version.py
CHANGED
fal/_serialization.py
CHANGED
|
@@ -195,7 +195,10 @@ def _patch_rlock() -> None:
|
|
|
195
195
|
|
|
196
196
|
|
|
197
197
|
def _patch_console_thread_locals() -> None:
|
|
198
|
-
|
|
198
|
+
try:
|
|
199
|
+
from rich.console import ConsoleThreadLocals
|
|
200
|
+
except ModuleNotFoundError:
|
|
201
|
+
return
|
|
199
202
|
|
|
200
203
|
def create_locals(kwargs: dict) -> ConsoleThreadLocals:
|
|
201
204
|
return ConsoleThreadLocals(**kwargs)
|
fal/api.py
CHANGED
|
@@ -282,7 +282,6 @@ def _handle_grpc_error():
|
|
|
282
282
|
"This is likely due to resource overflow. "
|
|
283
283
|
"You can try again by setting a bigger `machine_type`"
|
|
284
284
|
)
|
|
285
|
-
|
|
286
285
|
elif e.code() == grpc.StatusCode.INVALID_ARGUMENT and (
|
|
287
286
|
"The function function could not be deserialized" in e.details()
|
|
288
287
|
):
|
fal/cli/parser.py
CHANGED
|
@@ -51,6 +51,22 @@ class DictAction(argparse.Action):
|
|
|
51
51
|
setattr(args, self.dest, d)
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def _find_parser(parser, func):
|
|
55
|
+
defaults = parser._defaults
|
|
56
|
+
if not func or func == defaults.get("func"):
|
|
57
|
+
return parser
|
|
58
|
+
|
|
59
|
+
actions = parser._actions
|
|
60
|
+
for action in actions:
|
|
61
|
+
if not isinstance(action.choices, dict):
|
|
62
|
+
continue
|
|
63
|
+
for subparser in action.choices.values():
|
|
64
|
+
par = _find_parser(subparser, func)
|
|
65
|
+
if par:
|
|
66
|
+
return par
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
|
|
54
70
|
class FalParser(argparse.ArgumentParser):
|
|
55
71
|
def __init__(self, *args, **kwargs):
|
|
56
72
|
kwargs.setdefault("formatter_class", rich_argparse.RawTextRichHelpFormatter)
|
|
@@ -61,6 +77,13 @@ class FalParser(argparse.ArgumentParser):
|
|
|
61
77
|
self._print_message(message, sys.stderr)
|
|
62
78
|
raise FalParserExit(status)
|
|
63
79
|
|
|
80
|
+
def parse_args(self, args=None, namespace=None):
|
|
81
|
+
args, argv = self.parse_known_args(args, namespace)
|
|
82
|
+
if argv:
|
|
83
|
+
parser = _find_parser(self, getattr(args, "func", None)) or self
|
|
84
|
+
parser.error("unrecognized arguments: %s" % " ".join(argv))
|
|
85
|
+
return args
|
|
86
|
+
|
|
64
87
|
|
|
65
88
|
class FalClientParser(FalParser):
|
|
66
89
|
def __init__(self, *args, **kwargs):
|
|
@@ -4,7 +4,6 @@ import hashlib
|
|
|
4
4
|
import shutil
|
|
5
5
|
import subprocess
|
|
6
6
|
import sys
|
|
7
|
-
from functools import lru_cache
|
|
8
7
|
from pathlib import Path, PurePath
|
|
9
8
|
from tempfile import TemporaryDirectory
|
|
10
9
|
from urllib.parse import urlparse
|
|
@@ -40,8 +39,10 @@ def _hash_url(url: str) -> str:
|
|
|
40
39
|
return hashlib.sha256(url.encode("utf-8")).hexdigest()
|
|
41
40
|
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
def _get_remote_file_properties(
|
|
43
|
+
url: str,
|
|
44
|
+
request_headers: dict[str, str] | None = None,
|
|
45
|
+
) -> tuple[str, int]:
|
|
45
46
|
"""Retrieves the file name and content length of a remote file.
|
|
46
47
|
|
|
47
48
|
This function sends an HTTP request to the remote URL and retrieves the
|
|
@@ -60,11 +61,15 @@ def _get_remote_file_properties(url: str) -> tuple[str, int]:
|
|
|
60
61
|
|
|
61
62
|
Args:
|
|
62
63
|
url: The URL of the remote file.
|
|
64
|
+
request_headers: A dictionary containing additional headers to be included in
|
|
65
|
+
the HTTP request.
|
|
63
66
|
|
|
64
67
|
Returns:
|
|
65
68
|
A tuple containing the file name and the content length of the remote file.
|
|
66
69
|
"""
|
|
67
|
-
|
|
70
|
+
headers = {**TEMP_HEADERS, **(request_headers or {})}
|
|
71
|
+
request = Request(url, headers=headers)
|
|
72
|
+
|
|
68
73
|
with urlopen(request) as response:
|
|
69
74
|
file_name = response.headers.get_filename()
|
|
70
75
|
content_length = int(response.headers.get("Content-Length", -1))
|
|
@@ -81,7 +86,9 @@ def _get_remote_file_properties(url: str) -> tuple[str, int]:
|
|
|
81
86
|
return file_name, content_length
|
|
82
87
|
|
|
83
88
|
|
|
84
|
-
def _file_content_length_matches(
|
|
89
|
+
def _file_content_length_matches(
|
|
90
|
+
url: str, file_path: Path, request_headers: dict[str, str] | None = None
|
|
91
|
+
) -> bool:
|
|
85
92
|
"""Check if the local file's content length matches the expected remote
|
|
86
93
|
file's content length.
|
|
87
94
|
|
|
@@ -95,13 +102,15 @@ def _file_content_length_matches(url: str, file_path: Path) -> bool:
|
|
|
95
102
|
Args:
|
|
96
103
|
url: The URL of the remote file.
|
|
97
104
|
file_path: The local path to the file being compared.
|
|
105
|
+
request_headers: A dictionary containing additional headers to be included in
|
|
106
|
+
the HTTP request.
|
|
98
107
|
|
|
99
108
|
Returns:
|
|
100
109
|
bool: `True` if the local file's content length matches the remote file's
|
|
101
110
|
content length, `False` otherwise.
|
|
102
111
|
"""
|
|
103
112
|
local_file_content_length = file_path.stat().st_size
|
|
104
|
-
remote_file_content_length = _get_remote_file_properties(url)[1]
|
|
113
|
+
remote_file_content_length = _get_remote_file_properties(url, request_headers)[1]
|
|
105
114
|
|
|
106
115
|
return local_file_content_length == remote_file_content_length
|
|
107
116
|
|
|
@@ -111,6 +120,7 @@ def download_file(
|
|
|
111
120
|
target_dir: str | Path,
|
|
112
121
|
*,
|
|
113
122
|
force: bool = False,
|
|
123
|
+
request_headers: dict[str, str] | None = None,
|
|
114
124
|
) -> Path:
|
|
115
125
|
"""Downloads a file from the specified URL to the target directory.
|
|
116
126
|
|
|
@@ -134,6 +144,9 @@ def download_file(
|
|
|
134
144
|
force: If `True`, the file is downloaded even if it already exists locally and
|
|
135
145
|
its content length matches the expected content length from the remote file.
|
|
136
146
|
Defaults to `False`.
|
|
147
|
+
request_headers: A dictionary containing additional headers to be included in
|
|
148
|
+
the HTTP request. Defaults to `None`.
|
|
149
|
+
|
|
137
150
|
|
|
138
151
|
Returns:
|
|
139
152
|
A Path object representing the full path to the downloaded file.
|
|
@@ -142,7 +155,11 @@ def download_file(
|
|
|
142
155
|
ValueError: If the provided `file_name` contains a forward slash ('/').
|
|
143
156
|
DownloadError: If an error occurs during the download process.
|
|
144
157
|
"""
|
|
145
|
-
|
|
158
|
+
try:
|
|
159
|
+
file_name = _get_remote_file_properties(url, request_headers)[0]
|
|
160
|
+
except Exception as e:
|
|
161
|
+
raise DownloadError(f"Failed to get remote file properties for {url}") from e
|
|
162
|
+
|
|
146
163
|
if "/" in file_name:
|
|
147
164
|
raise ValueError(f"File name '{file_name}' cannot contain a slash.")
|
|
148
165
|
|
|
@@ -156,7 +173,7 @@ def download_file(
|
|
|
156
173
|
|
|
157
174
|
if (
|
|
158
175
|
target_path.exists()
|
|
159
|
-
and _file_content_length_matches(url, target_path)
|
|
176
|
+
and _file_content_length_matches(url, target_path, request_headers)
|
|
160
177
|
and not force
|
|
161
178
|
):
|
|
162
179
|
return target_path
|
|
@@ -170,7 +187,9 @@ def download_file(
|
|
|
170
187
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
171
188
|
|
|
172
189
|
try:
|
|
173
|
-
_download_file_python(
|
|
190
|
+
_download_file_python(
|
|
191
|
+
url=url, target_path=target_path, request_headers=request_headers
|
|
192
|
+
)
|
|
174
193
|
except Exception as e:
|
|
175
194
|
msg = f"Failed to download {url} to {target_path}"
|
|
176
195
|
|
|
@@ -181,13 +200,17 @@ def download_file(
|
|
|
181
200
|
return target_path
|
|
182
201
|
|
|
183
202
|
|
|
184
|
-
def _download_file_python(
|
|
203
|
+
def _download_file_python(
|
|
204
|
+
url: str, target_path: Path | str, request_headers: dict[str, str] | None = None
|
|
205
|
+
) -> Path:
|
|
185
206
|
"""Download a file from a given URL and save it to a specified path using a
|
|
186
207
|
Python interface.
|
|
187
208
|
|
|
188
209
|
Args:
|
|
189
210
|
url: The URL of the file to be downloaded.
|
|
190
211
|
target_path: The path where the downloaded file will be saved.
|
|
212
|
+
request_headers: A dictionary containing additional headers to be included in
|
|
213
|
+
the HTTP request. Defaults to `None`.
|
|
191
214
|
|
|
192
215
|
Returns:
|
|
193
216
|
The path where the downloaded file has been saved.
|
|
@@ -199,11 +222,14 @@ def _download_file_python(url: str, target_path: Path | str) -> Path:
|
|
|
199
222
|
try:
|
|
200
223
|
file_path = temp_file.name
|
|
201
224
|
|
|
202
|
-
for
|
|
225
|
+
for progress, total_size in _stream_url_data_to_file(
|
|
226
|
+
url, temp_file.name, request_headers=request_headers
|
|
227
|
+
):
|
|
203
228
|
if total_size:
|
|
204
229
|
progress_msg = f"Downloading {url} ... {progress:.2%}"
|
|
205
230
|
else:
|
|
206
231
|
progress_msg = f"Downloading {url} ... {progress:.2f} MB"
|
|
232
|
+
|
|
207
233
|
print(progress_msg, end="\r\n")
|
|
208
234
|
|
|
209
235
|
# Move the file when the file is downloaded completely. Since the
|
|
@@ -219,7 +245,12 @@ def _download_file_python(url: str, target_path: Path | str) -> Path:
|
|
|
219
245
|
return Path(target_path)
|
|
220
246
|
|
|
221
247
|
|
|
222
|
-
def _stream_url_data_to_file(
|
|
248
|
+
def _stream_url_data_to_file(
|
|
249
|
+
url: str,
|
|
250
|
+
file_path: str,
|
|
251
|
+
chunk_size_in_mb: int = 64,
|
|
252
|
+
request_headers: dict[str, str] | None = None,
|
|
253
|
+
):
|
|
223
254
|
"""Download data from a URL and stream it to a file.
|
|
224
255
|
|
|
225
256
|
Note:
|
|
@@ -233,6 +264,8 @@ def _stream_url_data_to_file(url: str, file_path: str, chunk_size_in_mb: int = 6
|
|
|
233
264
|
file_path: The path to the file where the downloaded data will be saved.
|
|
234
265
|
chunk_size_in_mb: The size of each download chunk in megabytes.
|
|
235
266
|
Defaults to 64.
|
|
267
|
+
request_headers: A dictionary containing additional headers to be included in
|
|
268
|
+
the HTTP request. Defaults to `None`.
|
|
236
269
|
|
|
237
270
|
Yields:
|
|
238
271
|
A tuple containing two elements:
|
|
@@ -244,7 +277,8 @@ def _stream_url_data_to_file(url: str, file_path: str, chunk_size_in_mb: int = 6
|
|
|
244
277
|
"""
|
|
245
278
|
ONE_MB = 1024**2
|
|
246
279
|
|
|
247
|
-
|
|
280
|
+
headers = {**TEMP_HEADERS, **(request_headers or {})}
|
|
281
|
+
request = Request(url, headers=headers)
|
|
248
282
|
|
|
249
283
|
received_size = 0
|
|
250
284
|
total_size = 0
|
|
@@ -267,7 +301,9 @@ def _stream_url_data_to_file(url: str, file_path: str, chunk_size_in_mb: int = 6
|
|
|
267
301
|
raise DownloadError("Received less data than expected from the server.")
|
|
268
302
|
|
|
269
303
|
|
|
270
|
-
def download_model_weights(
|
|
304
|
+
def download_model_weights(
|
|
305
|
+
url: str, force: bool = False, request_headers: dict[str, str] | None = None
|
|
306
|
+
) -> Path:
|
|
271
307
|
"""Downloads model weights from the specified URL and saves them to a
|
|
272
308
|
predefined directory.
|
|
273
309
|
|
|
@@ -284,6 +320,8 @@ def download_model_weights(url: str, force: bool = False):
|
|
|
284
320
|
force: If `True`, the model weights are downloaded even if they already exist
|
|
285
321
|
locally and their content length matches the expected content length from
|
|
286
322
|
the remote file. Defaults to `False`.
|
|
323
|
+
request_headers: A dictionary containing additional headers to be included in
|
|
324
|
+
the HTTP request. Defaults to `None`.
|
|
287
325
|
|
|
288
326
|
Returns:
|
|
289
327
|
A Path object representing the full path to the downloaded model weights.
|
|
@@ -303,6 +341,7 @@ def download_model_weights(url: str, force: bool = False):
|
|
|
303
341
|
url,
|
|
304
342
|
target_dir=weights_dir,
|
|
305
343
|
force=force,
|
|
344
|
+
request_headers=request_headers,
|
|
306
345
|
)
|
|
307
346
|
|
|
308
347
|
|
fal/utils.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: fal is an easy-to-use Serverless Python Framework
|
|
5
|
-
Author: Features & Labels <
|
|
5
|
+
Author: Features & Labels <support@fal.ai>
|
|
6
6
|
Requires-Python: >=3.8
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: isolate[build] <1.0,>=0.12.3
|
|
@@ -20,7 +20,7 @@ Requires-Dist: colorama <1,>=0.4.6
|
|
|
20
20
|
Requires-Dist: portalocker <3,>=2.7.0
|
|
21
21
|
Requires-Dist: rich <14,>=13.3.2
|
|
22
22
|
Requires-Dist: rich-argparse
|
|
23
|
-
Requires-Dist: packaging
|
|
23
|
+
Requires-Dist: packaging >=21.3
|
|
24
24
|
Requires-Dist: pathspec <1,>=0.11.1
|
|
25
25
|
Requires-Dist: pydantic !=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3
|
|
26
26
|
Requires-Dist: fastapi <1,>=0.99.1
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
fal/__init__.py,sha256=suif79hYcYwlZ8dAaVXCErKhuD2AYf8uU78rty8jow8,721
|
|
2
2
|
fal/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
|
|
3
|
-
fal/_fal_version.py,sha256=
|
|
4
|
-
fal/_serialization.py,sha256=
|
|
3
|
+
fal/_fal_version.py,sha256=vs6W4yrsz3i8OMGT2jlW8NQDmpo9yxVa6jtyzIhHvWw,411
|
|
4
|
+
fal/_serialization.py,sha256=7urrZXw99qmsK-RkjCurk6Va4TMEfDIMajkzKbSW4j4,7655
|
|
5
5
|
fal/_version.py,sha256=EBGqrknaf1WygENX-H4fBefLvHryvJBBGtVJetaB0NY,266
|
|
6
|
-
fal/api.py,sha256=
|
|
6
|
+
fal/api.py,sha256=PBBLmVYNfIstLXJoRRoCQSfspPxklh9PNSWZEMfi0s8,36286
|
|
7
7
|
fal/app.py,sha256=s9ba4t4D5KJrPFGKeRzL3XsdKH-W1Be6NmDQgYjPnCw,13826
|
|
8
8
|
fal/apps.py,sha256=UhR6mq8jBiTAp-QvUnvbnMNcuJ5wHIKSqdlfyx8aBQ8,6829
|
|
9
9
|
fal/flags.py,sha256=oWN_eidSUOcE9wdPK_77si3A1fpgOC0UEERPsvNLIMc,842
|
|
@@ -11,7 +11,7 @@ fal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
11
11
|
fal/rest_client.py,sha256=kGBGmuyHfX1lR910EoKCYPjsyU8MdXawT_cW2q8Sajc,568
|
|
12
12
|
fal/sdk.py,sha256=eHcg5TRouGL5d_9-p44x6lQUCYXVXdul6iEETCUov5Q,19976
|
|
13
13
|
fal/sync.py,sha256=ZuIJA2-hTPNANG9B_NNJZUsO68EIdTH0dc9MzeVE2VU,4340
|
|
14
|
-
fal/utils.py,sha256=
|
|
14
|
+
fal/utils.py,sha256=MFDs-eO3rBgc3jqIwBpfBtvvK5tbzAYWMzHV-tTVLic,1754
|
|
15
15
|
fal/workflows.py,sha256=4rjqL4xB6GHLJsqTplJmAvpd6uHZJ28sc8su33BFXEo,14682
|
|
16
16
|
fal/auth/__init__.py,sha256=r8iA2-5ih7-Fik3gEC4HEWNFbGoxpYnXpZu1icPIoS0,3561
|
|
17
17
|
fal/auth/auth0.py,sha256=rSG1mgH-QGyKfzd7XyAaj1AYsWt-ho8Y_LZ-FUVWzh4,5421
|
|
@@ -23,7 +23,7 @@ fal/cli/debug.py,sha256=1doDNwoaPDfLQginGNBxpC20dZYs5UxIojflDvV1Q04,1342
|
|
|
23
23
|
fal/cli/deploy.py,sha256=S_HIMLqDpGyzDdbiIxudRizwjGoAaHpN-sXcl2uCaQ4,4329
|
|
24
24
|
fal/cli/keys.py,sha256=-9N6ZY6rW-_IE9tpupgaBPDGjGdKB3HKqU2g9daM3Xc,3109
|
|
25
25
|
fal/cli/main.py,sha256=RSMXLUyzYmZhS0Wcq9phXJcMJM_UpQD3su7F7j8Wr3M,1933
|
|
26
|
-
fal/cli/parser.py,sha256=
|
|
26
|
+
fal/cli/parser.py,sha256=r1hd5e8Jq6yzDZw8-S0On1EjJbjRtHMuVuHC6MlvUj4,2835
|
|
27
27
|
fal/cli/run.py,sha256=NXwzkAWCKrRwgoMLsBOgW7RJPJW4IgSTrG85q2iePyk,894
|
|
28
28
|
fal/cli/secrets.py,sha256=mgHp3gBr8d2su7wBApeADKWHPkYu2ueB6gG3eNMETh8,2595
|
|
29
29
|
fal/console/__init__.py,sha256=ernZ4bzvvliQh5SmrEqQ7lA5eVcbw6Ra2jalKtA7dxg,132
|
|
@@ -49,7 +49,7 @@ fal/toolkit/file/providers/r2.py,sha256=YW5aJBOX41MQxfx1rA_f-IiJhAPMZ5md0cxBcg3y
|
|
|
49
49
|
fal/toolkit/image/__init__.py,sha256=qNLyXsBWysionUjbeWbohLqWlw3G_UpzunamkZd_JLQ,71
|
|
50
50
|
fal/toolkit/image/image.py,sha256=2q1ZCBSSdmDx9q1S4ahoCOniNaRcfSFnCS31f3b8ZZ0,4252
|
|
51
51
|
fal/toolkit/utils/__init__.py,sha256=CrmM9DyCz5-SmcTzRSm5RaLgxy3kf0ZsSEN9uhnX2Xo,97
|
|
52
|
-
fal/toolkit/utils/download_utils.py,sha256=
|
|
52
|
+
fal/toolkit/utils/download_utils.py,sha256=fDUITgdGW0wRXLE0NuQg29YJnJS3yr6l0zbRKqX6zMU,17006
|
|
53
53
|
openapi_fal_rest/__init__.py,sha256=ziculmF_i6trw63LzZGFX-6W3Lwq9mCR8_UpkpvpaHI,152
|
|
54
54
|
openapi_fal_rest/client.py,sha256=G6BpJg9j7-JsrAUGddYwkzeWRYickBjPdcVgXoPzxuE,2817
|
|
55
55
|
openapi_fal_rest/errors.py,sha256=8mXSxdfSGzxT82srdhYbR0fHfgenxJXaUtMkaGgb6iU,470
|
|
@@ -92,8 +92,8 @@ openapi_fal_rest/models/workflow_node_type.py,sha256=-FzyeY2bxcNmizKbJI8joG7byRi
|
|
|
92
92
|
openapi_fal_rest/models/workflow_schema.py,sha256=4K5gsv9u9pxx2ItkffoyHeNjBBYf6ur5bN4m_zePZNY,2019
|
|
93
93
|
openapi_fal_rest/models/workflow_schema_input.py,sha256=2OkOXWHTNsCXHWS6EGDFzcJKkW5FIap-2gfO233EvZQ,1191
|
|
94
94
|
openapi_fal_rest/models/workflow_schema_output.py,sha256=EblwSPAGfWfYVWw_WSSaBzQVju296is9o28rMBAd0mc,1196
|
|
95
|
-
fal-1.0.
|
|
96
|
-
fal-1.0.
|
|
97
|
-
fal-1.0.
|
|
98
|
-
fal-1.0.
|
|
99
|
-
fal-1.0.
|
|
95
|
+
fal-1.0.3.dist-info/METADATA,sha256=QWlHoDxrILAKMZch7EoPyYwFto1KKgg-bIBngB5bRgY,3738
|
|
96
|
+
fal-1.0.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
97
|
+
fal-1.0.3.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
|
|
98
|
+
fal-1.0.3.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
|
|
99
|
+
fal-1.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|