cmsdials 1.7.2b1__tar.gz → 2.0.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 (90) hide show
  1. cmsdials-2.0.0/.coveragerc +14 -0
  2. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/.gitignore +3 -0
  3. cmsdials-2.0.0/.gitlab-ci.yml +74 -0
  4. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/.pre-commit-config.yaml +9 -1
  5. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/PKG-INFO +41 -27
  6. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/README.md +40 -26
  7. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/__init__.py +1 -1
  8. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/_version.py +16 -3
  9. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/auth/_base.py +2 -3
  10. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/auth/bearer.py +4 -5
  11. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/auth/client.py +10 -8
  12. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/auth/secret_key.py +4 -4
  13. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/dataset_index/client.py +9 -3
  14. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/dataset_index/models.py +5 -9
  15. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/file_index/client.py +9 -3
  16. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/file_index/models.py +5 -9
  17. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/h1d/client.py +9 -3
  18. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/h1d/models.py +5 -9
  19. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/h2d/client.py +9 -3
  20. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/h2d/models.py +5 -9
  21. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/lumisection/client.py +9 -3
  22. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/lumisection/models.py +5 -9
  23. cmsdials-2.0.0/cmsdials/clients/mes/client.py +17 -0
  24. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/mes/models.py +2 -2
  25. cmsdials-2.0.0/cmsdials/clients/ml_bad_lumisection/client.py +41 -0
  26. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/ml_bad_lumisection/models.py +6 -12
  27. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/ml_models_index/client.py +9 -3
  28. cmsdials-2.0.0/cmsdials/clients/ml_models_index/models.py +47 -0
  29. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/oms_proxy/client.py +4 -4
  30. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/run/client.py +9 -3
  31. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/run/models.py +6 -9
  32. cmsdials-2.0.0/cmsdials/clients/workspace/client.py +49 -0
  33. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/cmsdials.py +2 -0
  34. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/utils/api_client.py +73 -45
  35. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/utils/base_model.py +26 -10
  36. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials.egg-info/PKG-INFO +41 -27
  37. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials.egg-info/SOURCES.txt +6 -0
  38. cmsdials-2.0.0/mypy.ini +5 -0
  39. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/pyproject.toml +5 -1
  40. cmsdials-2.0.0/pytest.ini +2 -0
  41. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/ruff.toml +3 -0
  42. cmsdials-2.0.0/tests/integration/env.py +15 -0
  43. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/integration/test_auth_client.py +6 -9
  44. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/integration/test_file_index_client.py +12 -9
  45. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/integration/test_h1d_client.py +34 -9
  46. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/integration/test_h2d_client.py +34 -9
  47. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/integration/test_lumisection_client.py +13 -9
  48. cmsdials-2.0.0/tests/integration/test_mes_client.py +31 -0
  49. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/integration/test_oms_proxy_client.py +9 -8
  50. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/integration/test_resume.py +8 -7
  51. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/integration/test_run_client.py +12 -9
  52. cmsdials-2.0.0/tests/integration/utils.py +9 -0
  53. cmsdials-2.0.0/tests/unit/__init__.py +0 -0
  54. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tox.ini +1 -1
  55. cmsdials-1.7.2b1/.gitlab-ci.yml +0 -58
  56. cmsdials-1.7.2b1/cmsdials/clients/mes/client.py +0 -12
  57. cmsdials-1.7.2b1/cmsdials/clients/ml_bad_lumisection/client.py +0 -29
  58. cmsdials-1.7.2b1/cmsdials/clients/ml_models_index/models.py +0 -30
  59. cmsdials-1.7.2b1/tests/integration/test_mes_client.py +0 -46
  60. cmsdials-1.7.2b1/tests/integration/utils.py +0 -15
  61. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/.editorconfig +0 -0
  62. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/.mailmap +0 -0
  63. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/.tool-versions +0 -0
  64. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/LICENSE +0 -0
  65. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/auth/__init__.py +0 -0
  66. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/auth/exceptions.py +0 -0
  67. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/auth/models.py +0 -0
  68. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/__init__.py +0 -0
  69. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/dataset_index/__init__.py +0 -0
  70. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/file_index/__init__.py +0 -0
  71. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/h1d/__init__.py +0 -0
  72. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/h2d/__init__.py +0 -0
  73. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/lumisection/__init__.py +0 -0
  74. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/mes/__init__.py +0 -0
  75. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/ml_bad_lumisection/__init__.py +0 -0
  76. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/ml_models_index/__init__.py +0 -0
  77. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/oms_proxy/__init__.py +0 -0
  78. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/oms_proxy/models.py +0 -0
  79. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/clients/run/__init__.py +0 -0
  80. {cmsdials-1.7.2b1/cmsdials/utils → cmsdials-2.0.0/cmsdials/clients/workspace}/__init__.py +0 -0
  81. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/filters.py +2 -2
  82. {cmsdials-1.7.2b1/tests/integration → cmsdials-2.0.0/cmsdials/utils}/__init__.py +0 -0
  83. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/utils/_json.py +0 -0
  84. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials/utils/logger.py +0 -0
  85. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials.egg-info/dependency_links.txt +0 -0
  86. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials.egg-info/requires.txt +0 -0
  87. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/cmsdials.egg-info/top_level.txt +0 -0
  88. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/setup.cfg +0 -0
  89. {cmsdials-1.7.2b1/tests/unit → cmsdials-2.0.0/tests/integration}/__init__.py +0 -0
  90. {cmsdials-1.7.2b1 → cmsdials-2.0.0}/tests/unit/test_base_api_client.py +0 -0
