cmsdials 1.7.2__tar.gz → 1.8.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.
- cmsdials-1.8.0/.coveragerc +14 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/.gitignore +3 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/.gitlab-ci.yml +3 -1
- {cmsdials-1.7.2 → cmsdials-1.8.0}/.pre-commit-config.yaml +9 -1
- {cmsdials-1.7.2 → cmsdials-1.8.0}/PKG-INFO +40 -27
- {cmsdials-1.7.2 → cmsdials-1.8.0}/README.md +39 -26
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/__init__.py +1 -1
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/_version.py +2 -2
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/auth/_base.py +2 -3
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/auth/bearer.py +4 -5
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/auth/client.py +10 -8
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/auth/secret_key.py +4 -4
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/dataset_index/client.py +9 -3
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/dataset_index/models.py +5 -9
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/file_index/client.py +9 -3
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/file_index/models.py +5 -9
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/h1d/client.py +9 -3
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/h1d/models.py +5 -9
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/h2d/client.py +9 -3
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/h2d/models.py +5 -9
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/lumisection/client.py +9 -3
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/lumisection/models.py +5 -9
- cmsdials-1.8.0/cmsdials/clients/mes/client.py +36 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/mes/models.py +2 -2
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/ml_bad_lumisection/client.py +11 -5
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/ml_bad_lumisection/models.py +5 -9
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/ml_models_index/client.py +9 -3
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/ml_models_index/models.py +5 -9
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/oms_proxy/client.py +4 -4
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/run/client.py +9 -3
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/run/models.py +5 -9
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/utils/api_client.py +73 -45
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/utils/base_model.py +26 -10
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials.egg-info/PKG-INFO +40 -27
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials.egg-info/SOURCES.txt +3 -0
- cmsdials-1.8.0/mypy.ini +5 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/pyproject.toml +4 -0
- cmsdials-1.8.0/pytest.ini +2 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/ruff.toml +3 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_mes_client.py +5 -2
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tox.ini +1 -1
- cmsdials-1.7.2/cmsdials/clients/mes/client.py +0 -12
- {cmsdials-1.7.2 → cmsdials-1.8.0}/.editorconfig +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/.mailmap +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/.tool-versions +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/LICENSE +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/auth/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/auth/exceptions.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/auth/models.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/dataset_index/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/file_index/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/h1d/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/h2d/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/lumisection/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/mes/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/ml_bad_lumisection/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/ml_models_index/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/oms_proxy/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/oms_proxy/models.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/clients/run/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/cmsdials.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/filters.py +2 -2
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/utils/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/utils/_json.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials/utils/logger.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials.egg-info/dependency_links.txt +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials.egg-info/requires.txt +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/cmsdials.egg-info/top_level.txt +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/setup.cfg +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_auth_client.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_file_index_client.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_h1d_client.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_h2d_client.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_lumisection_client.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_oms_proxy_client.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_resume.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/test_run_client.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/integration/utils.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/unit/__init__.py +0 -0
- {cmsdials-1.7.2 → cmsdials-1.8.0}/tests/unit/test_base_api_client.py +0 -0
|
@@ -15,7 +15,9 @@ test_matrix:
|
|
|
15
15
|
- pip install uv
|
|
16
16
|
- uv sync --all-groups --extra pandas --extra tqdm
|
|
17
17
|
script:
|
|
18
|
-
- uv run
|
|
18
|
+
- uv run ruff check cmsdials tests
|
|
19
|
+
- uv run mypy cmsdials tests
|
|
20
|
+
- uv run pytest tests
|
|
19
21
|
coverage: "/TOTAL.+ ([0-9]{1,3}%)/"
|
|
20
22
|
artifacts:
|
|
21
23
|
reports:
|
|
@@ -18,8 +18,16 @@ repos:
|
|
|
18
18
|
- id: trailing-whitespace
|
|
19
19
|
- id: end-of-file-fixer
|
|
20
20
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
21
|
-
rev: v0.
|
|
21
|
+
rev: v0.11.4
|
|
22
22
|
hooks:
|
|
23
23
|
- id: ruff
|
|
24
24
|
args: [--fix]
|
|
25
25
|
- id: ruff-format
|
|
26
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
27
|
+
rev: v1.15.0
|
|
28
|
+
hooks:
|
|
29
|
+
- id: mypy
|
|
30
|
+
additional_dependencies:
|
|
31
|
+
- pandas-stubs>=2.2.2.240807
|
|
32
|
+
- types-requests>=2.31.0.6
|
|
33
|
+
- types-tqdm>=4.67.0.20250516
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cmsdials
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: The Python api client interface to DIALS service
|
|
5
5
|
Author-email: Gabriel Moreira <gabriel.moreira@cern.ch>
|
|
6
6
|
Project-URL: Repository, https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py
|
|
@@ -21,8 +21,9 @@ Provides-Extra: tqdm
|
|
|
21
21
|
Requires-Dist: tqdm>=4.66.1; extra == "tqdm"
|
|
22
22
|
Dynamic: license-file
|
|
23
23
|
|
|
24
|
-

