elapi 2.0.2__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.
Files changed (71) hide show
  1. {elapi-2.0.2 → elapi-2.1.0}/PKG-INFO +12 -4
  2. {elapi-2.0.2 → elapi-2.1.0}/README.md +11 -3
  3. {elapi-2.0.2 → elapi-2.1.0}/pyproject.toml +1 -1
  4. elapi-2.1.0/src/elapi/VERSION +1 -0
  5. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/api/api.py +129 -22
  6. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/api/endpoint.py +5 -5
  7. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/cli/doc.py +3 -3
  8. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/cli/elapi.py +82 -23
  9. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/configuration/__init__.py +3 -0
  10. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/configuration/_overload_history.py +37 -32
  11. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/configuration/config.py +5 -5
  12. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/configuration/validators.py +17 -8
  13. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/_doc.py +2 -5
  14. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/cli.py +29 -5
  15. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/commons/export.py +4 -2
  16. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/commons/get_information.py +2 -2
  17. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/experiments/_doc.py +1 -5
  18. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/experiments/cli.py +22 -5
  19. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/show_config.py +26 -0
  20. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/styles/format.py +0 -14
  21. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/styles/highlight.py +7 -1
  22. elapi-2.1.0/src/elapi/utils/__init__.py +2 -0
  23. elapi-2.1.0/src/elapi/utils/utils.py +58 -0
  24. elapi-2.0.2/src/elapi/VERSION +0 -1
  25. elapi-2.0.2/src/elapi/utils/__init__.py +0 -2
  26. elapi-2.0.2/src/elapi/utils/utils.py +0 -22
  27. {elapi-2.0.2 → elapi-2.1.0}/LICENSE +0 -0
  28. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/__init__.py +0 -0
  29. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/_names.py +0 -0
  30. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/api/__init__.py +0 -0
  31. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/api/validators.py +0 -0
  32. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/cli/__init__.py +0 -0
  33. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/cli/__main__.py +0 -0
  34. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/cli/_plugin_handler.py +0 -0
  35. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/cli/_venv_state_manager.py +0 -0
  36. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/configuration/_config_history.py +0 -0
  37. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/configuration/log_file.py +0 -0
  38. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/configuration/overridable_vars.py +0 -0
  39. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/core_validators/__init__.py +0 -0
  40. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/core_validators/base.py +0 -0
  41. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/core_validators/path.py +0 -0
  42. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/loggers/__init__.py +0 -0
  43. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/loggers/base.py +0 -0
  44. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/loggers/handlers/__init__.py +0 -0
  45. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/loggers/handlers/file.py +0 -0
  46. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/loggers/handlers/stderr.py +0 -0
  47. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/path.py +0 -0
  48. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/__init__.py +0 -0
  49. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/__init__.py +0 -0
  50. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/bill_teams.py +0 -0
  51. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/format.py +0 -0
  52. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/generate_table.py +0 -0
  53. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/invoice.py +0 -0
  54. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/specification.py +0 -0
  55. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/bill_teams/validators.py +0 -0
  56. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/commons/__init__.py +0 -0
  57. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/commons/cli_helpers.py +0 -0
  58. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/commons/get_data_from_input_or_path.py +0 -0
  59. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/commons/get_location_from_headers.py +0 -0
  60. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/experiments/__init__.py +0 -0
  61. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/experiments/experiments.py +0 -0
  62. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/experiments/formats.py +0 -0
  63. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/plugins/version.py +0 -0
  64. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/styles/__init__.py +0 -0
  65. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/styles/_markdown_doc.py +0 -0
  66. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/styles/_missing.py +0 -0
  67. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/styles/base.py +0 -0
  68. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/styles/colors.py +0 -0
  69. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/styles/rich_utils.py +0 -0
  70. {elapi-2.0.2 → elapi-2.1.0}/src/elapi/utils/messages.py +0 -0
  71. {elapi-2.0.2 → 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.2
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
- <img src="./.assets/elapi_features.svg" alt="elAPI features in a nutshell" />
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
 
@@ -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 trying
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. THe default timeout is `90` seconds.
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
- <img src="./.assets/elapi_features.svg" alt="elAPI features in a nutshell" />
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
 
@@ -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 trying
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. THe default timeout is `90` seconds.
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':
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "elapi"
3
- version = "2.0.2"
3
+ version = "2.1.0"
4
4
  description = "elAPI is a powerful, extensible API client for eLabFTW."
5
5
  authors = [
6
6
  "Alexander Haller, Mahadi Xion <elabftw@uni-heidelberg.de>",
@@ -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 close(self):
376
- if not self.client.is_closed:
377
- await self.client.aclose()
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
- response = await self._make(
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
- if not self.keep_session_open:
397
- await self.close()
398
- return response
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 close(self):
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
- response = await self._make(
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
- if not self.keep_session_open:
447
- await self.close()
448
- return response
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 close(self):
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
- response = await self._make(
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
- if not self.keep_session_open:
551
- await self.close()
552
- return response
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 close(self):
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
- response = await self._make(
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
- if not self.keep_session_open:
642
- await self.close()
643
- return response
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 close(self):
86
- await self._get_session.close()
87
- await self._post_session.close()
88
- await self._patch_session.close()
89
- await self._delete_session.close()
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
- if override_config or not MainConfigurationValidator.ALREADY_VALIDATED:
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
- highlight = Highlight(format.name)
585
- if not raw_response.is_success:
586
- stderr_console.print(highlight(formatted_data))
587
- raise Exit(1)
588
- stdin_console.print(highlight(formatted_data))
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
- highlight = Highlight(format.name)
795
- if not raw_response.is_success:
796
- stderr_console.print(highlight(formatted_data))
797
- raise typer.Exit(1)
798
- stdin_console.print(highlight(formatted_data))
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
- highlight = Highlight(format.name)
940
- if not raw_response.is_success:
941
- stderr_console.print(highlight(formatted_data))
942
- raise typer.Exit(1)
943
- stdin_console.print(highlight(formatted_data))
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
- highlight = Highlight(format.name)
1080
- if not raw_response.is_success:
1081
- stderr_console.print(highlight(formatted_data))
1082
- raise typer.Exit(1)
1083
- stdin_console.print(highlight(formatted_data))
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
 
@@ -59,3 +59,6 @@ from .overridable_vars import (
59
59
  get_active_timeout,
60
60
  get_development_mode,
61
61
  )
62
+
63
+ if get_development_mode() is False:
64
+ reinitiate_config(ignore_essential_validation=True)