cmsdials 0.3.0__tar.gz → 1.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 (47) hide show
  1. {cmsdials-0.3.0 → cmsdials-1.0.0}/PKG-INFO +24 -20
  2. {cmsdials-0.3.0 → cmsdials-1.0.0}/README.md +22 -18
  3. cmsdials-1.0.0/cmsdials/clients/file_index/models.py +35 -0
  4. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/clients/h1d/client.py +1 -1
  5. cmsdials-1.0.0/cmsdials/clients/h1d/models.py +49 -0
  6. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/clients/h2d/client.py +1 -1
  7. cmsdials-1.0.0/cmsdials/clients/h2d/models.py +52 -0
  8. cmsdials-1.0.0/cmsdials/clients/lumisection/client.py +27 -0
  9. cmsdials-1.0.0/cmsdials/clients/lumisection/models.py +34 -0
  10. cmsdials-1.0.0/cmsdials/clients/run/client.py +27 -0
  11. cmsdials-1.0.0/cmsdials/clients/run/models.py +28 -0
  12. cmsdials-1.0.0/cmsdials/cmsdials.py +17 -0
  13. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/filters.py +0 -2
  14. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/utils/api_client.py +27 -46
  15. cmsdials-1.0.0/pyproject.toml +126 -0
  16. cmsdials-0.3.0/cmsdials/clients/bad_file_index/client.py +0 -9
  17. cmsdials-0.3.0/cmsdials/clients/bad_file_index/models.py +0 -30
  18. cmsdials-0.3.0/cmsdials/clients/file_index/models.py +0 -44
  19. cmsdials-0.3.0/cmsdials/clients/h1d/models.py +0 -44
  20. cmsdials-0.3.0/cmsdials/clients/h2d/models.py +0 -50
  21. cmsdials-0.3.0/cmsdials/clients/lumisection/client.py +0 -20
  22. cmsdials-0.3.0/cmsdials/clients/lumisection/models.py +0 -35
  23. cmsdials-0.3.0/cmsdials/clients/run/client.py +0 -9
  24. cmsdials-0.3.0/cmsdials/clients/run/models.py +0 -31
  25. cmsdials-0.3.0/cmsdials/cmsdials.py +0 -19
  26. cmsdials-0.3.0/cmsdials/utils/__init__.py +0 -0
  27. cmsdials-0.3.0/pyproject.toml +0 -31
  28. {cmsdials-0.3.0 → cmsdials-1.0.0}/LICENSE +0 -0
  29. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/__init__.py +0 -0
  30. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/auth/__init__.py +0 -0
  31. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/auth/_base.py +0 -0
  32. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/auth/bearer.py +0 -0
  33. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/auth/client.py +0 -0
  34. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/auth/exceptions.py +0 -0
  35. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/auth/models.py +0 -0
  36. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/auth/secret_key.py +0 -0
  37. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/clients/__init__.py +0 -0
  38. {cmsdials-0.3.0/cmsdials/clients/bad_file_index → cmsdials-1.0.0/cmsdials/clients/file_index}/__init__.py +0 -0
  39. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/clients/file_index/client.py +0 -0
  40. {cmsdials-0.3.0/cmsdials/clients/file_index → cmsdials-1.0.0/cmsdials/clients/h1d}/__init__.py +0 -0
  41. {cmsdials-0.3.0/cmsdials/clients/h1d → cmsdials-1.0.0/cmsdials/clients/h2d}/__init__.py +0 -0
  42. {cmsdials-0.3.0/cmsdials/clients/h2d → cmsdials-1.0.0/cmsdials/clients/lumisection}/__init__.py +0 -0
  43. {cmsdials-0.3.0/cmsdials/clients/lumisection → cmsdials-1.0.0/cmsdials/clients/run}/__init__.py +0 -0
  44. {cmsdials-0.3.0/cmsdials/clients/run → cmsdials-1.0.0/cmsdials/utils}/__init__.py +0 -0
  45. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/utils/_json.py +0 -0
  46. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/utils/base_model.py +0 -0
  47. {cmsdials-0.3.0 → cmsdials-1.0.0}/cmsdials/utils/logger.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cmsdials
3
- Version: 0.3.0
3
+ Version: 1.0.0
4
4
  Summary: The Python api client interface to DIALS service
5
5
  Home-page: https://github.com/cms-DQM/dials-py
6
6
  Author: Gabriel Moreira
7
7
  Author-email: gabrielmscampos@gmail.com
8
8
  Requires-Python: >=3.9,<4.0
9
- Classifier: Development Status :: 4 - Beta
9
+ Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python
12
12
  Classifier: Programming Language :: Python :: 3
@@ -76,10 +76,18 @@ from cmsdials import Dials
76
76
  from cmsdials.filters import LumisectionHistogram1DFilters
77
77
 
78
78
  creds = Credentials.from_creds_file()
79
- dials = Dials(creds, nthreads=2)
79
+ dials = Dials(creds)
80
80
 
81
81
  # Getting h1d data
82
- data = dials.h1d.list_all(LumisectionHistogram1DFilters(title="PixelPhase1/Tracks/PXBarrel/charge_PXLayer_2"), max_pages=5)
82
+ data = dials.h1d.list_all(LumisectionHistogram1DFilters(me="PixelPhase1/Tracks/PXBarrel/charge_PXLayer_2"), max_pages=5)
83
+ ```
84
+
85
+ ### Workspace
86
+
87
+ Users are automatically routed to a workspace based on e-groups, but it is possible to overwrite this configuration and inspect data from others workspaces:
88
+
89
+ ```python
90
+ dials = Dials(creds, workspace="jetmet")
83
91
  ```
84
92
 
85
93
  ## Available endpoints
@@ -89,12 +97,11 @@ This package interacts with DIALS api endpoints using underlying classes in `Dia
89
97
  ### Retrieving a specific object using `get`
90
98
 
91
99
  ```python
92
- dials.bad_file_index.get(id=1)
93
- dials.file_index.get(id=1)
100
+ dials.file_index.get(id=3386119397)
94
101
  dials.h1d.get(id=1)
95
102
  dials.h2d.get(id=1)
96
- dials.lumi.get(id=1)
97
- dials.run.get(id=1)
103
+ dials.lumi.get(dataset_id=14677060, run_number=367094, ls_number=1)
104
+ dials.run.get(dataset_id=14677060, run_number=367094)
98
105
  ```
