nc-py-api 0.21.0__py3-none-any.whl → 0.22.0__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.
- nc_py_api/__init__.py +1 -0
- nc_py_api/_exceptions.py +4 -0
- nc_py_api/_version.py +1 -1
- nc_py_api/ex_app/integration_fastapi.py +116 -58
- nc_py_api/options.py +2 -0
- {nc_py_api-0.21.0.dist-info → nc_py_api-0.22.0.dist-info}/METADATA +1 -1
- {nc_py_api-0.21.0.dist-info → nc_py_api-0.22.0.dist-info}/RECORD +10 -10
- {nc_py_api-0.21.0.dist-info → nc_py_api-0.22.0.dist-info}/WHEEL +0 -0
- {nc_py_api-0.21.0.dist-info → nc_py_api-0.22.0.dist-info}/licenses/AUTHORS +0 -0
- {nc_py_api-0.21.0.dist-info → nc_py_api-0.22.0.dist-info}/licenses/LICENSE.txt +0 -0
nc_py_api/__init__.py
CHANGED
nc_py_api/_exceptions.py
CHANGED
|
@@ -65,3 +65,7 @@ def check_error(response: Response, info: str = ""):
|
|
|
65
65
|
response.raise_for_status()
|
|
66
66
|
except HTTPError as e:
|
|
67
67
|
raise NextcloudException(status_code, reason=response.reason, info=info) from e
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ModelFetchError(Exception):
|
|
71
|
+
"""Exception raised when model fetching fails."""
|
nc_py_api/_version.py
CHANGED
|
@@ -7,6 +7,7 @@ import hashlib
|
|
|
7
7
|
import json
|
|
8
8
|
import os
|
|
9
9
|
import typing
|
|
10
|
+
from traceback import format_exc
|
|
10
11
|
from urllib.parse import urlparse
|
|
11
12
|
|
|
12
13
|
import niquests
|
|
@@ -22,10 +23,10 @@ from fastapi.responses import JSONResponse, PlainTextResponse
|
|
|
22
23
|
from starlette.requests import HTTPConnection, Request
|
|
23
24
|
from starlette.types import ASGIApp, Receive, Scope, Send
|
|
24
25
|
|
|
26
|
+
from .._exceptions import ModelFetchError
|
|
25
27
|
from .._misc import get_username_secret_from_headers
|
|
26
28
|
from ..nextcloud import AsyncNextcloudApp, NextcloudApp
|
|
27
29
|
from ..talk_bot import TalkBotMessage
|
|
28
|
-
from .defs import LogLvl
|
|
29
30
|
from .misc import persistent_storage
|
|
30
31
|
|
|
31
32
|
|
|
@@ -60,6 +61,7 @@ def set_handlers(
|
|
|
60
61
|
default_init: bool = True,
|
|
61
62
|
models_to_fetch: dict[str, dict] | None = None,
|
|
62
63
|
map_app_static: bool = True,
|
|
64
|
+
trigger_handler: typing.Callable[[str], typing.Awaitable[None] | None] | None = None,
|
|
63
65
|
):
|
|
64
66
|
"""Defines handlers for the application.
|
|
65
67
|
|
|
@@ -70,13 +72,30 @@ def set_handlers(
|
|
|
70
72
|
|
|
71
73
|
.. note:: When this parameter is ``False``, the provision of ``models_to_fetch`` is not allowed.
|
|
72
74
|
|
|
73
|
-
:param models_to_fetch: Dictionary describing which models should be downloaded during `init
|
|
75
|
+
:param models_to_fetch: Dictionary describing which models should be downloaded during `init` of the form:
|
|
76
|
+
.. code-block:: python
|
|
77
|
+
{
|
|
78
|
+
"model_url_1": {
|
|
79
|
+
"save_path": "path_or_filename_to_save_the_model_to",
|
|
80
|
+
},
|
|
81
|
+
"huggingface_model_name_1": {
|
|
82
|
+
"max_workers": 4,
|
|
83
|
+
"cache_dir": "path_to_cache_dir",
|
|
84
|
+
"revision": "revision_to_fetch",
|
|
85
|
+
...
|
|
86
|
+
},
|
|
87
|
+
...
|
|
88
|
+
}
|
|
89
|
+
|
|
74
90
|
|
|
75
91
|
.. note:: ``huggingface_hub`` package should be present for automatic models fetching.
|
|
92
|
+
All model options are optional and can be left empty.
|
|
76
93
|
|
|
77
94
|
:param map_app_static: Should be folders ``js``, ``css``, ``l10n``, ``img`` automatically mounted in FastAPI or not.
|
|
78
95
|
|
|
79
96
|
.. note:: First, presence of these directories in the current working dir is checked, then one directory higher.
|
|
97
|
+
|
|
98
|
+
:param trigger_handler: callback that is called for task processing `trigger` events with the id of the provider.
|
|
80
99
|
"""
|
|
81
100
|
if models_to_fetch is not None and default_init is False:
|
|
82
101
|
raise ValueError("`models_to_fetch` can be defined only with `default_init`=True.")
|
|
@@ -109,6 +128,21 @@ def set_handlers(
|
|
|
109
128
|
if map_app_static:
|
|
110
129
|
__map_app_static_folders(fast_api_app)
|
|
111
130
|
|
|
131
|
+
if trigger_handler:
|
|
132
|
+
if asyncio.iscoroutinefunction(trigger_handler):
|
|
133
|
+
|
|
134
|
+
@fast_api_app.post("/trigger")
|
|
135
|
+
async def trigger_callback(providerId: str): # pylint: disable=invalid-name
|
|
136
|
+
await trigger_handler(providerId)
|
|
137
|
+
return JSONResponse(content={})
|
|
138
|
+
|
|
139
|
+
else:
|
|
140
|
+
|
|
141
|
+
@fast_api_app.post("/trigger")
|
|
142
|
+
def trigger_callback(providerId: str): # pylint: disable=invalid-name
|
|
143
|
+
trigger_handler(providerId)
|
|
144
|
+
return JSONResponse(content={})
|
|
145
|
+
|
|
112
146
|
|
|
113
147
|
def __map_app_static_folders(fast_api_app: FastAPI):
|
|
114
148
|
"""Function to mount all necessary static folders to FastAPI."""
|
|
@@ -121,74 +155,98 @@ def __map_app_static_folders(fast_api_app: FastAPI):
|
|
|
121
155
|
|
|
122
156
|
|
|
123
157
|
def fetch_models_task(nc: NextcloudApp, models: dict[str, dict], progress_init_start_value: int) -> None:
|
|
124
|
-
"""Use for cases when you want to define custom `/init` but still need to easy download models.
|
|
158
|
+
"""Use for cases when you want to define custom `/init` but still need to easy download models.
|
|
159
|
+
|
|
160
|
+
:param nc: NextcloudApp instance.
|
|
161
|
+
:param models_to_fetch: Dictionary describing which models should be downloaded of the form:
|
|
162
|
+
.. code-block:: python
|
|
163
|
+
{
|
|
164
|
+
"model_url_1": {
|
|
165
|
+
"save_path": "path_or_filename_to_save_the_model_to",
|
|
166
|
+
},
|
|
167
|
+
"huggingface_model_name_1": {
|
|
168
|
+
"max_workers": 4,
|
|
169
|
+
"cache_dir": "path_to_cache_dir",
|
|
170
|
+
"revision": "revision_to_fetch",
|
|
171
|
+
...
|
|
172
|
+
},
|
|
173
|
+
...
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.. note:: ``huggingface_hub`` package should be present for automatic models fetching.
|
|
177
|
+
All model options are optional and can be left empty.
|
|
178
|
+
|
|
179
|
+
:param progress_init_start_value: Integer value defining from which percent the progress should start.
|
|
180
|
+
|
|
181
|
+
:raises ModelFetchError: in case of a model download error.
|
|
182
|
+
:raises NextcloudException: in case of a network error reaching the Nextcloud server.
|
|
183
|
+
"""
|
|
125
184
|
if models:
|
|
126
185
|
current_progress = progress_init_start_value
|
|
127
186
|
percent_for_each = min(int((100 - progress_init_start_value) / len(models)), 99)
|
|
128
187
|
for model in models:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
188
|
+
try:
|
|
189
|
+
if model.startswith(("http://", "https://")):
|
|
190
|
+
models[model]["path"] = __fetch_model_as_file(
|
|
191
|
+
current_progress, percent_for_each, nc, model, models[model]
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
models[model]["path"] = __fetch_model_as_snapshot(
|
|
195
|
+
current_progress, percent_for_each, nc, model, models[model]
|
|
196
|
+
)
|
|
197
|
+
current_progress += percent_for_each
|
|
198
|
+
except BaseException as e: # noqa pylint: disable=broad-exception-caught
|
|
199
|
+
nc.set_init_status(current_progress, f"Downloading of '{model}' failed: {e}: {format_exc()}")
|
|
200
|
+
raise ModelFetchError(f"Downloading of '{model}' failed.") from e
|
|
138
201
|
nc.set_init_status(100)
|
|
139
202
|
|
|
140
203
|
|
|
141
204
|
def __fetch_model_as_file(
|
|
142
205
|
current_progress: int, progress_for_task: int, nc: NextcloudApp, model_path: str, download_options: dict
|
|
143
|
-
) -> str
|
|
206
|
+
) -> str:
|
|
144
207
|
result_path = download_options.pop("save_path", urlparse(model_path).path.split("/")[-1])
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return result_path
|
|
184
|
-
except Exception as e: # noqa pylint: disable=broad-exception-caught
|
|
185
|
-
nc.log(LogLvl.ERROR, f"Downloading of '{model_path}' raised an exception: {e}")
|
|
186
|
-
|
|
187
|
-
return None
|
|
208
|
+
with niquests.get(model_path, stream=True) as response:
|
|
209
|
+
if not response.ok:
|
|
210
|
+
raise ModelFetchError(
|
|
211
|
+
f"Downloading of '{model_path}' failed, returned ({response.status_code}) {response.text}"
|
|
212
|
+
)
|
|
213
|
+
downloaded_size = 0
|
|
214
|
+
linked_etag = ""
|
|
215
|
+
for each_history in response.history:
|
|
216
|
+
linked_etag = each_history.headers.get("X-Linked-ETag", "")
|
|
217
|
+
if linked_etag:
|
|
218
|
+
break
|
|
219
|
+
if not linked_etag:
|
|
220
|
+
linked_etag = response.headers.get("X-Linked-ETag", response.headers.get("ETag", ""))
|
|
221
|
+
total_size = int(response.headers.get("Content-Length"))
|
|
222
|
+
try:
|
|
223
|
+
existing_size = os.path.getsize(result_path)
|
|
224
|
+
except OSError:
|
|
225
|
+
existing_size = 0
|
|
226
|
+
if linked_etag and total_size == existing_size:
|
|
227
|
+
with builtins.open(result_path, "rb") as file:
|
|
228
|
+
sha256_hash = hashlib.sha256()
|
|
229
|
+
for byte_block in iter(lambda: file.read(4096), b""):
|
|
230
|
+
sha256_hash.update(byte_block)
|
|
231
|
+
if f'"{sha256_hash.hexdigest()}"' == linked_etag:
|
|
232
|
+
nc.set_init_status(min(current_progress + progress_for_task, 99))
|
|
233
|
+
return result_path
|
|
234
|
+
|
|
235
|
+
with builtins.open(result_path, "wb") as file:
|
|
236
|
+
last_progress = current_progress
|
|
237
|
+
for chunk in response.iter_raw(-1):
|
|
238
|
+
downloaded_size += file.write(chunk)
|
|
239
|
+
if total_size:
|
|
240
|
+
new_progress = min(current_progress + int(progress_for_task * downloaded_size / total_size), 99)
|
|
241
|
+
if new_progress != last_progress:
|
|
242
|
+
nc.set_init_status(new_progress)
|
|
243
|
+
last_progress = new_progress
|
|
244
|
+
|
|
245
|
+
return result_path
|
|
188
246
|
|
|
189
247
|
|
|
190
248
|
def __fetch_model_as_snapshot(
|
|
191
|
-
current_progress: int, progress_for_task, nc: NextcloudApp,
|
|
249
|
+
current_progress: int, progress_for_task, nc: NextcloudApp, model_name: str, download_options: dict
|
|
192
250
|
) -> str:
|
|
193
251
|
from huggingface_hub import snapshot_download # noqa isort:skip pylint: disable=C0415 disable=E0401
|
|
194
252
|
from tqdm import tqdm # noqa isort:skip pylint: disable=C0415 disable=E0401
|
|
@@ -201,7 +259,7 @@ def __fetch_model_as_snapshot(
|
|
|
201
259
|
workers = download_options.pop("max_workers", 2)
|
|
202
260
|
cache = download_options.pop("cache_dir", persistent_storage())
|
|
203
261
|
return snapshot_download(
|
|
204
|
-
|
|
262
|
+
model_name, tqdm_class=TqdmProgress, **download_options, max_workers=workers, cache_dir=cache
|
|
205
263
|
)
|
|
206
264
|
|
|
207
265
|
|
nc_py_api/options.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nc-py-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.22.0
|
|
4
4
|
Summary: Nextcloud Python Framework
|
|
5
5
|
Project-URL: Changelog, https://github.com/cloud-py-api/nc_py_api/blob/main/CHANGELOG.md
|
|
6
6
|
Project-URL: Documentation, https://cloud-py-api.github.io/nc_py_api/
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
nc_py_api/__init__.py,sha256=
|
|
1
|
+
nc_py_api/__init__.py,sha256=S5IytGqp8PkWnmcZXRFPSVEwfeb4iZ8xPeCmSf4sTFQ,460
|
|
2
2
|
nc_py_api/_deffered_error.py,sha256=BpEe_tBqflwfj2Zolb67nhW-K16XX-WbcY2IH_6u8fo,319
|
|
3
|
-
nc_py_api/_exceptions.py,sha256=
|
|
3
|
+
nc_py_api/_exceptions.py,sha256=_4k-uzpKdW5_WjqVE_saoGFJ1YqWx7W6kr-wgpBume0,2414
|
|
4
4
|
nc_py_api/_misc.py,sha256=dUzCP9VmyhtICTsn1aexlFAYUioBm40k6Zh-YE5WwCY,3333
|
|
5
5
|
nc_py_api/_preferences.py,sha256=OtovFZuGHnHYKjdDjSnUappO795tW8Oxj7qVaejHWpQ,2479
|
|
6
6
|
nc_py_api/_preferences_ex.py,sha256=Y6sDBrFJc7lk8BoDUfjC_iwOfjSbPPNPpcSxsL1fyIM,6391
|
|
7
7
|
nc_py_api/_session.py,sha256=u45U4USdgbegBj3oAnm-OErFng2fAyleH1dWgshJSso,22018
|
|
8
8
|
nc_py_api/_talk_api.py,sha256=0Uo7OduYniuuX3UQPb468RyGJJ-PWBCgJ5HoPuz5Qa0,51068
|
|
9
9
|
nc_py_api/_theming.py,sha256=hTr3nuOemSuRFZaPy9iXNmBM7rDgQHECH43tHMWGqEY,1870
|
|
10
|
-
nc_py_api/_version.py,sha256=
|
|
10
|
+
nc_py_api/_version.py,sha256=hUeDIevdzurgTsk1Uo0m8u0An4IWucrh8zxG1wW377o,52
|
|
11
11
|
nc_py_api/activity.py,sha256=t9VDSnnaXRNOvALqOSGCeXSQZ-426pCOMSfQ96JHys4,9574
|
|
12
12
|
nc_py_api/apps.py,sha256=Us2y2lszdxXlD8t6kxwd5_Nrrmazc0EvZXIH9O-ol80,9315
|
|
13
13
|
nc_py_api/calendar_api.py,sha256=a2Q5EGf5_swWPYkUbHnoEg6h1S9KTEUQD7f7DljGHYY,1442
|
|
@@ -15,7 +15,7 @@ nc_py_api/loginflow_v2.py,sha256=QgR99Q59Q1My5U_PeLFkIAvEKhX_H7bIRrBZdddvmo4,576
|
|
|
15
15
|
nc_py_api/nextcloud.py,sha256=cKdsw0n_yFNdI-N8IIVIQKkSqmEzJnQtLBVqo24EVdM,22849
|
|
16
16
|
nc_py_api/notes.py,sha256=aM0SLVGKv8nBv_qI3z8sN08Z2721wLJUmEdwlo2EK1g,15112
|
|
17
17
|
nc_py_api/notifications.py,sha256=WgzV21TuLOhLk-UEjhBSbMsIi2isa5MmAx6cbe0pc2Y,9187
|
|
18
|
-
nc_py_api/options.py,sha256=
|
|
18
|
+
nc_py_api/options.py,sha256=fuAiua3HiFUZYLB7X3-6L7rp3oxYOJCLWCTyeA0nUIA,1751
|
|
19
19
|
nc_py_api/talk.py,sha256=OZFemYkDOaM6o4xAK3EvQbjMFiK75E5qnsCDyihIElg,29368
|
|
20
20
|
nc_py_api/talk_bot.py,sha256=QXaKKwIRRcOAnqoJgfzK3ub0aDPTUmTkagls-a0XZW0,16840
|
|
21
21
|
nc_py_api/user_status.py,sha256=I101nwYS8X1WvC8AnLa2f3qJUCPDPHrbq-ke0h1VT4E,13282
|
|
@@ -25,7 +25,7 @@ nc_py_api/weather_status.py,sha256=wAkjuJPjxc0Rxe4za0BzfwB0XeUmkCXoisJtTH3-qdQ,7
|
|
|
25
25
|
nc_py_api/webhooks.py,sha256=BGHRtankgbUkcqBRJTFShjRLpaVoFNcjLsrVitoNziM,8083
|
|
26
26
|
nc_py_api/ex_app/__init__.py,sha256=6Lwid4bBXOSrZf_ocf5m8qkkO1OgYxG0GTs4q6Nw72o,691
|
|
27
27
|
nc_py_api/ex_app/defs.py,sha256=FaQInH3jLugKxDUqpwrXdkMT-lBxmoqWmXJXc11fa6A,727
|
|
28
|
-
nc_py_api/ex_app/integration_fastapi.py,sha256=
|
|
28
|
+
nc_py_api/ex_app/integration_fastapi.py,sha256=BxE7oMZ5q2D5mRjTsd7k9mZEU7XH2yYp8fQvBHHUWgc,13392
|
|
29
29
|
nc_py_api/ex_app/logger.py,sha256=nAHLObuPvl3UBLrlqZulgoxxVaAJ661iP4F6bTW-V-Y,1475
|
|
30
30
|
nc_py_api/ex_app/misc.py,sha256=c7B0uE8isaIi4SQbxURGUuWjZaaXiLg3Ov6cqvRYplE,2298
|
|
31
31
|
nc_py_api/ex_app/occ_commands.py,sha256=hb2BJuvFKIigvLycSCyAe9v6hedq4Gfu2junQZTaK_M,5219
|
|
@@ -45,8 +45,8 @@ nc_py_api/files/_files.py,sha256=t7KBZE3fHmfVk0WZuQF9usCMY_b4rzX1ct1aIZx3RYw,143
|
|
|
45
45
|
nc_py_api/files/files.py,sha256=A05iT_s5cYMK1MtarqdjTZP9QA1l_SiINq805VLrzuY,24999
|
|
46
46
|
nc_py_api/files/files_async.py,sha256=GoTPswMgozcx3Vkbj4YSitNeP7vpPJ4lIbrCnj77rP4,25858
|
|
47
47
|
nc_py_api/files/sharing.py,sha256=VRZCl-TYK6dbu9rUHPs3_jcVozu1EO8bLGZwoRpiLsU,14439
|
|
48
|
-
nc_py_api-0.
|
|
49
|
-
nc_py_api-0.
|
|
50
|
-
nc_py_api-0.
|
|
51
|
-
nc_py_api-0.
|
|
52
|
-
nc_py_api-0.
|
|
48
|
+
nc_py_api-0.22.0.dist-info/METADATA,sha256=6CGpk5oqgcosb-sjxhP4RVmyHd92ratTVSZjbPaZL1E,8024
|
|
49
|
+
nc_py_api-0.22.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
50
|
+
nc_py_api-0.22.0.dist-info/licenses/AUTHORS,sha256=B2Q9q9XH3PAxJp0V3GiKQc1l0z7vtGDpDHqda-ISWKM,616
|
|
51
|
+
nc_py_api-0.22.0.dist-info/licenses/LICENSE.txt,sha256=OLEMh401fAumGHfRSna365MLIfnjdTcdOHZ6QOzMjkg,1551
|
|
52
|
+
nc_py_api-0.22.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|