digitalhub 0.8.0b0__py3-none-any.whl → 0.8.0b1__py3-none-any.whl
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.
Potentially problematic release.
This version of digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +62 -94
- digitalhub/client/__init__.py +0 -0
- digitalhub/client/builder.py +105 -0
- digitalhub/client/objects/__init__.py +0 -0
- digitalhub/client/objects/base.py +56 -0
- digitalhub/client/objects/dhcore.py +681 -0
- digitalhub/client/objects/local.py +533 -0
- digitalhub/context/__init__.py +0 -0
- digitalhub/context/builder.py +178 -0
- digitalhub/context/context.py +136 -0
- digitalhub/datastores/__init__.py +0 -0
- digitalhub/datastores/builder.py +134 -0
- digitalhub/datastores/objects/__init__.py +0 -0
- digitalhub/datastores/objects/base.py +85 -0
- digitalhub/datastores/objects/local.py +42 -0
- digitalhub/datastores/objects/remote.py +23 -0
- digitalhub/datastores/objects/s3.py +38 -0
- digitalhub/datastores/objects/sql.py +60 -0
- digitalhub/entities/__init__.py +0 -0
- digitalhub/entities/_base/__init__.py +0 -0
- digitalhub/entities/_base/api.py +346 -0
- digitalhub/entities/_base/base.py +82 -0
- digitalhub/entities/_base/crud.py +610 -0
- digitalhub/entities/_base/entity/__init__.py +0 -0
- digitalhub/entities/_base/entity/base.py +132 -0
- digitalhub/entities/_base/entity/context.py +118 -0
- digitalhub/entities/_base/entity/executable.py +380 -0
- digitalhub/entities/_base/entity/material.py +214 -0
- digitalhub/entities/_base/entity/unversioned.py +87 -0
- digitalhub/entities/_base/entity/versioned.py +94 -0
- digitalhub/entities/_base/metadata.py +59 -0
- digitalhub/entities/_base/spec/__init__.py +0 -0
- digitalhub/entities/_base/spec/base.py +58 -0
- digitalhub/entities/_base/spec/material.py +22 -0
- digitalhub/entities/_base/state.py +31 -0
- digitalhub/entities/_base/status/__init__.py +0 -0
- digitalhub/entities/_base/status/base.py +32 -0
- digitalhub/entities/_base/status/material.py +49 -0
- digitalhub/entities/_builders/__init__.py +0 -0
- digitalhub/entities/_builders/metadata.py +60 -0
- digitalhub/entities/_builders/name.py +31 -0
- digitalhub/entities/_builders/spec.py +43 -0
- digitalhub/entities/_builders/status.py +62 -0
- digitalhub/entities/_builders/uuid.py +33 -0
- digitalhub/entities/artifact/__init__.py +0 -0
- digitalhub/entities/artifact/builder.py +133 -0
- digitalhub/entities/artifact/crud.py +358 -0
- digitalhub/entities/artifact/entity/__init__.py +0 -0
- digitalhub/entities/artifact/entity/_base.py +39 -0
- digitalhub/entities/artifact/entity/artifact.py +9 -0
- digitalhub/entities/artifact/spec.py +39 -0
- digitalhub/entities/artifact/status.py +15 -0
- digitalhub/entities/dataitem/__init__.py +0 -0
- digitalhub/entities/dataitem/builder.py +144 -0
- digitalhub/entities/dataitem/crud.py +395 -0
- digitalhub/entities/dataitem/entity/__init__.py +0 -0
- digitalhub/entities/dataitem/entity/_base.py +75 -0
- digitalhub/entities/dataitem/entity/dataitem.py +9 -0
- digitalhub/entities/dataitem/entity/iceberg.py +7 -0
- digitalhub/entities/dataitem/entity/table.py +125 -0
- digitalhub/entities/dataitem/models.py +62 -0
- digitalhub/entities/dataitem/spec.py +61 -0
- digitalhub/entities/dataitem/status.py +38 -0
- digitalhub/entities/entity_types.py +19 -0
- digitalhub/entities/function/__init__.py +0 -0
- digitalhub/entities/function/builder.py +86 -0
- digitalhub/entities/function/crud.py +305 -0
- digitalhub/entities/function/entity.py +101 -0
- digitalhub/entities/function/models.py +118 -0
- digitalhub/entities/function/spec.py +81 -0
- digitalhub/entities/function/status.py +9 -0
- digitalhub/entities/model/__init__.py +0 -0
- digitalhub/entities/model/builder.py +152 -0
- digitalhub/entities/model/crud.py +358 -0
- digitalhub/entities/model/entity/__init__.py +0 -0
- digitalhub/entities/model/entity/_base.py +34 -0
- digitalhub/entities/model/entity/huggingface.py +9 -0
- digitalhub/entities/model/entity/mlflow.py +90 -0
- digitalhub/entities/model/entity/model.py +9 -0
- digitalhub/entities/model/entity/sklearn.py +9 -0
- digitalhub/entities/model/models.py +26 -0
- digitalhub/entities/model/spec.py +146 -0
- digitalhub/entities/model/status.py +33 -0
- digitalhub/entities/project/__init__.py +0 -0
- digitalhub/entities/project/builder.py +82 -0
- digitalhub/entities/project/crud.py +350 -0
- digitalhub/entities/project/entity.py +2060 -0
- digitalhub/entities/project/spec.py +50 -0
- digitalhub/entities/project/status.py +9 -0
- digitalhub/entities/registries.py +48 -0
- digitalhub/entities/run/__init__.py +0 -0
- digitalhub/entities/run/builder.py +77 -0
- digitalhub/entities/run/crud.py +232 -0
- digitalhub/entities/run/entity.py +461 -0
- digitalhub/entities/run/spec.py +153 -0
- digitalhub/entities/run/status.py +114 -0
- digitalhub/entities/secret/__init__.py +0 -0
- digitalhub/entities/secret/builder.py +93 -0
- digitalhub/entities/secret/crud.py +294 -0
- digitalhub/entities/secret/entity.py +73 -0
- digitalhub/entities/secret/spec.py +35 -0
- digitalhub/entities/secret/status.py +9 -0
- digitalhub/entities/task/__init__.py +0 -0
- digitalhub/entities/task/builder.py +74 -0
- digitalhub/entities/task/crud.py +241 -0
- digitalhub/entities/task/entity.py +135 -0
- digitalhub/entities/task/models.py +199 -0
- digitalhub/entities/task/spec.py +51 -0
- digitalhub/entities/task/status.py +9 -0
- digitalhub/entities/utils.py +184 -0
- digitalhub/entities/workflow/__init__.py +0 -0
- digitalhub/entities/workflow/builder.py +91 -0
- digitalhub/entities/workflow/crud.py +304 -0
- digitalhub/entities/workflow/entity.py +77 -0
- digitalhub/entities/workflow/spec.py +15 -0
- digitalhub/entities/workflow/status.py +9 -0
- digitalhub/readers/__init__.py +0 -0
- digitalhub/readers/builder.py +54 -0
- digitalhub/readers/objects/__init__.py +0 -0
- digitalhub/readers/objects/base.py +70 -0
- digitalhub/readers/objects/pandas.py +207 -0
- digitalhub/readers/registry.py +15 -0
- digitalhub/registry/__init__.py +0 -0
- digitalhub/registry/models.py +87 -0
- digitalhub/registry/registry.py +74 -0
- digitalhub/registry/utils.py +150 -0
- digitalhub/runtimes/__init__.py +0 -0
- digitalhub/runtimes/base.py +164 -0
- digitalhub/runtimes/builder.py +53 -0
- digitalhub/runtimes/kind_registry.py +170 -0
- digitalhub/stores/__init__.py +0 -0
- digitalhub/stores/builder.py +257 -0
- digitalhub/stores/objects/__init__.py +0 -0
- digitalhub/stores/objects/base.py +189 -0
- digitalhub/stores/objects/local.py +230 -0
- digitalhub/stores/objects/remote.py +143 -0
- digitalhub/stores/objects/s3.py +563 -0
- digitalhub/stores/objects/sql.py +328 -0
- digitalhub/utils/__init__.py +0 -0
- digitalhub/utils/data_utils.py +127 -0
- digitalhub/utils/env_utils.py +123 -0
- digitalhub/utils/exceptions.py +55 -0
- digitalhub/utils/file_utils.py +204 -0
- digitalhub/utils/generic_utils.py +207 -0
- digitalhub/utils/git_utils.py +148 -0
- digitalhub/utils/io_utils.py +79 -0
- digitalhub/utils/logger.py +17 -0
- digitalhub/utils/uri_utils.py +56 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/METADATA +27 -12
- digitalhub-0.8.0b1.dist-info/RECORD +161 -0
- test/test_crud_artifacts.py +1 -1
- test/test_crud_dataitems.py +1 -1
- test/test_crud_functions.py +1 -1
- test/test_crud_runs.py +1 -1
- test/test_crud_tasks.py +1 -1
- digitalhub-0.8.0b0.dist-info/RECORD +0 -14
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/LICENSE.txt +0 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/WHEEL +0 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib.util as imputil
|
|
4
|
+
import typing
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from digitalhub.client.builder import build_client, get_client
|
|
8
|
+
from digitalhub.context.builder import delete_context
|
|
9
|
+
from digitalhub.entities._base.crud import delete_entity_api_base, read_entity_api_base, update_entity_api_base
|
|
10
|
+
from digitalhub.entities.entity_types import EntityTypes
|
|
11
|
+
from digitalhub.entities.project.builder import project_from_dict, project_from_parameters
|
|
12
|
+
from digitalhub.utils.exceptions import BackendError, EntityError
|
|
13
|
+
from digitalhub.utils.io_utils import read_yaml
|
|
14
|
+
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
from digitalhub.entities.project.entity import Project
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
ENTITY_TYPE = EntityTypes.PROJECT.value
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def new_project(
|
|
23
|
+
name: str,
|
|
24
|
+
description: str | None = None,
|
|
25
|
+
labels: list[str] | None = None,
|
|
26
|
+
local: bool = False,
|
|
27
|
+
config: dict | None = None,
|
|
28
|
+
context: str | None = None,
|
|
29
|
+
setup_kwargs: dict | None = None,
|
|
30
|
+
**kwargs,
|
|
31
|
+
) -> Project:
|
|
32
|
+
"""
|
|
33
|
+
Create a new object.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
name : str
|
|
38
|
+
Object name.
|
|
39
|
+
description : str
|
|
40
|
+
Description of the object (human readable).
|
|
41
|
+
labels : list[str]
|
|
42
|
+
List of labels.
|
|
43
|
+
local : bool
|
|
44
|
+
If True, use local backend, if False use DHCore backend. Default to False.
|
|
45
|
+
config : dict
|
|
46
|
+
DHCore environment configuration.
|
|
47
|
+
context : str
|
|
48
|
+
The context local folder of the project.
|
|
49
|
+
setup_kwargs : dict
|
|
50
|
+
Setup keyword arguments passed to setup_project() function.
|
|
51
|
+
**kwargs : dict
|
|
52
|
+
Keyword arguments.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
Project
|
|
57
|
+
Object instance.
|
|
58
|
+
|
|
59
|
+
Examples
|
|
60
|
+
--------
|
|
61
|
+
>>> obj = new_project("my-project")
|
|
62
|
+
"""
|
|
63
|
+
build_client(local, config)
|
|
64
|
+
if context is None:
|
|
65
|
+
context = name
|
|
66
|
+
obj = project_from_parameters(
|
|
67
|
+
name=name,
|
|
68
|
+
kind="project",
|
|
69
|
+
description=description,
|
|
70
|
+
labels=labels,
|
|
71
|
+
local=local,
|
|
72
|
+
context=context,
|
|
73
|
+
**kwargs,
|
|
74
|
+
)
|
|
75
|
+
obj.save()
|
|
76
|
+
return _setup_project(obj, setup_kwargs)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_project(
|
|
80
|
+
name: str,
|
|
81
|
+
local: bool = False,
|
|
82
|
+
config: dict | None = None,
|
|
83
|
+
setup_kwargs: dict | None = None,
|
|
84
|
+
**kwargs,
|
|
85
|
+
) -> Project:
|
|
86
|
+
"""
|
|
87
|
+
Retrieves project details from backend.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
name : str
|
|
92
|
+
The Project name.
|
|
93
|
+
local : bool
|
|
94
|
+
Flag to determine if backend is local.
|
|
95
|
+
config : dict
|
|
96
|
+
DHCore environment configuration.
|
|
97
|
+
setup_kwargs : dict
|
|
98
|
+
Setup keyword arguments passed to setup_project() function.
|
|
99
|
+
**kwargs : dict
|
|
100
|
+
Parameters to pass to the API call.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
Project
|
|
105
|
+
Object instance.
|
|
106
|
+
|
|
107
|
+
Examples
|
|
108
|
+
--------
|
|
109
|
+
>>> obj = get_project("my-project")
|
|
110
|
+
"""
|
|
111
|
+
build_client(local, config)
|
|
112
|
+
client = get_client(local)
|
|
113
|
+
obj = read_entity_api_base(client, ENTITY_TYPE, name, **kwargs)
|
|
114
|
+
obj["local"] = local
|
|
115
|
+
project = project_from_dict(obj)
|
|
116
|
+
return _setup_project(project, setup_kwargs)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def import_project(
|
|
120
|
+
file: str,
|
|
121
|
+
local: bool = False,
|
|
122
|
+
config: dict | None = None,
|
|
123
|
+
setup_kwargs: dict | None = None,
|
|
124
|
+
) -> Project:
|
|
125
|
+
"""
|
|
126
|
+
Import object from a YAML file.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
file : str
|
|
131
|
+
Path to YAML file.
|
|
132
|
+
local : bool
|
|
133
|
+
Flag to determine if backend is local.
|
|
134
|
+
config : dict
|
|
135
|
+
DHCore environment configuration.
|
|
136
|
+
setup_kwargs : dict
|
|
137
|
+
Setup keyword arguments passed to setup_project() function.
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
Project
|
|
142
|
+
Object instance.
|
|
143
|
+
|
|
144
|
+
Examples
|
|
145
|
+
--------
|
|
146
|
+
>>> obj = import_project("my-project.yaml")
|
|
147
|
+
"""
|
|
148
|
+
build_client(local, config)
|
|
149
|
+
dict_obj: dict = read_yaml(file)
|
|
150
|
+
dict_obj["local"] = local
|
|
151
|
+
obj = project_from_dict(dict_obj)
|
|
152
|
+
obj = _setup_project(obj, setup_kwargs)
|
|
153
|
+
|
|
154
|
+
# Import related entities
|
|
155
|
+
obj._import_entities()
|
|
156
|
+
|
|
157
|
+
return obj
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def load_project(
|
|
161
|
+
name: str | None = None,
|
|
162
|
+
filename: str | None = None,
|
|
163
|
+
local: bool = False,
|
|
164
|
+
config: dict | None = None,
|
|
165
|
+
setup_kwargs: dict | None = None,
|
|
166
|
+
**kwargs,
|
|
167
|
+
) -> Project:
|
|
168
|
+
"""
|
|
169
|
+
Load project and context from backend or file. Name or
|
|
170
|
+
filename must be provided. Name takes precedence over filename.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
name : str
|
|
175
|
+
Project name.
|
|
176
|
+
filename : str
|
|
177
|
+
Path to YAML file.
|
|
178
|
+
local : bool
|
|
179
|
+
Flag to determine if backend is local.
|
|
180
|
+
config : dict
|
|
181
|
+
DHCore environment configuration.
|
|
182
|
+
setup_kwargs : dict
|
|
183
|
+
Setup keyword arguments passed to setup_project() function.
|
|
184
|
+
**kwargs : dict
|
|
185
|
+
Keyword arguments.
|
|
186
|
+
|
|
187
|
+
Returns
|
|
188
|
+
-------
|
|
189
|
+
Project
|
|
190
|
+
Object instance.
|
|
191
|
+
|
|
192
|
+
Examples
|
|
193
|
+
--------
|
|
194
|
+
If name is provided, load project from backend.
|
|
195
|
+
>>> obj = load_project(name="my-project")
|
|
196
|
+
|
|
197
|
+
If filename is provided, load project from file.
|
|
198
|
+
>>> obj = load_project(filename="my-project.yaml")
|
|
199
|
+
"""
|
|
200
|
+
if name is not None:
|
|
201
|
+
return get_project(name=name, local=local, config=config, setup_kwargs=setup_kwargs, **kwargs)
|
|
202
|
+
if filename is not None:
|
|
203
|
+
return import_project(filename, local=local, config=config, setup_kwargs=setup_kwargs)
|
|
204
|
+
raise EntityError("Either name or filename must be provided.")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def get_or_create_project(
|
|
208
|
+
name: str,
|
|
209
|
+
local: bool = False,
|
|
210
|
+
config: dict | None = None,
|
|
211
|
+
context: str | None = None,
|
|
212
|
+
setup_kwargs: dict | None = None,
|
|
213
|
+
**kwargs,
|
|
214
|
+
) -> Project:
|
|
215
|
+
"""
|
|
216
|
+
Try to get project. If not exists, create it.
|
|
217
|
+
|
|
218
|
+
Parameters
|
|
219
|
+
----------
|
|
220
|
+
name : str
|
|
221
|
+
Project name.
|
|
222
|
+
local : bool
|
|
223
|
+
Flag to determine if backend is local.
|
|
224
|
+
config : dict
|
|
225
|
+
DHCore environment configuration.
|
|
226
|
+
context : str
|
|
227
|
+
Folder where the project will saves its context locally.
|
|
228
|
+
setup_kwargs : dict
|
|
229
|
+
Setup keyword arguments passed to setup_project() function.
|
|
230
|
+
**kwargs : dict
|
|
231
|
+
Keyword arguments.
|
|
232
|
+
|
|
233
|
+
Returns
|
|
234
|
+
-------
|
|
235
|
+
Project
|
|
236
|
+
Object instance.
|
|
237
|
+
"""
|
|
238
|
+
try:
|
|
239
|
+
return get_project(
|
|
240
|
+
name,
|
|
241
|
+
local=local,
|
|
242
|
+
config=config,
|
|
243
|
+
setup_kwargs=setup_kwargs,
|
|
244
|
+
**kwargs,
|
|
245
|
+
)
|
|
246
|
+
except BackendError:
|
|
247
|
+
return new_project(
|
|
248
|
+
name,
|
|
249
|
+
local=local,
|
|
250
|
+
config=config,
|
|
251
|
+
setup_kwargs=setup_kwargs,
|
|
252
|
+
context=context,
|
|
253
|
+
**kwargs,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def update_project(entity: Project, local: bool = False, **kwargs) -> Project:
|
|
258
|
+
"""
|
|
259
|
+
Update object. Note that object spec are immutable.
|
|
260
|
+
|
|
261
|
+
Parameters
|
|
262
|
+
----------
|
|
263
|
+
entity : Project
|
|
264
|
+
Object to update.
|
|
265
|
+
local : bool
|
|
266
|
+
Flag to determine if backend is local.
|
|
267
|
+
**kwargs : dict
|
|
268
|
+
Parameters to pass to the API call.
|
|
269
|
+
|
|
270
|
+
Returns
|
|
271
|
+
-------
|
|
272
|
+
Project
|
|
273
|
+
The updated object.
|
|
274
|
+
|
|
275
|
+
Examples
|
|
276
|
+
--------
|
|
277
|
+
>>> obj = update_project(obj)
|
|
278
|
+
"""
|
|
279
|
+
client = get_client(local)
|
|
280
|
+
obj = update_entity_api_base(client, ENTITY_TYPE, entity.name, entity.to_dict(), **kwargs)
|
|
281
|
+
return project_from_dict(obj)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def delete_project(
|
|
285
|
+
name: str,
|
|
286
|
+
cascade: bool = True,
|
|
287
|
+
clean_context: bool = True,
|
|
288
|
+
local: bool = False,
|
|
289
|
+
**kwargs,
|
|
290
|
+
) -> list[dict]:
|
|
291
|
+
"""
|
|
292
|
+
Delete a project.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
name : str
|
|
297
|
+
Project name.
|
|
298
|
+
cascade : bool
|
|
299
|
+
Flag to determine if delete is cascading.
|
|
300
|
+
clean_context : bool
|
|
301
|
+
Flag to determine if context will be deleted. If a context is deleted,
|
|
302
|
+
all its objects are unreacheable.
|
|
303
|
+
local : bool
|
|
304
|
+
Flag to determine if backend is local.
|
|
305
|
+
**kwargs : dict
|
|
306
|
+
Parameters to pass to the API call.
|
|
307
|
+
|
|
308
|
+
Returns
|
|
309
|
+
-------
|
|
310
|
+
dict
|
|
311
|
+
Response from backend.
|
|
312
|
+
|
|
313
|
+
Examples
|
|
314
|
+
--------
|
|
315
|
+
>>> delete_project("my-project")
|
|
316
|
+
"""
|
|
317
|
+
client = get_client(local)
|
|
318
|
+
obj = delete_entity_api_base(client, ENTITY_TYPE, name, cascade=cascade, **kwargs)
|
|
319
|
+
if clean_context:
|
|
320
|
+
delete_context(name)
|
|
321
|
+
return obj
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _setup_project(project: Project, setup_kwargs: dict | None = None) -> Project:
|
|
325
|
+
"""
|
|
326
|
+
Search for setup_project.py file and launch setup hanlder as project hook.
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
project : Project
|
|
331
|
+
The project to scafold.
|
|
332
|
+
setup_kwargs : dict
|
|
333
|
+
Arguments to pass to setup handler.
|
|
334
|
+
|
|
335
|
+
Returns
|
|
336
|
+
-------
|
|
337
|
+
Project
|
|
338
|
+
Set up project.
|
|
339
|
+
"""
|
|
340
|
+
setup_kwargs = setup_kwargs if setup_kwargs is not None else {}
|
|
341
|
+
check_pth = Path(project.spec.context, ".CHECK")
|
|
342
|
+
setup_pth = Path(project.spec.context, "setup_project.py")
|
|
343
|
+
if setup_pth.exists() and not check_pth.exists():
|
|
344
|
+
spec = imputil.spec_from_file_location("setup_project", setup_pth)
|
|
345
|
+
mod = imputil.module_from_spec(spec)
|
|
346
|
+
spec.loader.exec_module(mod)
|
|
347
|
+
handler = getattr(mod, "setup")
|
|
348
|
+
project = handler(project, **setup_kwargs)
|
|
349
|
+
check_pth.touch()
|
|
350
|
+
return project
|