99
106
 
100
107
  ### Retrieving a list of objects per page using `list`
@@ -102,7 +109,6 @@ dials.run.get(id=1)
102
109
  It is possible to get a list of entries from those endpoint using the `list` and `list_all` methods, the `list` method will fetch only one page and the `list_all` will fetch all available pages:
103
110
 
104
111
  ```python
105
- dials.bad_file_index.list()
106
112
  dials.file_index.list()
107
113
  dials.h1d.list()
108
114
  dials.h2d.list()
@@ -112,8 +118,9 @@ dials.run.list()
112
118
 
113
119
  ### Retrieving all available pages of a list of objects using `list_all`
114
120
 
121
+ 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.
122
+
115
123
  ```python
116
- dials.bad_file_index.list_all()
117
124
  dials.file_index.list_all()
118
125
  dials.h1d.list_all()
119
126
  dials.h2d.list_all()
@@ -133,7 +140,6 @@ Keep in mind that calling those methods without any filter can take a lot of tim
133
140
 
134
141
  ```python
135
142
  from cmsdials.filters import (
136
- BadFileIndexFilters,
137
143
  FileIndexFilters,
138
144
  LumisectionHistogram1DFilters,
139
145
  LumisectionHistogram2DFilters,
@@ -141,17 +147,15 @@ from cmsdials.filters import (
141
147
  RunFilters
142
148
  )
143
149
 
144
- dials.bad_file_index.list(BadFileIndexFilters(path_contains="ZeroBias"))
145
-
146
- dials.file_index.list(FileIndexFilters(page="10"))
150
+ dials.file_index.list(FileIndexFilters(dataset__regex="2024B"))
147
151
 
148
- dials.h1d.list(LumisectionHistogram1DFilters(title="PixelPhase1/Tracks/PXBarrel/charge_PXLayer_2", page="15"))
152
+ dials.h1d.list(LumisectionHistogram1DFilters(me="PixelPhase1/Tracks/PXBarrel/charge_PXLayer_2"))
149
153
 
150
- dials.h2d.list_all(LumisectionHistogram2DFilters(title_contains="EEOT digi occupancy EE +", min_entries=100, min_run_number=360392, max_run_number=365000), max_pages=5)
154
+ dials.h2d.list_all(LumisectionHistogram2DFilters(me__regex="PXBarrel", ls_number=78, entries__gte=100), max_pages=5)
151
155
 
152
156
  dials.lumi.list_all(LumisectionFilters(run_number=360392), max_pages=5)
153
157
 
154
- dials.run.list_all(RunFilters(min_run_number=360392, max_run_number=365000), max_pages=5)
158
+ dials.run.list_all(RunFilters(run_number__gte=360392, run_number__lte=365000), max_pages=5)
155
159
  ```
156
160
 
157
161
  ### Dials MEs
@@ -173,12 +177,12 @@ from cmsdials import Dials
173
177
  from cmsdials.filters import LumisectionHistogram2DFilters
174
178
 
175
179
  DEV_URL = "http://localhost:8000/"
176
- DEV_CACHE_DIR = ".cache-dev
180
+ DEV_CACHE_DIR = ".cache-dev"
177
181
 
178
182
  auth = AuthClient(base_url=DEV_URL)
179
183
  creds = Credentials.from_creds_file(cache_dir=DEV_CACHE_DIR, client=auth) # Make sure to specify the auth client with overwritten values, using another cache_dir is recommended
180
- dials = Dials(creds, nthreads=2, base_url=DEV_URL)
184
+ dials = Dials(creds, base_url=DEV_URL)
181
185
 
182
- dials.h2d.list_all(LumisectionHistogram2DFilters(title_contains="EEOT digi occupancy EE +", min_entries=100, min_run_number=360392, max_run_number=365000), max_pages=5)
186
+ 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)
183
187
  ```
184
188
 
@@ -53,10 +53,18 @@ from cmsdials import Dials
53
53
  from cmsdials.filters import LumisectionHistogram1DFilters
54
54
 
55
55
  creds = Credentials.from_creds_file()
56
- dials = Dials(creds, nthreads=2)
56
+ dials = Dials(creds)
57
57
 
58
58
  # Getting h1d data
59
- data = dials.h1d.list_all(LumisectionHistogram1DFilters(title="PixelPhase1/Tracks/PXBarrel/charge_PXLayer_2"), max_pages=5)
59
+ data = dials.h1d.list_all(LumisectionHistogram1DFilters(me="PixelPhase1/Tracks/PXBarrel/charge_PXLayer_2"), max_pages=5)
60
+ ```
61
+
62
+ ### Workspace
63
+
64
+ Users are automatically routed to a workspace based on e-groups, but it is possible to overwrite this configuration and inspect data from others workspaces:
65
+
66
+ ```python
67
+ dials = Dials(creds, workspace="jetmet")
60
68
  ```
61
69
 
62
70
  ## Available endpoints
@@ -66,12 +74,11 @@ This package interacts with DIALS api endpoints using underlying classes in `Dia
66
74
  ### Retrieving a specific object using `get`
67
75
 
68
76
  ```python
69
- dials.bad_file_index.get(id=1)
70
- dials.file_index.get(id=1)
77
+ dials.file_index.get(id=3386119397)
71
78
  dials.h1d.get(id=1)
72
79
  dials.h2d.get(id=1)
73
- dials.lumi.get(id=1)
74
- dials.run.get(id=1)
80
+ dials.lumi.get(dataset_id=14677060, run_number=367094, ls_number=1)
81
+ dials.run.get(dataset_id=14677060, run_number=367094)
75
82
  ```
76
83
 
77
84
  ### Retrieving a list of objects per page using `list`
@@ -79,7 +86,6 @@ dials.run.get(id=1)
79
86
  It is possible to get a list of entries from those endpoint using the `list` and `list_all` methods, the `list` method will fetch only one page and the `list_all` will fetch all available pages:
80
87
 
81
88
  ```python