|
|
25
|
+

|
|
26
|
+

|
|
26
27
|
[](https://badge.fury.io/py/cmsdials)
|
|
27
28
|
|
|
28
29
|
# dials-py
|
|
@@ -136,7 +137,8 @@ dials.ml_bad_lumisection.list()
|
|
|
136
137
|
|
|
137
138
|
### Retrieving all available pages of a list of objects using `list_all`
|
|
138
139
|
|
|
139
|
-
|
|
140
|
+
> [!NOTE]
|
|
141
|
+
> Keep in mind that running `list_all` without any filter can take too much time, since you will be retrieving all rows in the database.
|
|
140
142
|
|
|
141
143
|
```python
|
|
142
144
|
dials.dataset_index.list_all()
|
|
@@ -316,12 +318,22 @@ dials = Dials(creds, base_url=DEV_URL)
|
|
|
316
318
|
dials.h2d.list_all(LumisectionHistogram2DFilters(me__regex="EEOT digi occupancy EE +", entries__gte=100, run_number__gte=360392, run_number__lte=365000), max_pages=5)
|
|
317
319
|
```
|
|
318
320
|
|
|
319
|
-
##
|
|
321
|
+
## Development
|
|
322
|
+
|
|
323
|
+
Install the dependencies and the package using `uv`:
|
|
324
|
+
|
|
325
|
+
```shell
|
|
326
|
+
uv sync --all-groups --extra pandas --extra tqdm
|
|
327
|
+
uv run pre-commit install
|
|
328
|
+
uv pip install -e .
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Running tests
|
|
320
332
|
|
|
321
333
|
The repository has some tests written to make sure DIALS responses are compatible with pydantic metaclasses, you can use pytest to run all tests but you need to specify a secret key to authenticate non-interactively against DIALS api:
|
|
322
334
|
|
|
323
335
|
```bash
|
|
324
|
-
SECRET_KEY=... pytest tests
|
|
336
|
+
SECRET_KEY=... uv run pytest tests
|
|
325
337
|
```
|
|
326
338
|
|
|
327
339
|
The secret key is an api client enabled secret key and can be obtained from the applications portal, any api client secret key whitelisted in DIALS can be used. The interactive authentication flow should be tested manually, an example for this can be found in [this line](/tests/integration/test_auth_client.py#L33).
|
|
@@ -329,15 +341,15 @@ The secret key is an api client enabled secret key and can be obtained from the
|
|
|
329
341
|
If testing against a local version of DIALS you need to specify the BASE_URL:
|
|
330
342
|
|
|
331
343
|
```bash
|
|
332
|
-
SECRET_KEY=... BASE_URL=http://localhost:8000 pytest tests
|
|
344
|
+
SECRET_KEY=... BASE_URL=http://localhost:8000 uv run pytest tests
|
|
333
345
|
```
|
|
334
346
|
|
|
335
|
-
|
|
347
|
+
#### Tox
|
|
336
348
|
|
|
337
|
-
Tox is pre-configured in `
|
|
349
|
+
Tox is pre-configured in `tox.ini`, so you can run the following to test against multiple python versions locally:
|
|
338
350
|
|
|
339
351
|
```bash
|
|
340
|
-
SECRET_KEY=...
|
|
352
|
+
SECRET_KEY=... uv run tox
|
|
341
353
|
```
|
|
342
354
|
|
|
343
355
|
**[asdf](https://asdf-vm.com/) users**
|
|
@@ -350,34 +362,35 @@ asdf set python 3.12.9 3.11.10 3.10.13 3.9.19
|
|
|
350
362
|
|
|
351
363
|
This will use `3.12.9` by default (if you just run `python`), but it will also put `python3.11`, `python3.10` and `python3.9` symlinks in your path so you can run those too (which is exactly what tox is looking for).
|
|
352
364
|
|
|
353
|
-
|
|
365
|
+
### Releasing the package on PyPI
|
|
354
366
|
|
|
355
|
-
|
|
367
|
+
The package is available in PyPI at [cmsdials](https://pypi.org/project/cmsdials/), under the [cmsdqm](https://pypi.org/org/cms-dqm/) organization. You'll need at leat Mantainer rights to be able to push new versions.
|
|
356
368
|
|
|
357
|
-
|
|
369
|
+
#### CI
|
|
358
370
|
|
|
359
|
-
|
|
360
|
-
poetry build
|
|
361
|
-
```
|
|
371
|
+
Do not worry. The GitLab CI is configured to automatically publish the package on PyPI and the release notes in GitLab whever a tag is pushed to the repo.
|
|
362
372
|
|
|
363
|
-
|
|
373
|
+
> [!NOTE]
|
|
374
|
+
> For this to work the CI/CD variables named `UV_PUBLISH_TOKEN`, `GITLAB_TOKEN` should be registered in gitlab. The `UV_PUBLISH_TOKEN` is a api token access of CMSDQM organization and the `GITLAB_TOKEN` is a Project Access Token with api read/write rights, which is needed to read merge requests using the `glab-cli`.
|
|
364
375
|
|
|
365
|
-
|
|
366
|
-
poetry self add "poetry-dynamic-versioning[plugin]"
|
|
367
|
-
```
|
|
376
|
+
#### Manual
|
|
368
377
|
|
|
369
|
-
|
|
378
|
+
If you want to follow the manual approach, you need to first build and then publish.
|
|
370
379
|
|
|
371
|
-
|
|
380
|
+
##### Build
|
|
372
381
|
|
|
373
|
-
|
|
382
|
+
You can use uv to build the package using:
|
|
374
383
|
|
|
384
|
+
```bash
|
|
385
|
+
uv build
|
|
375
386
|
```
|
|
376
|
-
poetry config pypi-token.pypi <your-api-token>
|
|
377
|
-
```
|
|
378
387
|
|
|
379
|
-
|
|
388
|
+
The build system will automatically update the package version based on the git tag of the current commit.
|
|
389
|
+
|
|
390
|
+
##### Publish
|
|
391
|
+
|
|
392
|
+
Provided you have already generate a PyPI api token in your account or in CMDQM org, you can publish using:
|
|
380
393
|
|
|
381
394
|
```bash
|
|
382
|
-
|
|
395
|
+
UV_PUBLISH_TOKEN=... uv publish
|
|
383
396
|
```
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-

|
|
2
|
+

|
|
3
|
+

|
|
3
4
|
[](https://badge.fury.io/py/cmsdials)
|
|
4
5
|
|
|
5
6
|
# dials-py
|
|
@@ -113,7 +114,8 @@ dials.ml_bad_lumisection.list()
|
|
|
113
114
|
|
|
114
115
|
### Retrieving all available pages of a list of objects using `list_all`
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
> [!NOTE]
|
|
118
|
+
> Keep in mind that running `list_all` without any filter can take too much time, since you will be retrieving all rows in the database.
|
|
117
119
|
|
|
118
120
|
```python
|
|
119
121
|
dials.dataset_index.list_all()
|
|
@@ -293,12 +295,22 @@ dials = Dials(creds, base_url=DEV_URL)
|
|
|
293
295
|
dials.h2d.list_all(LumisectionHistogram2DFilters(me__regex="EEOT digi occupancy EE +", entries__gte=100, run_number__gte=360392, run_number__lte=365000), max_pages=5)
|
|
294
296
|
```
|
|
295
297
|
|
|
296
|
-
##
|
|
298
|
+
## Development
|
|
299
|
+
|
|
300
|
+
Install the dependencies and the package using `uv`:
|
|
301
|
+
|
|
302
|
+
```shell
|
|
303
|
+
uv sync --all-groups --extra pandas --extra tqdm
|
|
304
|
+
uv run pre-commit install
|
|
305
|
+
uv pip install -e .
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Running tests
|
|
297
309
|
|
|
298
310
|
The repository has some tests written to make sure DIALS responses are compatible with pydantic metaclasses, you can use pytest to run all tests but you need to specify a secret key to authenticate non-interactively against DIALS api:
|
|
299
311
|
|
|
300
312
|
```bash
|
|
301
|
-
SECRET_KEY=... pytest tests
|
|
313
|
+
SECRET_KEY=... uv run pytest tests
|
|
302
314
|
```
|
|
303
315
|
|
|
304
316
|
The secret key is an api client enabled secret key and can be obtained from the applications portal, any api client secret key whitelisted in DIALS can be used. The interactive authentication flow should be tested manually, an example for this can be found in [this line](/tests/integration/test_auth_client.py#L33).
|
|
@@ -306,15 +318,15 @@ The secret key is an api client enabled secret key and can be obtained from the
|
|
|
306
318
|
If testing against a local version of DIALS you need to specify the BASE_URL:
|
|
307
319
|
|
|
308
320
|
```bash
|
|
309
|
-
SECRET_KEY=... BASE_URL=http://localhost:8000 pytest tests
|
|
321
|
+
SECRET_KEY=... BASE_URL=http://localhost:8000 uv run pytest tests
|
|
310
322
|
```
|
|
311
323
|
|
|
312
|
-
|
|
324
|
+
#### Tox
|
|
313
325
|
|
|
314
|
-
Tox is pre-configured in `
|
|
326
|
+
Tox is pre-configured in `tox.ini`, so you can run the following to test against multiple python versions locally:
|
|
315
327
|
|
|
316
328
|
```bash
|
|
317
|
-
SECRET_KEY=...
|
|
329
|
+
SECRET_KEY=... uv run tox
|
|
318
330
|
```
|
|
319
331
|
|
|
320
332
|
**[asdf](https://asdf-vm.com/) users**
|
|
@@ -327,34 +339,35 @@ asdf set python 3.12.9 3.11.10 3.10.13 3.9.19
|
|
|
327
339
|
|
|
328
340
|
This will use `3.12.9` by default (if you just run `python`), but it will also put `python3.11`, `python3.10` and `python3.9` symlinks in your path so you can run those too (which is exactly what tox is looking for).
|
|
329
341
|
|
|
330
|
-
|
|
342
|
+
### Releasing the package on PyPI
|
|
331
343
|
|
|
332
|
-
|
|
344
|
+
The package is available in PyPI at [cmsdials](https://pypi.org/project/cmsdials/), under the [cmsdqm](https://pypi.org/org/cms-dqm/) organization. You'll need at leat Mantainer rights to be able to push new versions.
|
|
333
345
|
|
|
334
|
-
|
|
346
|
+
#### CI
|
|
335
347
|
|
|
336
|
-
|
|
337
|
-
poetry build
|
|
338
|
-
```
|
|
348
|
+
Do not worry. The GitLab CI is configured to automatically publish the package on PyPI and the release notes in GitLab whever a tag is pushed to the repo.
|
|
339
349
|
|
|
340
|
-
|
|
350
|
+
> [!NOTE]
|
|
351
|
+
> For this to work the CI/CD variables named `UV_PUBLISH_TOKEN`, `GITLAB_TOKEN` should be registered in gitlab. The `UV_PUBLISH_TOKEN` is a api token access of CMSDQM organization and the `GITLAB_TOKEN` is a Project Access Token with api read/write rights, which is needed to read merge requests using the `glab-cli`.
|
|
341
352
|
|
|
342
|
-
|
|
343
|
-
poetry self add "poetry-dynamic-versioning[plugin]"
|
|
344
|
-
```
|
|
353
|
+
#### Manual
|
|
345
354
|
|
|
346
|
-
|
|
355
|
+
If you want to follow the manual approach, you need to first build and then publish.
|
|
347
356
|
|
|
348
|
-
|
|
357
|
+
##### Build
|
|
349
358
|
|
|
350
|
-
|
|
359
|
+
You can use uv to build the package using:
|
|
351
360
|
|
|
361
|
+
```bash
|
|
362
|
+
uv build
|
|
352
363
|
```
|
|
353
|
-
poetry config pypi-token.pypi <your-api-token>
|
|
354
|
-
```
|
|
355
364
|
|
|
356
|
-
|
|
365
|
+
The build system will automatically update the package version based on the git tag of the current commit.
|
|
366
|
+
|
|
367
|
+
##### Publish
|
|
368
|
+
|
|
369
|
+
Provided you have already generate a PyPI api token in your account or in CMDQM org, you can publish using:
|
|
357
370
|
|
|
358
371
|
```bash
|
|
359
|
-
|
|
372
|
+
UV_PUBLISH_TOKEN=... uv publish
|
|
360
373
|
```
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import threading
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
|
-
from collections.abc import Mapping
|
|
4
3
|
from datetime import datetime, timedelta
|
|
5
4
|
from enum import Enum
|
|
6
5
|
|
|
@@ -87,7 +86,7 @@ class BaseCredentials(ABC):
|
|
|
87
86
|
return TokenState.FRESH
|
|
88
87
|
|
|
89
88
|
@abstractmethod
|
|
90
|
-
def apply(self, headers:
|
|
89
|
+
def apply(self, headers: dict[str, str]) -> None:
|
|
91
90
|
"""
|
|
92
91
|
Apply the token to the authentication header.
|
|
93
92
|
"""
|
|
@@ -109,7 +108,7 @@ class BaseCredentials(ABC):
|
|
|
109
108
|
self.refresh()
|
|
110
109
|
self._stale_lock.release()
|
|
111
110
|
|
|
112
|
-
def before_request(self, headers:
|
|
111
|
+
def before_request(self, headers: dict[str, str]):
|
|
113
112
|
"""
|
|
114
113
|
Performs credential-specific before request logic.
|
|
115
114
|
"""
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
import os.path
|
|
4
|
-
from collections.abc import Mapping
|
|
5
4
|
from datetime import datetime
|
|
6
5
|
from typing import Optional
|
|
7
6
|
|
|
@@ -55,8 +54,8 @@ class Credentials(BaseCredentials):
|
|
|
55
54
|
# If loaded token is invalid, trigger authentication
|
|
56
55
|
if self.token_state == TokenState.INVALID:
|
|
57
56
|
logger.info("Access token and refresh token are expired, triggering device authentication flow...")
|
|
58
|
-
|
|
59
|
-
self._set_token(
|
|
57
|
+
_token = self.client.device_auth_flow()
|
|
58
|
+
self._set_token(_token)
|
|
60
59
|
|
|
61
60
|
# Only cache if token is fresh
|
|
62
61
|
if self.token_state == TokenState.FRESH:
|
|
@@ -112,7 +111,7 @@ class Credentials(BaseCredentials):
|
|
|
112
111
|
token = Token(**fcontents)
|
|
113
112
|
except FileNotFoundError:
|
|
114
113
|
logger.info("Credentials file not found, triggering device authentication flow...")
|
|
115
|
-
token
|
|
114
|
+
token = client.device_auth_flow()
|
|
116
115
|
|
|
117
116
|
return Credentials.from_authclient_token(token, cache_dir, client)
|
|
118
117
|
|
|
@@ -135,7 +134,7 @@ class Credentials(BaseCredentials):
|
|
|
135
134
|
self._set_token(token)
|
|
136
135
|
self.cache_credentials()
|
|
137
136
|
|
|
138
|
-
def apply(self, headers:
|
|
137
|
+
def apply(self, headers: dict[str, str]) -> None:
|
|
139
138
|
"""
|
|
140
139
|
Apply the Client secret key token to the X-CLIENT-SECRET header.
|
|
141
140
|
"""
|
|
@@ -27,8 +27,8 @@ class AuthClient(BaseAPIClient):
|
|
|
27
27
|
timeout=self.default_timeout,
|
|
28
28
|
)
|
|
29
29
|
response.raise_for_status()
|
|
30
|
-
|
|
31
|
-
return Device(**
|
|
30
|
+
data = response.json()
|
|
31
|
+
return Device(**data)
|
|
32
32
|
|
|
33
33
|
def device_token(self, device_code: str) -> Token:
|
|
34
34
|
endpoint_url = self.api_url + "auth/device-token/"
|
|
@@ -47,11 +47,12 @@ class AuthClient(BaseAPIClient):
|
|
|
47
47
|
raise PendingAuthorizationError
|
|
48
48
|
|
|
49
49
|
response.raise_for_status()
|
|
50
|
-
|
|
50
|
+
data = response.json()
|
|
51
51
|
curr_datetime = datetime.now()
|
|
52
|
-
token = DeviceToken(**
|
|
52
|
+
token = DeviceToken(**data)
|
|
53
|
+
token_dict = token.model_dump()
|
|
53
54
|
return Token(
|
|
54
|
-
**
|
|
55
|
+
**token_dict,
|
|
55
56
|
expires_at=curr_datetime + timedelta(seconds=token.expires_in),
|
|
56
57
|
refresh_expires_at=curr_datetime + timedelta(seconds=token.refresh_expires_in),
|
|
57
58
|
)
|
|
@@ -70,11 +71,12 @@ class AuthClient(BaseAPIClient):
|
|
|
70
71
|
timeout=self.default_timeout,
|
|
71
72
|
)
|
|
72
73
|
response.raise_for_status()
|
|
73
|
-
|
|
74
|
+
data = response.json()
|
|
74
75
|
curr_datetime = datetime.now()
|
|
75
|
-
token = RefreshToken(**
|
|
76
|
+
token = RefreshToken(**data)
|
|
77
|
+
token_dict = token.model_dump()
|
|
76
78
|
return Token(
|
|
77
|
-
**
|
|
79
|
+
**token_dict,
|
|
78
80
|
expires_at=curr_datetime + timedelta(seconds=token.expires_in),
|
|
79
81
|
refresh_expires_at=curr_datetime + timedelta(seconds=token.refresh_expires_in),
|
|
80
82
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Optional
|
|
2
2
|
|
|
3
3
|
from ._base import BaseCredentials
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ class Credentials(BaseCredentials):
|
|
|
9
9
|
These credential use Client secret key to provide authorization to applications.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
def __init__(self, token: str) -> None:
|
|
12
|
+
def __init__(self, token: Optional[str]) -> None:
|
|
13
13
|
if not token:
|
|
14
14
|
raise ValueError("token must be a non-empty Client secret key string")
|
|
15
15
|
self.token = token
|
|
@@ -29,13 +29,13 @@ class Credentials(BaseCredentials):
|
|
|
29
29
|
def refresh(self) -> None:
|
|
30
30
|
return
|
|
31
31
|
|
|
32
|
-
def apply(self, headers:
|
|
32
|
+
def apply(self, headers: dict[str, str]) -> None:
|
|
33
33
|
"""
|
|
34
34
|
Apply the Client secret key token to the X-CLIENT-SECRET header.
|
|
35
35
|
"""
|
|
36
36
|
headers["X-CLIENT-SECRET"] = self.token
|
|
37
37
|
|
|
38
|
-
def before_request(self, headers:
|
|
38
|
+
def before_request(self, headers: dict[str, str]):
|
|
39
39
|
"""
|
|
40
40
|
Performs credential-specific before request logic.
|
|
41
41
|
"""
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
from ...utils.api_client import
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedPaginatedAPIClient
|
|
2
2
|
from .models import DatasetIndex, DatasetIndexFilters, PaginatedDatasetIndexList
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class DatasetIndexClient(
|
|
5
|
+
class DatasetIndexClient(
|
|
6
|
+
BaseAuthorizedPaginatedAPIClient[
|
|
7
|
+
DatasetIndex,
|
|
8
|
+
PaginatedDatasetIndexList,
|
|
9
|
+
DatasetIndexFilters,
|
|
10
|
+
]
|
|
11
|
+
):
|
|
6
12
|
data_model = DatasetIndex
|
|
7
13
|
pagination_model = PaginatedDatasetIndexList
|
|
8
14
|
filter_class = DatasetIndexFilters
|
|
@@ -10,4 +16,4 @@ class DatasetIndexClient(BaseAuthorizedAPIClient):
|
|
|
10
16
|
|
|
11
17
|
def get(self, dataset_id: int, file_id: int, **kwargs):
|
|
12
18
|
edp = f"{dataset_id}/{file_id}/"
|
|
13
|
-
return super().
|
|
19
|
+
return super()._get(edp, **kwargs)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from pydantic import
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
5
|
|
|
6
|
-
from ...utils.base_model import
|
|
6
|
+
from ...utils.base_model import PaginatedBaseFilters, PaginatedBaseModel
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class DatasetIndex(BaseModel):
|
|
@@ -17,14 +17,10 @@ class DatasetIndex(BaseModel):
|
|
|
17
17
|
last_modification_date: datetime
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class PaginatedDatasetIndexList(PaginatedBaseModel):
|
|
21
|
-
|
|
22
|
-
previous: Optional[AnyUrl]
|
|
23
|
-
results: list[DatasetIndex]
|
|
20
|
+
class PaginatedDatasetIndexList(PaginatedBaseModel[DatasetIndex]):
|
|
21
|
+
pass
|
|
24
22
|
|
|
25
23
|
|
|
26
|
-
class DatasetIndexFilters(
|
|
27
|
-
next_token: Optional[str] = None
|
|
28
|
-
page_size: Optional[int] = None
|
|
24
|
+
class DatasetIndexFilters(PaginatedBaseFilters):
|
|
29
25
|
dataset: Optional[str] = None
|
|
30
26
|
dataset__regex: Optional[str] = None
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
from ...utils.api_client import
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedPaginatedAPIClient
|
|
2
2
|
from .models import FileIndex, FileIndexFilters, PaginatedFileIndexList
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class FileIndexClient(
|
|
5
|
+
class FileIndexClient(
|
|
6
|
+
BaseAuthorizedPaginatedAPIClient[
|
|
7
|
+
FileIndex,
|
|
8
|
+
PaginatedFileIndexList,
|
|
9
|
+
FileIndexFilters,
|
|
10
|
+
]
|
|
11
|
+
):
|
|
6
12
|
data_model = FileIndex
|
|
7
13
|
pagination_model = PaginatedFileIndexList
|
|
8
14
|
filter_class = FileIndexFilters
|
|
@@ -10,4 +16,4 @@ class FileIndexClient(BaseAuthorizedAPIClient):
|
|
|
10
16
|
|
|
11
17
|
def get(self, dataset_id: int, file_id: int, **kwargs):
|
|
12
18
|
edp = f"{dataset_id}/{file_id}/"
|
|
13
|
-
return super().
|
|
19
|
+
return super()._get(edp, **kwargs)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from pydantic import
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
5
|
|
|
6
|
-
from ...utils.base_model import
|
|
6
|
+
from ...utils.base_model import PaginatedBaseFilters, PaginatedBaseModel
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class FileIndex(BaseModel):
|
|
@@ -18,15 +18,11 @@ class FileIndex(BaseModel):
|
|
|
18
18
|
err_trace: Optional[str] = Field(..., max_length=5000)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class PaginatedFileIndexList(PaginatedBaseModel):
|
|
22
|
-
|
|
23
|
-
previous: Optional[AnyUrl]
|
|
24
|
-
results: list[FileIndex]
|
|
21
|
+
class PaginatedFileIndexList(PaginatedBaseModel[FileIndex]):
|
|
22
|
+
pass
|
|
25
23
|
|
|
26
24
|
|
|
27
|
-
class FileIndexFilters(
|
|
28
|
-
next_token: Optional[str] = None
|
|
29
|
-
page_size: Optional[int] = None
|
|
25
|
+
class FileIndexFilters(PaginatedBaseFilters):
|
|
30
26
|
dataset_id: Optional[int] = None
|
|
31
27
|
logical_file_name: Optional[str] = None
|
|
32
28
|
logical_file_name__regex: Optional[str] = None
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
from ...utils.api_client import
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedPaginatedAPIClient
|
|
2
2
|
from .models import LumisectionHistogram1D, LumisectionHistogram1DFilters, PaginatedLumisectionHistogram1DList
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class LumisectionHistogram1DClient(
|
|
5
|
+
class LumisectionHistogram1DClient(
|
|
6
|
+
BaseAuthorizedPaginatedAPIClient[
|
|
7
|
+
LumisectionHistogram1D,
|
|
8
|
+
PaginatedLumisectionHistogram1DList,
|
|
9
|
+
LumisectionHistogram1DFilters,
|
|
10
|
+
]
|
|
11
|
+
):
|
|
6
12
|
data_model = LumisectionHistogram1D
|
|
7
13
|
pagination_model = PaginatedLumisectionHistogram1DList
|
|
8
14
|
filter_class = LumisectionHistogram1DFilters
|
|
@@ -10,4 +16,4 @@ class LumisectionHistogram1DClient(BaseAuthorizedAPIClient):
|
|
|
10
16
|
|
|
11
17
|
def get(self, dataset_id: int, run_number: int, ls_number: int, me_id: int, **kwargs):
|
|
12
18
|
edp = f"{dataset_id}/{run_number}/{ls_number}/{me_id}/"
|
|
13
|
-
return super().
|
|
19
|
+
return super()._get(edp, **kwargs)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
4
|
|
|
5
|
-
from ...utils.base_model import
|
|
5
|
+
from ...utils.base_model import PaginatedBaseFilters, PaginatedBaseModel
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class LumisectionHistogram1D(BaseModel):
|
|
@@ -20,15 +20,11 @@ class LumisectionHistogram1D(BaseModel):
|
|
|
20
20
|
data: list[float]
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
class PaginatedLumisectionHistogram1DList(PaginatedBaseModel):
|
|
24
|
-
|
|
25
|
-
previous: Optional[AnyUrl]
|
|
26
|
-
results: list[LumisectionHistogram1D]
|
|
23
|
+
class PaginatedLumisectionHistogram1DList(PaginatedBaseModel[LumisectionHistogram1D]):
|
|
24
|
+
pass
|
|
27
25
|
|
|
28
26
|
|
|
29
|
-
class LumisectionHistogram1DFilters(
|
|
30
|
-
next_token: Optional[str] = None
|
|
31
|
-
page_size: Optional[int] = None
|
|
27
|
+
class LumisectionHistogram1DFilters(PaginatedBaseFilters):
|
|
32
28
|
dataset_id: Optional[int] = None
|
|
33
29
|
file_id: Optional[int] = None
|
|
34
30
|
run_number: Optional[int] = None
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
from ...utils.api_client import
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedPaginatedAPIClient
|
|
2
2
|
from .models import LumisectionHistogram2D, LumisectionHistogram2DFilters, PaginatedLumisectionHistogram2DList
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class LumisectionHistogram2DClient(
|
|
5
|
+
class LumisectionHistogram2DClient(
|
|
6
|
+
BaseAuthorizedPaginatedAPIClient[
|
|
7
|
+
LumisectionHistogram2D,
|
|
8
|
+
PaginatedLumisectionHistogram2DList,
|
|
9
|
+
LumisectionHistogram2DFilters,
|
|
10
|
+
]
|
|
11
|
+
):
|
|
6
12
|
data_model = LumisectionHistogram2D
|
|
7
13
|
pagination_model = PaginatedLumisectionHistogram2DList
|
|
8
14
|
filter_class = LumisectionHistogram2DFilters
|
|
@@ -10,4 +16,4 @@ class LumisectionHistogram2DClient(BaseAuthorizedAPIClient):
|
|
|
10
16
|
|
|
11
17
|
def get(self, dataset_id: int, run_number: int, ls_number: int, me_id: int, **kwargs):
|
|
12
18
|
edp = f"{dataset_id}/{run_number}/{ls_number}/{me_id}/"
|
|
13
|
-
return super().
|
|
19
|
+
return super()._get(edp, **kwargs)
|