@@ -0,0 +1,14 @@
1
+ [run]
2
+ source =
3
+ cmsdials
4
+ omit =
5
+ cmsdials/_version.py
6
+
7
+ [report]
8
+ show_missing = true
9
+
10
+ [html]
11
+ directory = htmlcov
12
+
13
+ [xml]
14
+ output = coverage.xml
@@ -160,6 +160,9 @@ cython_debug/
160
160
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
161
161
  #.idea/
162
162
 
163
+ # ruff
164
+ .ruff_cache/
165
+
163
166
  # uv
164
167
  uv.lock
165
168
 
@@ -0,0 +1,74 @@
1
+ include:
2
+ - project: "cms-ppd/technical-support/ci-templates"
3
+ ref: release/v1
4
+ file:
5
+ - "templates/python/uv_build.yml"
6
+ - "templates/python/uv_publish.yml"
7
+ - "templates/gitlab/create_release.yml"
8
+
9
+ stages:
10
+ - test
11
+ - build
12
+ - publish
13
+ - create_release
14
+
15
+ ###########
16
+ # Run tests
17
+ ###########
18
+
19
+ test_matrix:
20
+ stage: test
21
+ parallel:
22
+ matrix:
23
+ - PYTHON_VERSION: ["3.9", "3.10", "3.11", "3.12"]
24
+ image: registry.cern.ch/docker.io/library/python:$PYTHON_VERSION
25
+ before_script:
26
+ - python -m pip install --upgrade pip
27
+ - pip install uv
28
+ - uv sync --all-groups --extra pandas --extra tqdm
29
+ script:
30
+ - uv run ruff format --check cmsdials tests
31
+ - uv run ruff check cmsdials tests
32
+ - uv run mypy cmsdials tests
33
+ - uv run pytest tests
34
+ coverage: "/TOTAL.+ ([0-9]{1,3}%)/"
35
+ artifacts:
36
+ reports:
37
+ coverage_report:
38
+ coverage_format: cobertura
39
+ path: coverage.xml
40
+ variables:
41
+ SECRET_KEY: "$SECRET_KEY"
42
+
43
+ ###############
44
+ # Build package
45
+ ###############
46
+
47
+ build:
48
+ extends: .uv_build
49
+ variables:
50
+ UV_SYNC_FLAGS: "--all-groups --extra pandas --extra tqdm"
51
+ needs:
52
+ - test_matrix
53
+
54
+ #################
55
+ # Publish to PyPI
56
+ #################
57
+
58
+ publish:
59
+ extends: .uv_publish
60
+ rules:
61
+ - if: $CI_COMMIT_TAG
62
+ needs:
63
+ - build
64
+
65
+ ##########################
66
+ # Create release in GitLab
67
+ ##########################
68
+
69
+ create_release:
70
+ extends: .create_gitlab_release
71
+ rules:
72
+ - if: $CI_COMMIT_TAG
73
+ needs:
74
+ - publish
@@ -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.3.2
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.7.2b1
3
+ Version: 2.0.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
- ![Build Status](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/badges/dev/pipeline.svg)
25
- ![Coverage](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/badges/dev/coverage.svg)
24
+ ![Build Status](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/badges/develop/pipeline.svg)
25
+ ![Coverage](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/badges/develop/coverage.svg)
26
+ ![Latest Release](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/-/badges/release.svg)
26
27
  [![PyPI version](https://badge.fury.io/py/cmsdials.png)](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
- Note: 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
+ > [!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
- ## Running tests
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 --cov=cmsdials --cov-report=xml --cov-report=term --cov-report=html
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 --cov=cmsdials --cov-report=xml --cov-report=term --cov-report=html
344
+ SECRET_KEY=... BASE_URL=http://localhost:8000 uv run pytest tests
333
345
  ```
334
346
 
335
- ### Tox
347
+ #### Tox
336
348
 
337
- Tox is pre-configured in `pyproject.toml`, so you can run the following to test against multiple python versions locally:
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=... poetry run tox
352
+ SECRET_KEY=... uv run tox
341
353
  ```
342
354
 
343
355
  **[asdf](https://asdf-vm.com/) users**
@@ -350,34 +362,36 @@ 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
- ## Releasing the package on PyPI
365
+ ### Releasing the package on PyPI
354
366
 
355
- ### Build
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
- You can use poetry to build the package using:
369
+ #### CI
358
370
 
359
- ```bash
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
- The build system requires `poetry-dynamic-versioning` plugin installed to update the package version during build time. To install this plugin simply:
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`.
375
+ > https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/-/settings/access_tokens
364
376
 
365
- ```
366
- poetry self add "poetry-dynamic-versioning[plugin]"
367
- ```
377
+ #### Manual
368
378
 
369
- The build system will automatically update the package version based on the git tag of the current commit.
379
+ If you want to follow the manual approach, you need to first build and then publish.
370
380
 
371
- ### Publish
381
+ ##### Build
372
382
 
373
- To publish the package will need maintainer rights to the `cmsdials` page in PyPI and configure your poetry installation to publish with a PyPI api token. Provided you have already generate a PyPI api token in your account you can configure your poetry installation using:
383
+ You can use uv to build the package using:
374
384
 
385
+ ```bash
386
+ uv build
375
387
  ```
376
- poetry config pypi-token.pypi <your-api-token>
377
- ```
378
388
 
379
- Then, to build and publish in one go you can do:
389
+ The build system will automatically update the package version based on the git tag of the current commit.
390
+
391
+ ##### Publish
392
+
393
+ Provided you have already generate a PyPI api token in your account or in CMDQM org, you can publish using:
380
394
 
381
395
  ```bash
382
- poetry publish --build
396
+ UV_PUBLISH_TOKEN=... uv publish
383
397
  ```
@@ -1,5 +1,6 @@
1
- ![Build Status](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/badges/dev/pipeline.svg)
2
- ![Coverage](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/badges/dev/coverage.svg)
1
+ ![Build Status](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/badges/develop/pipeline.svg)
2
+ ![Coverage](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/badges/develop/coverage.svg)
3
+ ![Latest Release](https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/-/badges/release.svg)
3
4
  [![PyPI version](https://badge.fury.io/py/cmsdials.png)](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
- Note: 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
+ > [!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
- ## Running tests
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 --cov=cmsdials --cov-report=xml --cov-report=term --cov-report=html
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 --cov=cmsdials --cov-report=xml --cov-report=term --cov-report=html
321
+ SECRET_KEY=... BASE_URL=http://localhost:8000 uv run pytest tests
310
322
  ```
311
323
 
312
- ### Tox
324
+ #### Tox
313
325
 
314
- Tox is pre-configured in `pyproject.toml`, so you can run the following to test against multiple python versions locally:
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=... poetry run tox
329
+ SECRET_KEY=... uv run tox
318
330
  ```
319
331
 
320
332
  **[asdf](https://asdf-vm.com/) users**
@@ -327,34 +339,36 @@ 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
- ## Releasing the package on PyPI
342
+ ### Releasing the package on PyPI
331
343
 
332
- ### Build
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
- You can use poetry to build the package using:
346
+ #### CI
335
347
 
336
- ```bash
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
- The build system requires `poetry-dynamic-versioning` plugin installed to update the package version during build time. To install this plugin simply:
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`.
352
+ > https://gitlab.cern.ch/cms-dqmdc/libraries/dials-py/-/settings/access_tokens
341
353
 
342
- ```
343
- poetry self add "poetry-dynamic-versioning[plugin]"
344
- ```
354
+ #### Manual
345
355
 
346
- The build system will automatically update the package version based on the git tag of the current commit.
356
+ If you want to follow the manual approach, you need to first build and then publish.
347
357
 
348
- ### Publish
358
+ ##### Build
349
359
 
350
- To publish the package will need maintainer rights to the `cmsdials` page in PyPI and configure your poetry installation to publish with a PyPI api token. Provided you have already generate a PyPI api token in your account you can configure your poetry installation using:
360
+ You can use uv to build the package using:
351
361
 
362
+ ```bash
363
+ uv build
352
364
  ```
353
- poetry config pypi-token.pypi <your-api-token>
354
- ```
355
365
 
356
- Then, to build and publish in one go you can do:
366
+ The build system will automatically update the package version based on the git tag of the current commit.
367
+
368
+ ##### Publish
369
+
370
+ Provided you have already generate a PyPI api token in your account or in CMDQM org, you can publish using:
357
371
 
358
372
  ```bash
359
- poetry publish --build
373
+ UV_PUBLISH_TOKEN=... uv publish
360
374
  ```
@@ -2,4 +2,4 @@ from ._version import __version__
2
2
  from .cmsdials import Dials
3
3
 
4
4
 
5
- __all__ = ["__version__", "Dials"]
5
+ __all__ = ["Dials", "__version__"]
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '1.7.2b1'
21
- __version_tuple__ = version_tuple = (1, 7, 2, 'b1')
31
+ __version__ = version = '2.0.0'
32
+ __version_tuple__ = version_tuple = (2, 0, 0)
33
+
34
+ __commit_id__ = commit_id = 'g68ff6a70a'
@@ -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: Mapping[str, str]) -> None:
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: Mapping[str, str]):
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
- token: Token = self.client.device_auth_flow()
59
- self._set_token(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: Token = client.device_auth_flow()
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: Mapping[str, str]) -> None:
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
- response = response.json()
31
- return Device(**response)
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
- response = response.json()
50
+ data = response.json()
51
51
  curr_datetime = datetime.now()
52
- token = DeviceToken(**response)
52
+ token = DeviceToken(**data)
53
+ token_dict = token.model_dump()
53
54
  return Token(
54
- **token.dict(),
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
- response = response.json()
74
+ data = response.json()
74
75
  curr_datetime = datetime.now()
75
- token = RefreshToken(**response)
76
+ token = RefreshToken(**data)
77
+ token_dict = token.model_dump()
76
78
  return Token(
77
- **token.dict(),
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 collections.abc import Mapping
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: Mapping[str, str]) -> None:
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: Mapping[str, str]):
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 BaseAuthorizedAPIClient
1
+ from ...utils.api_client import BaseAuthorizedPaginatedAPIClient
2
2
  from .models import DatasetIndex, DatasetIndexFilters, PaginatedDatasetIndexList
3
3
 
4
4
 
5
- class DatasetIndexClient(BaseAuthorizedAPIClient):
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().get(edp, **kwargs)
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 AnyUrl, BaseModel, Field
4
+ from pydantic import BaseModel, Field
5
5
 
6
- from ...utils.base_model import OBaseModel, PaginatedBaseModel
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
- next: Optional[AnyUrl]
22
- previous: Optional[AnyUrl]
23
- results: list[DatasetIndex]
20
+ class PaginatedDatasetIndexList(PaginatedBaseModel[DatasetIndex]):
21
+ pass
24
22
 
25
23
 
26
- class DatasetIndexFilters(OBaseModel):
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 BaseAuthorizedAPIClient
1
+ from ...utils.api_client import BaseAuthorizedPaginatedAPIClient
2
2
  from .models import FileIndex, FileIndexFilters, PaginatedFileIndexList
3
3
 
4
4
 
5
- class FileIndexClient(BaseAuthorizedAPIClient):
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().get(edp, **kwargs)
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 AnyUrl, BaseModel, Field
4
+ from pydantic import BaseModel, Field
5
5
 
6
- from ...utils.base_model import OBaseModel, PaginatedBaseModel
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
- next: Optional[AnyUrl]
23
- previous: Optional[AnyUrl]
24
- results: list[FileIndex]
21
+ class PaginatedFileIndexList(PaginatedBaseModel[FileIndex]):
22
+ pass
25
23
 
26
24
 
27
- class FileIndexFilters(OBaseModel):
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