82
- dials.bad_file_index.list()
83
89
  dials.file_index.list()
84
90
  dials.h1d.list()
85
91
  dials.h2d.list()
@@ -89,8 +95,9 @@ dials.run.list()
89
95
 
90
96
  ### Retrieving all available pages of a list of objects using `list_all`
91
97
 
98
+ 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.
99
+
92
100
  ```python
93
- dials.bad_file_index.list_all()
94
101
  dials.file_index.list_all()
95
102
  dials.h1d.list_all()
96
103
  dials.h2d.list_all()
@@ -110,7 +117,6 @@ Keep in mind that calling those methods without any filter can take a lot of tim
110
117
 
111
118
  ```python
112
119
  from cmsdials.filters import (
113
- BadFileIndexFilters,
114
120
  FileIndexFilters,
115
121
  LumisectionHistogram1DFilters,
116
122
  LumisectionHistogram2DFilters,
@@ -118,17 +124,15 @@ from cmsdials.filters import (
118
124
  RunFilters
119
125
  )
120
126
 
121
- dials.bad_file_index.list(BadFileIndexFilters(path_contains="ZeroBias"))
122
-
123
- dials.file_index.list(FileIndexFilters(page="10"))
127
+ dials.file_index.list(FileIndexFilters(dataset__regex="2024B"))
124
128
 
125
- dials.h1d.list(LumisectionHistogram1DFilters(title="PixelPhase1/Tracks/PXBarrel/charge_PXLayer_2", page="15"))
129
+ dials.h1d.list(LumisectionHistogram1DFilters(me="PixelPhase1/Tracks/PXBarrel/charge_PXLayer_2"))
126
130
 
127
- dials.h2d.list_all(LumisectionHistogram2DFilters(title_contains="EEOT digi occupancy EE +", min_entries=100, min_run_number=360392, max_run_number=365000), max_pages=5)
131
+ dials.h2d.list_all(LumisectionHistogram2DFilters(me__regex="PXBarrel", ls_number=78, entries__gte=100), max_pages=5)
128
132
 
129
133
  dials.lumi.list_all(LumisectionFilters(run_number=360392), max_pages=5)
130
134
 
131
- dials.run.list_all(RunFilters(min_run_number=360392, max_run_number=365000), max_pages=5)
135
+ dials.run.list_all(RunFilters(run_number__gte=360392, run_number__lte=365000), max_pages=5)
132
136
  ```
133
137
 
134
138
  ### Dials MEs
@@ -150,11 +154,11 @@ from cmsdials import Dials
150
154
  from cmsdials.filters import LumisectionHistogram2DFilters
151
155
 
152
156
  DEV_URL = "http://localhost:8000/"
153
- DEV_CACHE_DIR = ".cache-dev
157
+ DEV_CACHE_DIR = ".cache-dev"
154
158
 
155
159
  auth = AuthClient(base_url=DEV_URL)
156
160
  creds = Credentials.from_creds_file(cache_dir=DEV_CACHE_DIR, client=auth) # Make sure to specify the auth client with overwritten values, using another cache_dir is recommended
157
- dials = Dials(creds, nthreads=2, base_url=DEV_URL)
161
+ dials = Dials(creds, base_url=DEV_URL)
158
162
 
159
- dials.h2d.list_all(LumisectionHistogram2DFilters(title_contains="EEOT digi occupancy EE +", min_entries=100, min_run_number=360392, max_run_number=365000), max_pages=5)
163
+ 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)
160
164
  ```
@@ -0,0 +1,35 @@
1
+ from datetime import datetime
2
+ from typing import List, Optional
3
+
4
+ from pydantic import AnyUrl, BaseModel, Field
5
+
6
+ from ...utils.base_model import OBaseModel
7
+
8
+
9
+ class FileIndex(BaseModel):
10
+ dataset_id: int
11
+ dataset: str = Field(..., max_length=255)
12
+ file_id: int
13
+ file_size: int
14
+ creation_date: datetime
15
+ last_modification_date: datetime
16
+ logical_file_name: str
17
+ status: str = Field(..., max_length=15)
18
+ err_trace: Optional[str] = Field(..., max_length=5000)
19
+
20
+
21
+ class PaginatedFileIndexList(BaseModel):
22
+ next: Optional[AnyUrl]
23
+ previous: Optional[AnyUrl]
24
+ results: List[FileIndex]
25
+
26
+
27
+ class FileIndexFilters(OBaseModel):
28
+ next_token: Optional[str] = None
29
+ dataset_id: Optional[int] = None
30
+ logical_file_name: Optional[str] = None
31
+ logical_file_name__regex: Optional[str] = None
32
+ status: Optional[str] = None
33
+ min_size: Optional[int] = None
34
+ dataset: Optional[str] = None
35
+ dataset__regex: Optional[str] = None
@@ -6,4 +6,4 @@ class LumisectionHistogram1DClient(BaseAuthorizedAPIClient):
6
6
  data_model = LumisectionHistogram1D
7
7
  pagination_model = PaginatedLumisectionHistogram1DList
8
8
  filter_class = LumisectionHistogram1DFilters
9
- lookup_url = "lumisection-h1d/"
9
+ lookup_url = "th1/"
@@ -0,0 +1,49 @@
1
+ from typing import List, Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel, Field
4
+
5
+ from ...utils.base_model import OBaseModel
6
+
7
+
8
+ class LumisectionHistogram1D(BaseModel):
9
+ hist_id: int
10
+ dataset: str = Field(..., max_length=255)
11
+ me: str = Field(..., max_length=255)
12
+ dataset_id: int
13
+ file_id: int
14
+ run_number: int
15
+ ls_number: int
16
+ me_id: int
17
+ ls_id: int
18
+ x_min: float
19
+ x_max: float
20
+ x_bin: float
21
+ entries: int
22
+ data: list[float]
23
+
24
+
25
+ class PaginatedLumisectionHistogram1DList(BaseModel):
26
+ next: Optional[AnyUrl]
27
+ previous: Optional[AnyUrl]
28
+ results: List[LumisectionHistogram1D]
29
+
30
+
31
+ class LumisectionHistogram1DFilters(OBaseModel):
32
+ next_token: Optional[str] = None
33
+ dataset_id: Optional[int] = None
34
+ file_id: Optional[int] = None
35
+ run_number: Optional[int] = None
36
+ run_number__lte: Optional[int] = None
37
+ run_number__gte: Optional[int] = None
38
+ ls_number: Optional[int] = None
39
+ ls_numbet__lte: Optional[int] = None
40
+ ls_number__gte: Optional[int] = None
41
+ me_id: Optional[int] = None
42
+ ls_id: Optional[int] = None
43
+ entries__gte: Optional[int] = None
44
+ dataset: Optional[str] = None
45
+ dataset__regex: Optional[str] = None
46
+ logical_file_name: Optional[str] = None
47
+ logical_file_name__regex: Optional[str] = None
48
+ me: Optional[str] = None
49
+ me__regex: Optional[str] = None
@@ -6,4 +6,4 @@ class LumisectionHistogram2DClient(BaseAuthorizedAPIClient):
6
6
  data_model = LumisectionHistogram2D
7
7
  pagination_model = PaginatedLumisectionHistogram2DList
8
8
  filter_class = LumisectionHistogram2DFilters
9
- lookup_url = "lumisection-h2d/"
9
+ lookup_url = "th2/"
@@ -0,0 +1,52 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel, Field
4
+
5
+ from ...utils.base_model import OBaseModel
6
+
7
+
8
+ class LumisectionHistogram2D(BaseModel):
9
+ hist_id: int
10
+ dataset: str = Field(..., max_length=255)
11
+ me: str = Field(..., max_length=255)
12
+ dataset_id: int
13
+ file_id: int
14
+ run_number: int
15
+ ls_number: int
16
+ me_id: int
17
+ ls_id: int
18
+ x_min: float
19
+ x_max: float
20
+ x_bin: float
21
+ y_min: float
22
+ y_max: float
23
+ y_bin: float
24
+ entries: int
25
+ data: list[list[float]]
26
+
27
+
28
+ class PaginatedLumisectionHistogram2DList(BaseModel):
29
+ next: Optional[AnyUrl]
30
+ previous: Optional[AnyUrl]
31
+ results: list[LumisectionHistogram2D]
32
+
33
+
34
+ class LumisectionHistogram2DFilters(OBaseModel):
35
+ next_token: Optional[str] = None
36
+ dataset_id: Optional[int] = None
37
+ file_id: Optional[int] = None
38
+ run_number: Optional[int] = None
39
+ run_number__lte: Optional[int] = None
40
+ run_number__gte: Optional[int] = None
41
+ ls_number: Optional[int] = None
42
+ ls_numbet__lte: Optional[int] = None
43
+ ls_number__gte: Optional[int] = None
44
+ me_id: Optional[int] = None
45
+ ls_id: Optional[int] = None
46
+ entries__gte: Optional[int] = None
47
+ dataset: Optional[str] = None
48
+ dataset__regex: Optional[str] = None
49
+ logical_file_name: Optional[str] = None
50
+ logical_file_name__regex: Optional[str] = None
51
+ me: Optional[str] = None
52
+ me__regex: Optional[str] = None
@@ -0,0 +1,27 @@
1
+ import requests
2
+ from requests.exceptions import HTTPError
3
+
4
+ from ...utils.api_client import BaseAuthorizedAPIClient
5
+ from ...utils.logger import logger
6
+ from .models import Lumisection, LumisectionFilters, PaginatedLumisectionList
7
+
8
+
9
+ class LumisectionClient(BaseAuthorizedAPIClient):
10
+ data_model = Lumisection
11
+ pagination_model = PaginatedLumisectionList
12
+ filter_class = LumisectionFilters
13
+ lookup_url = "lumisection/"
14
+
15
+ def get(self, dataset_id: int, run_number: int, ls_number: int):
16
+ endpoint_url = f"{self.api_url}{self.lookup_url}{dataset_id}/{run_number}/{ls_number}/"
17
+ headers = self._build_headers()
18
+ response = requests.get(endpoint_url, headers=headers)
19
+
20
+ try:
21
+ response.raise_for_status()
22
+ except HTTPError as err:
23
+ logger.info(f"Api raw response: {response.text}")
24
+ raise err
25
+
26
+ response = response.json()
27
+ return self.data_model(**response)
@@ -0,0 +1,34 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel, Field
4
+
5
+ from ...utils.base_model import OBaseModel
6
+
7
+
8
+ class Lumisection(BaseModel):
9
+ dataset_id: int
10
+ dataset: str = Field(..., max_length=255)
11
+ run_number: int
12
+ ls_number: int
13
+ ls_id: int
14
+ th1_count: int
15
+ th2_count: int
16
+
17
+
18
+ class PaginatedLumisectionList(BaseModel):
19
+ next: Optional[AnyUrl]
20
+ previous: Optional[AnyUrl]
21
+ results: list[Lumisection]
22
+
23
+
24
+ class LumisectionFilters(OBaseModel):
25
+ next_token: Optional[str] = None
26
+ dataset_id: Optional[int] = None
27
+ run_number: Optional[int] = None
28
+ run_number__lte: Optional[int] = None
29
+ run_number__gte: Optional[int] = None
30
+ ls_number: Optional[int] = None
31
+ ls_number__lte: Optional[int] = None
32
+ ls_number__gte: Optional[int] = None
33
+ dataset: Optional[str] = None
34
+ dataset__regex: Optional[str] = None
@@ -0,0 +1,27 @@
1
+ import requests
2
+ from requests.exceptions import HTTPError
3
+
4
+ from ...utils.api_client import BaseAuthorizedAPIClient
5
+ from ...utils.logger import logger
6
+ from .models import PaginatedRunList, Run, RunFilters
7
+
8
+
9
+ class RunClient(BaseAuthorizedAPIClient):
10
+ data_model = Run
11
+ pagination_model = PaginatedRunList
12
+ filter_class = RunFilters
13
+ lookup_url = "run/"
14
+
15
+ def get(self, dataset_id: int, run_number: int):
16
+ endpoint_url = f"{self.api_url}{self.lookup_url}{dataset_id}/{run_number}/"
17
+ headers = self._build_headers()
18
+ response = requests.get(endpoint_url, headers=headers)
19
+
20
+ try:
21
+ response.raise_for_status()
22
+ except HTTPError as err:
23
+ logger.info(f"Api raw response: {response.text}")
24
+ raise err
25
+
26
+ response = response.json()
27
+ return self.data_model(**response)
@@ -0,0 +1,28 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel, Field
4
+
5
+ from ...utils.base_model import OBaseModel
6
+
7
+
8
+ class Run(BaseModel):
9
+ dataset_id: int
10
+ dataset: str = Field(..., max_length=255)
11
+ run_number: int
12
+ ls_count: int
13
+
14
+
15
+ class PaginatedRunList(BaseModel):
16
+ next: Optional[AnyUrl]
17
+ previous: Optional[AnyUrl]
18
+ results: list[Run]
19
+
20
+
21
+ class RunFilters(OBaseModel):
22
+ next_token: Optional[str] = None
23
+ dataset_id: Optional[int] = None
24
+ run_number: Optional[int] = None
25
+ run_number__lte: Optional[int] = None
26
+ run_number__gte: Optional[int] = None
27
+ dataset: Optional[str] = None
28
+ dataset__regex: Optional[str] = None
@@ -0,0 +1,17 @@
1
+ from typing import Optional
2
+
3
+ from .auth._base import BaseCredentials
4
+ from .clients.file_index.client import FileIndexClient
5
+ from .clients.h1d.client import LumisectionHistogram1DClient
6
+ from .clients.h2d.client import LumisectionHistogram2DClient
7
+ from .clients.lumisection.client import LumisectionClient
8
+ from .clients.run.client import RunClient
9
+
10
+
11
+ class Dials:
12
+ def __init__(self, creds: BaseCredentials, workspace: Optional[str] = None, *args, **kwargs) -> None:
13
+ self.file_index = FileIndexClient(creds, workspace, *args, **kwargs)
14
+ self.h1d = LumisectionHistogram1DClient(creds, workspace, *args, **kwargs)
15
+ self.h2d = LumisectionHistogram2DClient(creds, workspace, *args, **kwargs)
16
+ self.lumi = LumisectionClient(creds, workspace, *args, **kwargs)
17
+ self.run = RunClient(creds, workspace, *args, **kwargs)
@@ -1,4 +1,3 @@
1
- from .clients.bad_file_index.models import BadFileIndexFilters
2
1
  from .clients.file_index.models import FileIndexFilters
3
2
  from .clients.h1d.models import LumisectionHistogram1DFilters
4
3
  from .clients.h2d.models import LumisectionHistogram2DFilters
@@ -6,7 +5,6 @@ from .clients.lumisection.models import LumisectionFilters
6
5
  from .clients.run.models import RunFilters
7
6
 
8
7
  __all__ = [
9
- "BadFileIndexFilters",
10
8
  "FileIndexFilters",
11
9
  "LumisectionHistogram1DFilters",
12
10
  "LumisectionHistogram2DFilters",
@@ -1,6 +1,5 @@
1
- from concurrent.futures import ThreadPoolExecutor
2
- from math import ceil
3
1
  from typing import Optional
2
+ from urllib.parse import parse_qs, urlparse
4
3
 
5
4
  import requests
6
5
  from requests.exceptions import HTTPError
@@ -38,15 +37,27 @@ class BaseAuthorizedAPIClient(BaseAPIClient):
38
37
  filter_class = None
39
38
  lookup_url = None
40
39
 
41
- def __init__(self, creds: BaseCredentials, nthreads: Optional[int] = None, *args: str, **kwargs: str) -> None:
40
+ def __init__(
41
+ self,
42
+ creds: BaseCredentials,
43
+ workspace: Optional[str] = None,
44
+ *args: str,
45
+ **kwargs: str,
46
+ ) -> None:
42
47
  super().__init__(*args, **kwargs)
43
48
  self.creds = creds
44
- self.nthreads = nthreads or 1
49
+ self.workspace = workspace
45
50
 
46
- def get(self, id: str):
51
+ def _build_headers(self) -> dict:
52
+ base = {"accept": "application/json"}
53
+ if self.workspace is not None:
54
+ base["Workspace"] = self.workspace
55
+ self.creds.before_request(base)
56
+ return base
57
+
58
+ def get(self, id: int):
47
59
  endpoint_url = self.api_url + self.lookup_url + str(id) + "/"
48
- headers = {"accept": "application/json"}
49
- self.creds.before_request(headers)
60
+ headers = self._build_headers()
50
61
  response = requests.get(endpoint_url, headers=headers)
51
62
 
52
63
  try:
@@ -61,8 +72,7 @@ class BaseAuthorizedAPIClient(BaseAPIClient):
61
72
  def list(self, filters=None):
62
73
  filters = filters or self.filter_class()
63
74
  endpoint_url = self.api_url + self.lookup_url
64
- headers = {"accept": "application/json"}
65
- self.creds.before_request(headers)
75
+ headers = self._build_headers()
66
76
  response = requests.get(endpoint_url, headers=headers, params=filters.cleandict())
67
77
 
68
78
  try:
@@ -75,54 +85,25 @@ class BaseAuthorizedAPIClient(BaseAPIClient):
75
85
  return self.pagination_model(**response)
76
86
 
77
87
  def __list_sync(self, filters, max_pages: Optional[int] = None):
78
- page = 1
88
+ next_token = None
79
89
  results = []
80
90
  is_last_page = False
91
+ total_pages = 0
81
92
 
82
93
  while is_last_page is False:
83
94
  curr_filters = self.filter_class(**filters.dict())
84
- curr_filters.page = str(page)
95
+ curr_filters.next_token = next_token
85
96
  response = self.list(curr_filters)
86
97
  results.extend(response.results)
87
98
  is_last_page = response.next is None
88
- page += 1
89
- if max_pages and page > max_pages:
99
+ next_token = parse_qs(urlparse(response.next).query).get("next_token") if response.next else None
100
+ total_pages += 1
101
+ if max_pages and total_pages > max_pages:
90
102
  break
91
103
 
92
104
  return self.pagination_model(
93
- count=response.count, next=None, previous=None, results=results # No problem re-using last response count
105
+ next=None, previous=None, results=results # No problem re-using last response count
94
106
  )
95
107
 
96
- def __list_multithreaded(self, filters, max_pages: Optional[int] = None):
97
- results = []
98
-
99
- # Request the first page to compute number of pages
100
- curr_filters = self.filter_class(**filters.dict())
101
- curr_filters.page = "1"
102
- response = self.list(curr_filters)
103
- results.extend(response.results)
104
- count = response.count
105
- n_pages = ceil(response.count / 10)
106
- n_pages = max_pages if max_pages is not None and n_pages > max_pages else n_pages
107
-
108
- # If there isn't another page, just return current results
109
- if response.next is None:
110
- return response
111
-
112
- # Request other pages
113
- all_filters = [{**filters.dict(), "page": str(page)} for page in range(2, n_pages + 1)]
114
- all_filters = [self.filter_class(**filter) for filter in all_filters]
115
- with ThreadPoolExecutor(max_workers=self.nthreads) as executor:
116
- jobs = executor.map(self.list, all_filters)
117
- del all_filters
118
- [results.extend(job_result.results) for job_result in jobs]
119
- del jobs
120
-
121
- return self.pagination_model(count=count, next=None, previous=None, results=results)
122
-
123
108
  def list_all(self, filters, max_pages: Optional[int] = None):
124
- return (
125
- self.__list_sync(filters, max_pages)
126
- if self.nthreads is None
127
- else self.__list_multithreaded(filters, max_pages)
128
- )
109
+ return self.__list_sync(filters, max_pages)
@@ -0,0 +1,126 @@
1
+ [tool.poetry]
2
+ name = "cmsdials"
3
+ version = "1.0.0"
4
+ description = "The Python api client interface to DIALS service"
5
+ authors = ["Gabriel Moreira <gabrielmscampos@gmail.com>"]
6
+ readme = "README.md"
7
+ include = ["LICENSE"]
8
+ repository = "https://github.com/cms-DQM/dials-py"
9
+ classifiers = [
10
+ "Development Status :: 5 - Production/Stable",
11
+ "Programming Language :: Python",
12
+ "Operating System :: OS Independent",
13
+ "Topic :: Software Development :: Libraries :: Python Modules"
14
+ ]
15
+ packages = [
16
+ { include = "cmsdials" }
17
+ ]
18
+
19
+ [tool.poetry.dependencies]
20
+ python = "^3.9"
21
+ requests = "^2.31.0"
22
+ pydantic = "<2, >=1"
23
+ typing-extensions = "<4.6.0, >=3.6.6"
24
+
25
+ [tool.poetry.group.dev.dependencies]
26
+ pre-commit = "^3.6.2"
27
+ ruff = "^0.4.2"
28
+
29
+ [tool.ruff]
30
+ exclude = [
31
+ ".bzr",
32
+ ".direnv",
33
+ ".eggs",
34
+ ".git",
35
+ ".git-rewrite",
36
+ ".hg",
37
+ ".mypy_cache",
38
+ ".nox",
39
+ ".pants.d",
40
+ ".pytype",
41
+ ".ruff_cache",
42
+ ".svn",
43
+ ".tox",
44
+ ".venv",
45
+ "__pypackages__",
46
+ "_build",
47
+ "buck-out",
48
+ "build",
49
+ "dist",
50
+ "venv",
51
+ "virtualenvs",
52
+ ]
53
+ line-length = 120
54
+ indent-width = 4
55
+ target-version = "py310"
56
+
57
+ [tool.ruff.lint]
58
+ select = [
59
+ # pycodestyle
60
+ "E",
61
+ # Pyflakes
62
+ "F",
63
+ # pep8-naming
64
+ "N",
65
+ # pyupgrade
66
+ "UP",
67
+ # flake8-bugbear
68
+ "B",
69
+ # flake8-bandit
70
+ "S",
71
+ # flake8-blind-except
72
+ "BLE",
73
+ # flake8-builtins
74
+ "A",
75
+ # isort
76
+ "I",
77
+ # flake8-logging-format
78
+ "G",
79
+ # flake8-no-pep420
80
+ "INP",
81
+ # Ruff-specific rules
82
+ "RUF"
83
+ ]
84
+ ignore = [
85
+ # Disable eradicate (commented code removal)
86
+ "ERA001",
87
+ # Disable Conflicting lint rules,
88
+ # see https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
89
+ "W191",
90
+ "E501",
91
+ "E111",
92
+ "E117",
93
+ "D206",
94
+ "D300",
95
+ "Q000",
96
+ "Q001",
97
+ "Q002",
98
+ "Q003",
99
+ "COM812",
100
+ "COM819",
101
+ "ISC001",
102
+ "ISC002",
103
+ # Disable unused `noqa` directive
104
+ "RUF100",
105
+ ]
106
+ # Allow unused variables when underscore-prefixed:
107
+ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
108
+
109
+ [tool.ruff.lint.pycodestyle]
110
+ ignore-overlong-task-comments = true
111
+
112
+ [tool.ruff.lint.isort]
113
+ section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]
114
+ lines-after-imports = 2
115
+
116
+ [tool.ruff.lint.per-file-ignores]
117
+ # Ignore "E402", "F403", "F405" (import violations) in __init__.py files.
118
+ # Ignore "S" (flake8-bandit) and "N802" (function name should be lowercase) in tests and docs.
119
+ # Ignore "RUF" (Ruff-specific rules) and "I" (isort) in migrations.
120
+ "__init__.py" = ["E402", "F403", "F405"]
121
+ "**/{tests,docs}/*" = ["E402", "F403", "F405", "S", "N802"]
122
+ "**/*test*.py" = ["E402", "F403", "F405", "S", "N802"]
123
+
124
+ [build-system]
125
+ requires = ["poetry-core"]
126
+ build-backend = "poetry.core.masonry.api"
@@ -1,9 +0,0 @@
1
- from ...utils.api_client import BaseAuthorizedAPIClient
2
- from .models import BadFileIndex, BadFileIndexFilters, PaginatedBadFileIndexList
3
-
4
-
5
- class BadFileIndexClient(BaseAuthorizedAPIClient):
6
- data_model = BadFileIndex
7
- pagination_model = PaginatedBadFileIndexList
8
- filter_class = BadFileIndexFilters
9
- lookup_url = "bad-file-index/"
@@ -1,30 +0,0 @@
1
- from datetime import datetime
2
- from typing import List, Optional
3
-
4
- from pydantic import AnyUrl, BaseModel, Field
5
-
6
- from ...utils.base_model import OBaseModel
7
-
8
-
9
- class BadFileIndex(BaseModel):
10
- id: int
11
- file_path: str = Field(..., description="Path where the file is stored", max_length=255)
12
- data_era: str = Field(..., description="The era that the data refers to (e.g. 2018A)", max_length=7)
13
- st_size: float = Field(..., description="The data file's size in bytes")
14
- st_ctime: datetime = Field(..., description="Time of files's last status change in filesystem")
15
- st_itime: datetime = Field(..., description="Time when file was indexed in database")
16
- err: str = Field(..., description="Error message", max_length=255)
17
-
18
-
19
- class PaginatedBadFileIndexList(BaseModel):
20
- count: int
21
- next: Optional[AnyUrl]
22
- previous: Optional[AnyUrl]
23
- results: List[BadFileIndex]
24
-
25
-
26
- class BadFileIndexFilters(OBaseModel):
27
- era: Optional[str] = None
28
- min_size: Optional[float] = None
29
- page: Optional[str] = None
30
- path_contains: Optional[str] = None
@@ -1,44 +0,0 @@
1
- from datetime import datetime
2
- from typing import List, Optional
3
-
4
- from pydantic import AnyUrl, BaseModel, Field
5
-
6
- from ...utils.base_model import OBaseModel
7
-
8
-
9
- class FileIndex(BaseModel):
10
- id: int
11
- file_uuid: str = Field(..., description="ROOT TFile UUID", max_length=36)
12
- file_path: str = Field(..., description="Path where the file is stored", max_length=255)
13
- data_era: str = Field(..., description="The era that the data refers to (e.g. 2018A)", max_length=7)
14
- n_entries: int = Field(
15
- ...,
16
- description="Total number of entries contained in this histogram file",
17
- )
18
- n_entries_ingested: int = Field(
19
- ...,
20
- description="Number of histogram entries that have been extracted from the file",
21
- )
22
- st_size: float = Field(..., description="The data file's size in bytes")
23
- st_ctime: datetime = Field(..., description="Time of files's last status change in filesystem")
24
- st_itime: datetime = Field(..., description="Time when file was indexed in database")
25
- status: str = Field(
26
- ...,
27
- description="Indicate the processing status of run-histogram within the file",
28
- max_length=9,
29
- )
30
-
31
-
32
- class PaginatedFileIndexList(BaseModel):
33
- count: int
34
- next: Optional[AnyUrl]
35
- previous: Optional[AnyUrl]
36
- results: List[FileIndex]
37
-
38
-
39
- class FileIndexFilters(OBaseModel):
40
- era: Optional[str] = None
41
- min_size: Optional[float] = None
42
- page: Optional[str] = None
43
- path_contains: Optional[str] = None
44
- status: Optional[str] = None
@@ -1,44 +0,0 @@
1
- from datetime import datetime
2
- from typing import List, Optional
3
-
4
- from pydantic import AnyUrl, BaseModel, Field
5
-
6
- from ...utils.base_model import OBaseModel
7
-
8
-
9
- class LumisectionHistogram1D(BaseModel):
10
- id: int
11
- ls_number: int
12
- run_number: int
13
- date: datetime
14
- title: str = Field(..., max_length=220)
15
- entries: int
16
- data: list[float]
17
- x_min: float
18
- x_max: float
19
- x_bin: int
20
- source_data_file: int
21
- lumisection: int
22
-
23
-
24
- class PaginatedLumisectionHistogram1DList(BaseModel):
25
- count: int
26
- next: Optional[AnyUrl]
27
- previous: Optional[AnyUrl]
28
- results: List[LumisectionHistogram1D]
29
-
30
-
31
- class LumisectionHistogram1DFilters(OBaseModel):
32
- ls_number: Optional[int] = None
33
- lumisection_id: Optional[int] = None
34
- max_ls_number: Optional[int] = None
35
- max_run_number: Optional[int] = None
36
- min_entries: Optional[int] = None
37
- min_ls_number: Optional[int] = None
38
- min_run_number: Optional[int] = None
39
- page: Optional[str] = None
40
- run_number: Optional[int] = None
41
- title: Optional[str] = None
42
- title_contains: Optional[str] = None
43
- era: Optional[str] = None
44
- dqmio_filepath_contains: Optional[str] = None
@@ -1,50 +0,0 @@
1
- from datetime import datetime
2
- from typing import Optional
3
-
4
- from pydantic import AnyUrl, BaseModel, Field
5
-
6
- from ...utils.base_model import OBaseModel
7
-
8
-
9
- class LumisectionHistogram2D(BaseModel):
10
- id: int
11
- ls_number: int
12
- run_number: int
13
- date: datetime
14
- title: str = Field(..., max_length=220)
15
- entries: int
16
- data: list[list[float]]
17
- x_min: float
18
- x_max: float
19
- x_bin: int
20
- y_max: float
21
- y_min: float
22
- y_bin: int
23
- source_data_file: int = Field(
24
- ...,
25
- description="Source data file that the specific Histogram was read from, if any",
26
- )
27
- lumisection: int
28
-
29
-
30
- class PaginatedLumisectionHistogram2DList(BaseModel):
31
- count: int
32
- next: Optional[AnyUrl]
33
- previous: Optional[AnyUrl]
34
- results: list[LumisectionHistogram2D]
35
-
36
-
37
- class LumisectionHistogram2DFilters(OBaseModel):
38
- ls_number: Optional[int] = None
39
- lumisection_id: Optional[int] = None
40
- max_ls_number: Optional[int] = None
41
- max_run_number: Optional[int] = None
42
- min_entries: Optional[int] = None
43
- min_ls_number: Optional[int] = None
44
- min_run_number: Optional[int] = None
45
- page: Optional[str] = None
46
- run_number: Optional[int] = None
47
- title: Optional[str] = None
48
- title_contains: Optional[str] = None
49
- era: Optional[str] = None
50
- dqmio_filepath_contains: Optional[str] = None
@@ -1,20 +0,0 @@
1
- import requests
2
-
3
- from ...utils.api_client import BaseAuthorizedAPIClient
4
- from .models import ConfiguredMEs, Lumisection, LumisectionFilters, PaginatedLumisectionList
5
-
6
-
7
- class LumisectionClient(BaseAuthorizedAPIClient):
8
- data_model = Lumisection
9
- pagination_model = PaginatedLumisectionList
10
- filter_class = LumisectionFilters
11
- lookup_url = "lumisection/"
12
-
13
- def configured_mes(self):
14
- endpoint_url = self.api_url + self.lookup_url + "configured-mes/"
15
- headers = {"accept": "application/json"}
16
- self.creds.before_request(headers)
17
- response = requests.get(endpoint_url, headers=headers)
18
- response.raise_for_status()
19
- response = response.json()
20
- return ConfiguredMEs(**response)
@@ -1,35 +0,0 @@
1
- from datetime import datetime
2
- from typing import List, Optional
3
-
4
- from pydantic import AnyUrl, BaseModel
5
-
6
- from ...utils.base_model import OBaseModel
7
-
8
-
9
- class Lumisection(BaseModel):
10
- id: int
11
- ls_number: int
12
- date: datetime
13
- oms_zerobias_rate: Optional[float]
14
- run: int
15
-
16
-
17
- class PaginatedLumisectionList(BaseModel):
18
- count: int
19
- next: Optional[AnyUrl]
20
- previous: Optional[AnyUrl]
21
- results: list[Lumisection]
22
-
23
-
24
- class LumisectionFilters(OBaseModel):
25
- ls_number: Optional[int] = None
26
- max_ls_number: Optional[int] = None
27
- max_run_number: Optional[int] = None
28
- min_ls_number: Optional[int] = None
29
- min_run_number: Optional[int] = None
30
- page: Optional[str] = None
31
- run_number: Optional[int] = None
32
-
33
-
34
- class ConfiguredMEs(BaseModel):
35
- mes: List[str]
@@ -1,9 +0,0 @@
1
- from ...utils.api_client import BaseAuthorizedAPIClient
2
- from .models import PaginatedRunList, Run, RunFilters
3
-
4
-
5
- class RunClient(BaseAuthorizedAPIClient):
6
- data_model = Run
7
- pagination_model = PaginatedRunList
8
- filter_class = RunFilters
9
- lookup_url = "run/"
@@ -1,31 +0,0 @@
1
- from datetime import datetime
2
- from typing import Optional
3
-
4
- from pydantic import AnyUrl, BaseModel, Field
5
-
6
- from ...utils.base_model import OBaseModel
7
-
8
-
9
- class Run(BaseModel):
10
- run_number: int
11
- run_date: Optional[datetime]
12
- year: Optional[int]
13
- period: Optional[str] = Field(..., max_length=1)
14
- date: datetime
15
- oms_fill: Optional[int]
16
- oms_lumisections: Optional[int]
17
- oms_initial_lumi: Optional[float]
18
- oms_end_lumi: Optional[float]
19
-
20
-
21
- class PaginatedRunList(BaseModel):
22
- count: int
23
- next: Optional[AnyUrl]
24
- previous: Optional[AnyUrl]
25
- results: list[Run]
26
-
27
-
28
- class RunFilters(OBaseModel):
29
- max_run_number: Optional[int] = None
30
- min_run_number: Optional[int] = None
31
- page: Optional[str] = None
@@ -1,19 +0,0 @@
1
- from typing import Optional
2
-
3
- from .auth._base import BaseCredentials
4
- from .clients.bad_file_index.client import BadFileIndexClient
5
- from .clients.file_index.client import FileIndexClient
6
- from .clients.h1d.client import LumisectionHistogram1DClient
7
- from .clients.h2d.client import LumisectionHistogram2DClient
8
- from .clients.lumisection.client import LumisectionClient
9
- from .clients.run.client import RunClient
10
-
11
-
12
- class Dials:
13
- def __init__(self, creds: BaseCredentials, nthreads: Optional[int] = None, *args, **kwargs) -> None:
14
- self.bad_file_index = BadFileIndexClient(creds, nthreads, *args, **kwargs)
15
- self.file_index = FileIndexClient(creds, nthreads, *args, **kwargs)
16
- self.h1d = LumisectionHistogram1DClient(creds, nthreads, *args, **kwargs)
17
- self.h2d = LumisectionHistogram2DClient(creds, nthreads, *args, **kwargs)
18
- self.lumi = LumisectionClient(creds, nthreads, *args, **kwargs)
19
- self.run = RunClient(creds, nthreads, *args, **kwargs)
File without changes
@@ -1,31 +0,0 @@
1
- [tool.poetry]
2
- name = "cmsdials"
3
- version = "0.3.0"
4
- description = "The Python api client interface to DIALS service"
5
- authors = ["Gabriel Moreira <gabrielmscampos@gmail.com>"]
6
- readme = "README.md"
7
- include = ["LICENSE"]
8
- repository = "https://github.com/cms-DQM/dials-py"
9
- classifiers = [
10
- "Development Status :: 4 - Beta",
11
- "Programming Language :: Python",
12
- "Operating System :: OS Independent",
13
- "Topic :: Software Development :: Libraries :: Python Modules"
14
- ]
15
- packages = [
16
- { include = "cmsdials" }
17
- ]
18
-
19
- [tool.poetry.dependencies]
20
- python = "^3.9"
21
- requests = "^2.31.0"
22
- pydantic = "<2, >=1"
23
- typing-extensions = "<4.6.0, >=3.6.6"
24
-
25
-
26
- [tool.poetry.group.dev.dependencies]
27
- pre-commit = "^3.6.2"
28
-
29
- [build-system]
30
- requires = ["poetry-core"]
31
- build-backend = "poetry.core.masonry.api"
File without changes
File without changes