elapi 2.0.1__tar.gz → 2.1.0__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.
- {elapi-2.0.1 → elapi-2.1.0}/PKG-INFO +13 -5
- {elapi-2.0.1 → elapi-2.1.0}/README.md +12 -4
- {elapi-2.0.1 → elapi-2.1.0}/pyproject.toml +1 -1
- elapi-2.1.0/src/elapi/VERSION +1 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/api/api.py +129 -22
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/api/endpoint.py +5 -5
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/cli/doc.py +3 -3
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/cli/elapi.py +82 -23
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/configuration/__init__.py +3 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/configuration/_overload_history.py +37 -32
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/configuration/config.py +5 -5
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/configuration/validators.py +17 -8
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/_doc.py +2 -5
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/cli.py +29 -5
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/commons/export.py +4 -2
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/commons/get_information.py +2 -2
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/experiments/_doc.py +1 -5
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/experiments/cli.py +22 -5
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/show_config.py +26 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/styles/format.py +0 -14
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/styles/highlight.py +7 -1
- elapi-2.1.0/src/elapi/utils/__init__.py +2 -0
- elapi-2.1.0/src/elapi/utils/utils.py +58 -0
- elapi-2.0.1/src/elapi/VERSION +0 -1
- elapi-2.0.1/src/elapi/utils/__init__.py +0 -2
- elapi-2.0.1/src/elapi/utils/utils.py +0 -22
- {elapi-2.0.1 → elapi-2.1.0}/LICENSE +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/_names.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/api/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/api/validators.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/cli/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/cli/__main__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/cli/_plugin_handler.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/cli/_venv_state_manager.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/configuration/_config_history.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/configuration/log_file.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/configuration/overridable_vars.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/core_validators/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/core_validators/base.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/core_validators/path.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/loggers/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/loggers/base.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/loggers/handlers/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/loggers/handlers/file.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/loggers/handlers/stderr.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/path.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/bill_teams.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/format.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/generate_table.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/invoice.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/specification.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/bill_teams/validators.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/commons/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/commons/cli_helpers.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/commons/get_data_from_input_or_path.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/commons/get_location_from_headers.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/experiments/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/experiments/experiments.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/experiments/formats.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/plugins/version.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/styles/__init__.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/styles/_markdown_doc.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/styles/_missing.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/styles/base.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/styles/colors.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/styles/rich_utils.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/utils/messages.py +0 -0
- {elapi-2.0.1 → elapi-2.1.0}/src/elapi/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: elapi
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: elAPI is a powerful, extensible API client for eLabFTW.
|
|
5
5
|
Home-page: https://www.urz.uni-heidelberg.de/en/service-catalogue/software-and-applications/elabftw
|
|
6
6
|
License: AGPL-3.0-only
|
|
@@ -50,7 +50,7 @@ the [University Computing Centre](https://www.urz.uni-heidelberg.de/en)
|
|
|
50
50
|
[eLabFTW API documentation](https://doc.elabftw.net/api/v2/) with ease. elAPI provides a simple interface via its CLI
|
|
51
51
|
executable, and numerous advanced APIs when it is used directly as a Python package.
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
<img src="https://heibox.uni-heidelberg.de/f/7714c7eeb0f54a3ba318/?dl=1" alt="elAPI features in a nutshell" />
|
|
54
54
|
|
|
55
55
|
**Example:**
|
|
56
56
|
|
|
@@ -60,7 +60,7 @@ From [the documentation](https://doc.elabftw.net/api/v2/#/Users/read-user):
|
|
|
60
60
|
With elAPI you can do the following:
|
|
61
61
|
|
|
62
62
|
```sh
|
|
63
|
-
$ elapi get experiments
|
|
63
|
+
$ elapi get experiments --format csv --export ~/Downloads/
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
Once the command is run, in the background, elAPI will read host (eLab server) address, API key and various other
|
|
@@ -146,9 +146,10 @@ When `unsafe_api_token_warning` is `True`, elAPI will show a warning if you're s
|
|
|
146
146
|
working directory, as it typically happens that developers accidentally commit and push configuration files with
|
|
147
147
|
secrets. `enable_http2` enables HTTP/2 protocol support which by default is turned off. Be aware
|
|
148
148
|
of [known issues](https://github.com/encode/httpx/discussions/2112) with
|
|
149
|
-
HTTP/2 if you are making async requests with heavy load. `verify_ssl` can be turned off with `False` if you are
|
|
149
|
+
HTTP/2 if you are making async requests with a heavy load. `verify_ssl` can be turned off with value `False` if you are
|
|
150
|
+
trying
|
|
150
151
|
out a development server that doesn't provide a valid SSL certificate. `timeout` can be modified to your needs. E.g., a
|
|
151
|
-
poor internet connection might benefit from a higher timeout number.
|
|
152
|
+
poor internet connection might benefit from a higher timeout number. The default timeout is `90` seconds.
|
|
152
153
|
|
|
153
154
|
### `show-config`
|
|
154
155
|
|
|
@@ -215,6 +216,13 @@ You can request a list o all active experiments and export it to a `JSON` file.
|
|
|
215
216
|
$ elapi get experiments --export ~/Downoads/experiments.json
|
|
216
217
|
```
|
|
217
218
|
|
|
219
|
+
Enable built-in syntax highlighting with `--highlight` or `-H`. Here, the following command will fetch team information
|
|
220
|
+
of team with team ID 1.
|
|
221
|
+
|
|
222
|
+
```shell
|
|
223
|
+
$ elapi get -H teams --id 1
|
|
224
|
+
```
|
|
225
|
+
|
|
218
226
|
### `POST` requests
|
|
219
227
|
|
|
220
228
|
Create a new user by the name 'John Doe':
|
|
@@ -9,7 +9,7 @@ the [University Computing Centre](https://www.urz.uni-heidelberg.de/en)
|
|
|
9
9
|
[eLabFTW API documentation](https://doc.elabftw.net/api/v2/) with ease. elAPI provides a simple interface via its CLI
|
|
10
10
|
executable, and numerous advanced APIs when it is used directly as a Python package.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
<img src="https://heibox.uni-heidelberg.de/f/7714c7eeb0f54a3ba318/?dl=1" alt="elAPI features in a nutshell" />
|
|
13
13
|
|
|
14
14
|
**Example:**
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ From [the documentation](https://doc.elabftw.net/api/v2/#/Users/read-user):
|
|
|
19
19
|
With elAPI you can do the following:
|
|
20
20
|
|
|
21
21
|
```sh
|
|
22
|
-
$ elapi get experiments
|
|
22
|
+
$ elapi get experiments --format csv --export ~/Downloads/
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Once the command is run, in the background, elAPI will read host (eLab server) address, API key and various other
|
|
@@ -105,9 +105,10 @@ When `unsafe_api_token_warning` is `True`, elAPI will show a warning if you're s
|
|
|
105
105
|
working directory, as it typically happens that developers accidentally commit and push configuration files with
|
|
106
106
|
secrets. `enable_http2` enables HTTP/2 protocol support which by default is turned off. Be aware
|
|
107
107
|
of [known issues](https://github.com/encode/httpx/discussions/2112) with
|
|
108
|
-
HTTP/2 if you are making async requests with heavy load. `verify_ssl` can be turned off with `False` if you are
|
|
108
|
+
HTTP/2 if you are making async requests with a heavy load. `verify_ssl` can be turned off with value `False` if you are
|
|
109
|
+
trying
|
|
109
110
|
out a development server that doesn't provide a valid SSL certificate. `timeout` can be modified to your needs. E.g., a
|
|
110
|
-
poor internet connection might benefit from a higher timeout number.
|
|
111
|
+
poor internet connection might benefit from a higher timeout number. The default timeout is `90` seconds.
|
|
111
112
|
|
|
112
113
|
### `show-config`
|
|
113
114
|
|
|
@@ -174,6 +175,13 @@ You can request a list o all active experiments and export it to a `JSON` file.
|
|
|
174
175
|
$ elapi get experiments --export ~/Downoads/experiments.json
|
|
175
176
|
```
|
|
176
177
|
|
|
178
|
+
Enable built-in syntax highlighting with `--highlight` or `-H`. Here, the following command will fetch team information
|
|
179
|
+
of team with team ID 1.
|
|
180
|
+
|
|
181
|
+
```shell
|
|
182
|
+
$ elapi get -H teams --id 1
|
|
183
|
+
```
|
|
184
|
+
|
|
177
185
|
### `POST` requests
|
|
178
186
|
|
|
179
187
|
Create a new user by the name 'John Doe':
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.1.0
|
|
@@ -22,6 +22,14 @@ class APIRequest(ABC):
|
|
|
22
22
|
def __init__(self, keep_session_open: bool = False, **kwargs):
|
|
23
23
|
from ..utils import check_reserved_keyword
|
|
24
24
|
from .._names import CONFIG_FILE_NAME
|
|
25
|
+
from ..utils import missing_warning
|
|
26
|
+
from ..configuration import (
|
|
27
|
+
KEY_HOST,
|
|
28
|
+
KEY_API_TOKEN,
|
|
29
|
+
KEY_ENABLE_HTTP2,
|
|
30
|
+
KEY_VERIFY_SSL,
|
|
31
|
+
KEY_TIMEOUT,
|
|
32
|
+
)
|
|
25
33
|
|
|
26
34
|
self.host: str = get_active_host()
|
|
27
35
|
self.api_token: str = get_active_api_token().token
|
|
@@ -30,6 +38,15 @@ class APIRequest(ABC):
|
|
|
30
38
|
enable_http2 = get_active_enable_http2()
|
|
31
39
|
verify_ssl = get_active_verify_ssl()
|
|
32
40
|
timeout = get_active_timeout()
|
|
41
|
+
|
|
42
|
+
for field in (
|
|
43
|
+
(KEY_HOST, self.host),
|
|
44
|
+
(KEY_API_TOKEN, self.api_token),
|
|
45
|
+
(KEY_ENABLE_HTTP2, enable_http2),
|
|
46
|
+
(KEY_VERIFY_SSL, verify_ssl),
|
|
47
|
+
(KEY_TIMEOUT, timeout),
|
|
48
|
+
):
|
|
49
|
+
missing_warning(field)
|
|
33
50
|
_client = Client if not self.is_async_client else AsyncClient
|
|
34
51
|
try:
|
|
35
52
|
self._client: Union[Client, AsyncClient] = _client(
|
|
@@ -70,6 +87,11 @@ class APIRequest(ABC):
|
|
|
70
87
|
if not self.client.is_closed:
|
|
71
88
|
self.client.close()
|
|
72
89
|
|
|
90
|
+
@abstractmethod
|
|
91
|
+
async def aclose(self):
|
|
92
|
+
if not self.client.is_closed:
|
|
93
|
+
await self.client.aclose()
|
|
94
|
+
|
|
73
95
|
@abstractmethod
|
|
74
96
|
def __call__(self, *args, **kwargs):
|
|
75
97
|
response = self._make(*args, **kwargs)
|
|
@@ -77,6 +99,16 @@ class APIRequest(ABC):
|
|
|
77
99
|
self.close()
|
|
78
100
|
return response
|
|
79
101
|
|
|
102
|
+
@abstractmethod
|
|
103
|
+
async def __acall__(self, *args, **kwargs):
|
|
104
|
+
response = await self._make(
|
|
105
|
+
*args,
|
|
106
|
+
**kwargs,
|
|
107
|
+
)
|
|
108
|
+
if not self.keep_session_open:
|
|
109
|
+
await self.aclose()
|
|
110
|
+
return response
|
|
111
|
+
|
|
80
112
|
|
|
81
113
|
class ElabFTWURLError(Exception): ...
|
|
82
114
|
|
|
@@ -266,6 +298,11 @@ class GETRequest(APIRequest):
|
|
|
266
298
|
def close(self):
|
|
267
299
|
super().close()
|
|
268
300
|
|
|
301
|
+
def aclose(self):
|
|
302
|
+
raise NotImplementedError(
|
|
303
|
+
f"{GETRequest.__name__} is not async and only supports 'close' method."
|
|
304
|
+
)
|
|
305
|
+
|
|
269
306
|
def __call__(
|
|
270
307
|
self,
|
|
271
308
|
endpoint_name: str,
|
|
@@ -284,6 +321,12 @@ class GETRequest(APIRequest):
|
|
|
284
321
|
**kwargs,
|
|
285
322
|
)
|
|
286
323
|
|
|
324
|
+
def __acall__(self, *args, **kwargs):
|
|
325
|
+
raise NotImplementedError(
|
|
326
|
+
"__acall__ cannot be called directly. It's mainly an async"
|
|
327
|
+
" placeholder for __call__. Please use __call__ instead."
|
|
328
|
+
)
|
|
329
|
+
|
|
287
330
|
|
|
288
331
|
class POSTRequest(APIRequest):
|
|
289
332
|
__slots__ = ()
|
|
@@ -320,6 +363,11 @@ class POSTRequest(APIRequest):
|
|
|
320
363
|
def close(self):
|
|
321
364
|
super().close()
|
|
322
365
|
|
|
366
|
+
def aclose(self):
|
|
367
|
+
raise NotImplementedError(
|
|
368
|
+
f"{POSTRequest.__name__} is not async and only supports 'close' method."
|
|
369
|
+
)
|
|
370
|
+
|
|
323
371
|
def __call__(
|
|
324
372
|
self,
|
|
325
373
|
endpoint_name: str,
|
|
@@ -338,6 +386,12 @@ class POSTRequest(APIRequest):
|
|
|
338
386
|
**kwargs,
|
|
339
387
|
)
|
|
340
388
|
|
|
389
|
+
def __acall__(self, *args, **kwargs):
|
|
390
|
+
raise NotImplementedError(
|
|
391
|
+
"__acall__ cannot be called directly. It's mainly an async"
|
|
392
|
+
" placeholder for __call__. Please use __call__ instead."
|
|
393
|
+
)
|
|
394
|
+
|
|
341
395
|
|
|
342
396
|
class AsyncPOSTRequest(APIRequest, is_async_client=True):
|
|
343
397
|
__slots__ = ()
|
|
@@ -372,9 +426,13 @@ class AsyncPOSTRequest(APIRequest, is_async_client=True):
|
|
|
372
426
|
**kwargs,
|
|
373
427
|
)
|
|
374
428
|
|
|
375
|
-
async def
|
|
376
|
-
|
|
377
|
-
|
|
429
|
+
async def aclose(self):
|
|
430
|
+
await super().aclose()
|
|
431
|
+
|
|
432
|
+
def close(self):
|
|
433
|
+
raise NotImplementedError(
|
|
434
|
+
f"{AsyncPOSTRequest.__name__} is async and only supports 'aclose' method."
|
|
435
|
+
)
|
|
378
436
|
|
|
379
437
|
async def __call__(
|
|
380
438
|
self,
|
|
@@ -385,7 +443,7 @@ class AsyncPOSTRequest(APIRequest, is_async_client=True):
|
|
|
385
443
|
query: Optional[dict] = None,
|
|
386
444
|
**kwargs,
|
|
387
445
|
) -> Response:
|
|
388
|
-
|
|
446
|
+
return await super().__acall__(
|
|
389
447
|
endpoint_name,
|
|
390
448
|
endpoint_id,
|
|
391
449
|
sub_endpoint_name,
|
|
@@ -393,9 +451,12 @@ class AsyncPOSTRequest(APIRequest, is_async_client=True):
|
|
|
393
451
|
query,
|
|
394
452
|
**kwargs,
|
|
395
453
|
)
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
454
|
+
|
|
455
|
+
def __acall__(self, *args, **kwargs):
|
|
456
|
+
raise NotImplementedError(
|
|
457
|
+
"__acall__ cannot be called directly. It's mainly an async"
|
|
458
|
+
" placeholder for __call__. Please use __call__ instead."
|
|
459
|
+
)
|
|
399
460
|
|
|
400
461
|
|
|
401
462
|
class AsyncGETRequest(APIRequest, is_async_client=True):
|
|
@@ -422,10 +483,15 @@ class AsyncGETRequest(APIRequest, is_async_client=True):
|
|
|
422
483
|
url.get(), headers=headers or {"Accept": "application/json"}, **kwargs
|
|
423
484
|
)
|
|
424
485
|
|
|
425
|
-
async def
|
|
486
|
+
async def aclose(self):
|
|
426
487
|
if not self.client.is_closed:
|
|
427
488
|
await self.client.aclose()
|
|
428
489
|
|
|
490
|
+
def close(self):
|
|
491
|
+
raise NotImplementedError(
|
|
492
|
+
f"{AsyncGETRequest.__name__} is async and only supports 'aclose' method."
|
|
493
|
+
)
|
|
494
|
+
|
|
429
495
|
async def __call__(
|
|
430
496
|
self,
|
|
431
497
|
endpoint_name: str,
|
|
@@ -435,7 +501,7 @@ class AsyncGETRequest(APIRequest, is_async_client=True):
|
|
|
435
501
|
query: Optional[dict] = None,
|
|
436
502
|
**kwargs,
|
|
437
503
|
) -> Response:
|
|
438
|
-
|
|
504
|
+
return await super().__acall__(
|
|
439
505
|
endpoint_name,
|
|
440
506
|
endpoint_id,
|
|
441
507
|
sub_endpoint_name,
|
|
@@ -443,9 +509,12 @@ class AsyncGETRequest(APIRequest, is_async_client=True):
|
|
|
443
509
|
query,
|
|
444
510
|
**kwargs,
|
|
445
511
|
)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
512
|
+
|
|
513
|
+
def __acall__(self, *args, **kwargs):
|
|
514
|
+
raise NotImplementedError(
|
|
515
|
+
"__acall__ cannot be called directly. It's mainly an async"
|
|
516
|
+
" placeholder for __call__. Please use __call__ instead."
|
|
517
|
+
)
|
|
449
518
|
|
|
450
519
|
|
|
451
520
|
class PATCHRequest(APIRequest):
|
|
@@ -476,6 +545,11 @@ class PATCHRequest(APIRequest):
|
|
|
476
545
|
def close(self):
|
|
477
546
|
super().close()
|
|
478
547
|
|
|
548
|
+
def aclose(self):
|
|
549
|
+
raise NotImplementedError(
|
|
550
|
+
f"{PATCHRequest.__name__} is not async and only supports 'close' method."
|
|
551
|
+
)
|
|
552
|
+
|
|
479
553
|
def __call__(
|
|
480
554
|
self,
|
|
481
555
|
endpoint_name: str,
|
|
@@ -494,6 +568,12 @@ class PATCHRequest(APIRequest):
|
|
|
494
568
|
**kwargs,
|
|
495
569
|
)
|
|
496
570
|
|
|
571
|
+
def __acall__(self, *args, **kwargs):
|
|
572
|
+
raise NotImplementedError(
|
|
573
|
+
"__acall__ cannot be called directly. It's mainly an async"
|
|
574
|
+
" placeholder for __call__. Please use __call__ instead."
|
|
575
|
+
)
|
|
576
|
+
|
|
497
577
|
|
|
498
578
|
class AsyncPATCHRequest(APIRequest, is_async_client=True):
|
|
499
579
|
__slots__ = ()
|
|
@@ -526,10 +606,15 @@ class AsyncPATCHRequest(APIRequest, is_async_client=True):
|
|
|
526
606
|
**kwargs,
|
|
527
607
|
)
|
|
528
608
|
|
|
529
|
-
async def
|
|
609
|
+
async def aclose(self):
|
|
530
610
|
if not self.client.is_closed:
|
|
531
611
|
await self.client.aclose()
|
|
532
612
|
|
|
613
|
+
def close(self):
|
|
614
|
+
raise NotImplementedError(
|
|
615
|
+
f"{AsyncPATCHRequest.__name__} is async and only supports 'aclose' method."
|
|
616
|
+
)
|
|
617
|
+
|
|
533
618
|
async def __call__(
|
|
534
619
|
self,
|
|
535
620
|
endpoint_name: str,
|
|
@@ -539,7 +624,7 @@ class AsyncPATCHRequest(APIRequest, is_async_client=True):
|
|
|
539
624
|
query: Optional[dict] = None,
|
|
540
625
|
**kwargs,
|
|
541
626
|
) -> Response:
|
|
542
|
-
|
|
627
|
+
return await super().__acall__(
|
|
543
628
|
endpoint_name,
|
|
544
629
|
endpoint_id,
|
|
545
630
|
sub_endpoint_name,
|
|
@@ -547,9 +632,12 @@ class AsyncPATCHRequest(APIRequest, is_async_client=True):
|
|
|
547
632
|
query,
|
|
548
633
|
**kwargs,
|
|
549
634
|
)
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
635
|
+
|
|
636
|
+
def __acall__(self, *args, **kwargs):
|
|
637
|
+
raise NotImplementedError(
|
|
638
|
+
"__acall__ cannot be called directly. It's mainly an async"
|
|
639
|
+
" placeholder for __call__. Please use __call__ instead."
|
|
640
|
+
)
|
|
553
641
|
|
|
554
642
|
|
|
555
643
|
class DELETERequest(APIRequest):
|
|
@@ -572,6 +660,11 @@ class DELETERequest(APIRequest):
|
|
|
572
660
|
def close(self):
|
|
573
661
|
super().close()
|
|
574
662
|
|
|
663
|
+
def aclose(self):
|
|
664
|
+
raise NotImplementedError(
|
|
665
|
+
f"{DELETERequest.__name__} is not async and only supports 'close' method."
|
|
666
|
+
)
|
|
667
|
+
|
|
575
668
|
def __call__(
|
|
576
669
|
self,
|
|
577
670
|
endpoint_name: str,
|
|
@@ -590,6 +683,12 @@ class DELETERequest(APIRequest):
|
|
|
590
683
|
**kwargs,
|
|
591
684
|
)
|
|
592
685
|
|
|
686
|
+
def __acall__(self, *args, **kwargs):
|
|
687
|
+
raise NotImplementedError(
|
|
688
|
+
"__acall__ cannot be called directly. It's mainly an async"
|
|
689
|
+
" placeholder for __call__. Please use __call__ instead."
|
|
690
|
+
)
|
|
691
|
+
|
|
593
692
|
|
|
594
693
|
class AsyncDELETERequest(APIRequest, is_async_client=True):
|
|
595
694
|
__slots__ = ()
|
|
@@ -617,10 +716,15 @@ class AsyncDELETERequest(APIRequest, is_async_client=True):
|
|
|
617
716
|
**kwargs,
|
|
618
717
|
)
|
|
619
718
|
|
|
620
|
-
async def
|
|
719
|
+
async def aclose(self):
|
|
621
720
|
if not self.client.is_closed:
|
|
622
721
|
await self.client.aclose()
|
|
623
722
|
|
|
723
|
+
def close(self):
|
|
724
|
+
raise NotImplementedError(
|
|
725
|
+
f"{AsyncDELETERequest.__name__} is async and only supports 'aclose' method."
|
|
726
|
+
)
|
|
727
|
+
|
|
624
728
|
async def __call__(
|
|
625
729
|
self,
|
|
626
730
|
endpoint_name: str,
|
|
@@ -630,7 +734,7 @@ class AsyncDELETERequest(APIRequest, is_async_client=True):
|
|
|
630
734
|
query: Optional[dict] = None,
|
|
631
735
|
**kwargs,
|
|
632
736
|
) -> Response:
|
|
633
|
-
|
|
737
|
+
return await super().__acall__(
|
|
634
738
|
endpoint_name,
|
|
635
739
|
endpoint_id,
|
|
636
740
|
sub_endpoint_name,
|
|
@@ -638,6 +742,9 @@ class AsyncDELETERequest(APIRequest, is_async_client=True):
|
|
|
638
742
|
query,
|
|
639
743
|
**kwargs,
|
|
640
744
|
)
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
745
|
+
|
|
746
|
+
def __acall__(self, *args, **kwargs):
|
|
747
|
+
raise NotImplementedError(
|
|
748
|
+
"__acall__ cannot be called directly. It's mainly an async"
|
|
749
|
+
" placeholder for __call__. Please use __call__ instead."
|
|
750
|
+
)
|
|
@@ -82,11 +82,11 @@ class FixedAsyncEndpoint:
|
|
|
82
82
|
query,
|
|
83
83
|
)
|
|
84
84
|
|
|
85
|
-
async def
|
|
86
|
-
await self._get_session.
|
|
87
|
-
await self._post_session.
|
|
88
|
-
await self._patch_session.
|
|
89
|
-
await self._delete_session.
|
|
85
|
+
async def aclose(self):
|
|
86
|
+
await self._get_session.aclose()
|
|
87
|
+
await self._post_session.aclose()
|
|
88
|
+
await self._patch_session.aclose()
|
|
89
|
+
await self._delete_session.aclose()
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
class FixedEndpoint:
|
|
@@ -82,11 +82,11 @@ __PARAMETERS__doc__ = {
|
|
|
82
82
|
f"an existing file when exporting.",
|
|
83
83
|
"data_format": f"Format style for the output. Supported values are: {supported_highlighting_formats}. "
|
|
84
84
|
f"The values are case insensitive. The default format is `{DEFAULT_EXPORT_DATA_FORMAT.upper()}`. "
|
|
85
|
-
"When 'txt' is used, the response will be sent in *original*, un-formatted (almost), "
|
|
86
|
-
"without syntax highlighting. This can be utilized if one wishes to pipe the output "
|
|
87
|
-
" to some external formatting program like `less`. "
|
|
88
85
|
"If an unsupported format value is provided then the output format "
|
|
89
86
|
f"falls back to `{DEFAULT_EXPORT_DATA_FORMAT.upper()}`.",
|
|
87
|
+
"highlight_syntax": "Enable syntax highlighting for shown output if possible. Default is **False**. "
|
|
88
|
+
"Tools like `jq` or `less` might behave erratically when syntax highlighted output "
|
|
89
|
+
"is piped to them.",
|
|
90
90
|
"verify": "SSL certificates are verified for HTTPS requests by default. Passing _'--verify False'_ will "
|
|
91
91
|
"disable the verification. This can be useful during local development. You can also pass a "
|
|
92
92
|
"path to SSL certificate (a.k.a CA bundle) file to --verify.",
|
|
@@ -12,6 +12,7 @@ documented in https://doc.elabftw.net/api/v2/ with ease.
|
|
|
12
12
|
$ elapi get users --id <id>
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
import sys
|
|
15
16
|
from functools import partial
|
|
16
17
|
from typing import Optional
|
|
17
18
|
|
|
@@ -84,7 +85,6 @@ def cli_startup(
|
|
|
84
85
|
minimal_active_configuration,
|
|
85
86
|
)
|
|
86
87
|
from ..configuration.config import APIToken
|
|
87
|
-
from ..configuration.validators import MainConfigurationValidator
|
|
88
88
|
from ..core_validators import Exit
|
|
89
89
|
from ..configuration import reinitiate_config
|
|
90
90
|
from ..plugins.commons.get_data_from_input_or_path import get_structured_data
|
|
@@ -121,8 +121,7 @@ def cli_startup(
|
|
|
121
121
|
and ctx.command.name != calling_sub_command_name
|
|
122
122
|
):
|
|
123
123
|
if argv[-1] != (ARG_TO_SKIP := "--help") or ARG_TO_SKIP not in argv:
|
|
124
|
-
|
|
125
|
-
reinitiate_config()
|
|
124
|
+
reinitiate_config()
|
|
126
125
|
else:
|
|
127
126
|
if calling_sub_command_name in SENSITIVE_PLUGIN_NAMES:
|
|
128
127
|
if override_config:
|
|
@@ -431,6 +430,15 @@ def get(
|
|
|
431
430
|
Optional[str],
|
|
432
431
|
typer.Option("--format", "-F", help=docs["data_format"], show_default=False),
|
|
433
432
|
] = None,
|
|
433
|
+
highlight_syntax: Annotated[
|
|
434
|
+
Optional[bool],
|
|
435
|
+
typer.Option(
|
|
436
|
+
"--highlight",
|
|
437
|
+
"-H",
|
|
438
|
+
help=docs["highlight_syntax"],
|
|
439
|
+
show_default=True,
|
|
440
|
+
),
|
|
441
|
+
] = False,
|
|
434
442
|
export: Annotated[
|
|
435
443
|
Optional[bool],
|
|
436
444
|
typer.Option(
|
|
@@ -581,11 +589,17 @@ def get(
|
|
|
581
589
|
f"{export.destination} in {export.format_name} format."
|
|
582
590
|
)
|
|
583
591
|
else:
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
592
|
+
if highlight_syntax is True:
|
|
593
|
+
highlight = Highlight(format.name)
|
|
594
|
+
if not raw_response.is_success:
|
|
595
|
+
stderr_console.print(highlight(formatted_data))
|
|
596
|
+
raise Exit(1)
|
|
597
|
+
stdin_console.print(highlight(formatted_data))
|
|
598
|
+
else:
|
|
599
|
+
if not raw_response.is_success:
|
|
600
|
+
typer.echo(formatted_data, file=sys.stderr)
|
|
601
|
+
raise Exit(1)
|
|
602
|
+
typer.echo(formatted_data)
|
|
589
603
|
return response_data
|
|
590
604
|
|
|
591
605
|
|
|
@@ -631,6 +645,15 @@ def post(
|
|
|
631
645
|
str,
|
|
632
646
|
typer.Option("--format", "-F", help=docs["data_format"], show_default=False),
|
|
633
647
|
] = "json",
|
|
648
|
+
highlight_syntax: Annotated[
|
|
649
|
+
Optional[bool],
|
|
650
|
+
typer.Option(
|
|
651
|
+
"--highlight",
|
|
652
|
+
"-H",
|
|
653
|
+
help=docs["highlight_syntax"],
|
|
654
|
+
show_default=True,
|
|
655
|
+
),
|
|
656
|
+
] = False,
|
|
634
657
|
headers: Annotated[
|
|
635
658
|
Optional[str],
|
|
636
659
|
typer.Option("--headers", help=docs["headers"], show_default=False),
|
|
@@ -791,11 +814,17 @@ def post(
|
|
|
791
814
|
)
|
|
792
815
|
raise typer.Exit(1)
|
|
793
816
|
else:
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
817
|
+
if highlight_syntax is True:
|
|
818
|
+
highlight = Highlight(format.name)
|
|
819
|
+
if not raw_response.is_success:
|
|
820
|
+
stderr_console.print(highlight(formatted_data))
|
|
821
|
+
raise Exit(1)
|
|
822
|
+
stdin_console.print(highlight(formatted_data))
|
|
823
|
+
else:
|
|
824
|
+
if not raw_response.is_success:
|
|
825
|
+
typer.echo(formatted_data, file=sys.stderr)
|
|
826
|
+
raise Exit(1)
|
|
827
|
+
typer.echo(formatted_data)
|
|
799
828
|
return formatted_data
|
|
800
829
|
|
|
801
830
|
|
|
@@ -833,6 +862,15 @@ def patch(
|
|
|
833
862
|
str,
|
|
834
863
|
typer.Option("--format", "-F", help=docs["data_format"], show_default=False),
|
|
835
864
|
] = "json",
|
|
865
|
+
highlight_syntax: Annotated[
|
|
866
|
+
Optional[bool],
|
|
867
|
+
typer.Option(
|
|
868
|
+
"--highlight",
|
|
869
|
+
"-H",
|
|
870
|
+
help=docs["highlight_syntax"],
|
|
871
|
+
show_default=True,
|
|
872
|
+
),
|
|
873
|
+
] = False,
|
|
836
874
|
headers: Annotated[
|
|
837
875
|
Optional[str],
|
|
838
876
|
typer.Option("--headers", help=docs["headers"], show_default=False),
|
|
@@ -936,11 +974,17 @@ def patch(
|
|
|
936
974
|
)
|
|
937
975
|
raise typer.Exit(1)
|
|
938
976
|
else:
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
977
|
+
if highlight_syntax is True:
|
|
978
|
+
highlight = Highlight(format.name)
|
|
979
|
+
if not raw_response.is_success:
|
|
980
|
+
stderr_console.print(highlight(formatted_data))
|
|
981
|
+
raise Exit(1)
|
|
982
|
+
stdin_console.print(highlight(formatted_data))
|
|
983
|
+
else:
|
|
984
|
+
if not raw_response.is_success:
|
|
985
|
+
typer.echo(formatted_data, file=sys.stderr)
|
|
986
|
+
raise Exit(1)
|
|
987
|
+
typer.echo(formatted_data)
|
|
944
988
|
return formatted_data
|
|
945
989
|
|
|
946
990
|
|
|
@@ -974,6 +1018,15 @@ def delete(
|
|
|
974
1018
|
str,
|
|
975
1019
|
typer.Option("--format", "-F", help=docs["data_format"], show_default=False),
|
|
976
1020
|
] = "json",
|
|
1021
|
+
highlight_syntax: Annotated[
|
|
1022
|
+
Optional[bool],
|
|
1023
|
+
typer.Option(
|
|
1024
|
+
"--highlight",
|
|
1025
|
+
"-H",
|
|
1026
|
+
help=docs["highlight_syntax"],
|
|
1027
|
+
show_default=True,
|
|
1028
|
+
),
|
|
1029
|
+
] = False,
|
|
977
1030
|
headers: Annotated[
|
|
978
1031
|
Optional[str],
|
|
979
1032
|
typer.Option("--headers", help=docs["headers"], show_default=False),
|
|
@@ -1076,11 +1129,17 @@ def delete(
|
|
|
1076
1129
|
)
|
|
1077
1130
|
raise typer.Exit(1)
|
|
1078
1131
|
else:
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1132
|
+
if highlight_syntax is True:
|
|
1133
|
+
highlight = Highlight(format.name)
|
|
1134
|
+
if not raw_response.is_success:
|
|
1135
|
+
stderr_console.print(highlight(formatted_data))
|
|
1136
|
+
raise Exit(1)
|
|
1137
|
+
stdin_console.print(highlight(formatted_data))
|
|
1138
|
+
else:
|
|
1139
|
+
if not raw_response.is_success:
|
|
1140
|
+
typer.echo(formatted_data, file=sys.stderr)
|
|
1141
|
+
raise Exit(1)
|
|
1142
|
+
typer.echo(formatted_data)
|
|
1084
1143
|
return formatted_data
|
|
1085
1144
|
|
|
1086
1145
|
|