fair-cli 0.9.7__tar.gz → 0.9.9__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.
- {fair_cli-0.9.7 → fair_cli-0.9.9}/CITATION.cff +1 -1
- {fair_cli-0.9.7 → fair_cli-0.9.9}/PKG-INFO +6 -4
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/cli.py +1 -1
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/common.py +4 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/identifiers.py +13 -5
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/logging.py +1 -1
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/registry/requests.py +2 -2
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/registry/sync.py +8 -7
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/session.py +12 -15
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/staging.py +5 -3
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/user_config/__init__.py +10 -4
- {fair_cli-0.9.7 → fair_cli-0.9.9}/pyproject.toml +2 -3
- {fair_cli-0.9.7 → fair_cli-0.9.9}/CHANGELOG.md +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/LICENSE +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/README.md +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/__init__.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/configuration/__init__.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/configuration/validation.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/exceptions.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/files.txt +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/history.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/register.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/registry/__init__.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/registry/file_formats.json +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/registry/file_types.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/registry/server.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/registry/storage.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/registry/versioning.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/run.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/templates/__init__.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/templates/config.jinja +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/templates/hist.jinja +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/testing.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/user_config/globbing.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/user_config/validation.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/utilities.py +0 -0
- {fair_cli-0.9.7 → fair_cli-0.9.9}/fair/virtualenv.py +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: fair-cli
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.9
|
|
4
4
|
Summary: Synchronization interface for the SCRC FAIR Data Pipeline registry
|
|
5
5
|
License: BSD-2-Clause
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Keywords: FAIR Data Pipeline,FAIR,Data Management,Provenance
|
|
7
8
|
Author: Richard Reeve
|
|
8
9
|
Author-email: richard.reeve@glasgow.ac.uk
|
|
9
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10.0,<4.0.0
|
|
10
11
|
Classifier: Development Status :: 4 - Beta
|
|
11
12
|
Classifier: Environment :: Console
|
|
12
13
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -15,11 +16,12 @@ Classifier: Operating System :: MacOS
|
|
|
15
16
|
Classifier: Operating System :: OS Independent
|
|
16
17
|
Classifier: Operating System :: POSIX :: Linux
|
|
17
18
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
25
|
Classifier: Topic :: Database :: Front-Ends
|
|
24
26
|
Classifier: Topic :: Scientific/Engineering
|
|
25
27
|
Provides-Extra: all
|
|
@@ -347,7 +347,7 @@ def purge(glob: bool, debug: bool, yes: bool, data: bool, all: bool) -> None:
|
|
|
347
347
|
)
|
|
348
348
|
else:
|
|
349
349
|
_purge = click.confirm(
|
|
350
|
-
"Are you sure you want to reset FAIR tracking,
|
|
350
|
+
"Are you sure you want to reset FAIR tracking, this is not reversible?"
|
|
351
351
|
)
|
|
352
352
|
if data:
|
|
353
353
|
data = click.confirm(
|
|
@@ -308,7 +308,11 @@ def set_file_permissions(path: str):
|
|
|
308
308
|
|
|
309
309
|
def remove_readonly(fn, path, excinfo):
|
|
310
310
|
try:
|
|
311
|
+
_ = excinfo # Required to avoid unused variable warning
|
|
312
|
+
# Remove readonly bit
|
|
311
313
|
os.chmod(path, stat.S_IWRITE)
|
|
314
|
+
# Call the provided function again
|
|
312
315
|
fn(path)
|
|
313
316
|
except Exception as exc:
|
|
317
|
+
# Log the error
|
|
314
318
|
print("Skipped:", path, "because:\n", exc)
|
|
@@ -30,6 +30,9 @@ from fake_useragent import UserAgent
|
|
|
30
30
|
|
|
31
31
|
logger = logging.getLogger("FAIRDataPipeline.Identifiers")
|
|
32
32
|
|
|
33
|
+
JSON_MIME_TYPE = "application/json"
|
|
34
|
+
JSON_HEADERS = {"Accept": JSON_MIME_TYPE}
|
|
35
|
+
|
|
33
36
|
ID_URIS = {
|
|
34
37
|
"orcid": "https://orcid.org/",
|
|
35
38
|
"ror": "https://ror.org/",
|
|
@@ -57,7 +60,7 @@ def check_orcid(orcid: str) -> typing.Dict:
|
|
|
57
60
|
metadata from the given ID
|
|
58
61
|
"""
|
|
59
62
|
orcid = orcid.replace(ID_URIS["orcid"], "")
|
|
60
|
-
_header =
|
|
63
|
+
_header = JSON_HEADERS
|
|
61
64
|
_url = urllib.parse.urljoin(QUERY_URLS["orcid"], orcid)
|
|
62
65
|
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
|
63
66
|
_response = requests.get(_url, headers=_header, verify=False, allow_redirects=True)
|
|
@@ -95,7 +98,7 @@ def check_github(github: str) -> typing.Dict:
|
|
|
95
98
|
typing.Dict
|
|
96
99
|
metadata from the given ID
|
|
97
100
|
"""
|
|
98
|
-
_header =
|
|
101
|
+
_header = JSON_HEADERS
|
|
99
102
|
_url = urllib.parse.urljoin(QUERY_URLS["github"], github)
|
|
100
103
|
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
|
101
104
|
_response = requests.get(_url, headers=_header, verify=False, allow_redirects=True)
|
|
@@ -104,7 +107,7 @@ def check_github(github: str) -> typing.Dict:
|
|
|
104
107
|
|
|
105
108
|
if _response.status_code == 403:
|
|
106
109
|
time.sleep(3)
|
|
107
|
-
_header = {"Accept":
|
|
110
|
+
_header = {"Accept": JSON_MIME_TYPE, "User-Agent": str(UserAgent().chrome)}
|
|
108
111
|
_response = requests.get(
|
|
109
112
|
_url, headers=_header, verify=False, allow_redirects=True
|
|
110
113
|
)
|
|
@@ -149,7 +152,7 @@ def check_gitlab(gitlab: str, gitlab_url: str = "https://gitlab.com/") -> typing
|
|
|
149
152
|
|
|
150
153
|
if _response.status_code == 403:
|
|
151
154
|
time.sleep(3)
|
|
152
|
-
_header = {"Accept":
|
|
155
|
+
_header = {"Accept": JSON_MIME_TYPE, "User-Agent": str(UserAgent().chrome)}
|
|
153
156
|
_response = requests.get(
|
|
154
157
|
_url, headers=_header, verify=False, allow_redirects=True
|
|
155
158
|
)
|
|
@@ -231,7 +234,12 @@ def _check_generic_ror(id: str) -> typing.Dict:
|
|
|
231
234
|
return _result_dict
|
|
232
235
|
|
|
233
236
|
_id = _response.json()["items"][0]["id"]
|
|
234
|
-
|
|
237
|
+
# The ROR API lists every name of an organisation, marking the one to show as "ror_display"
|
|
238
|
+
_names = _response.json()["items"][0]["names"]
|
|
239
|
+
_name = next(
|
|
240
|
+
(n["value"] for n in _names if "ror_display" in n["types"]),
|
|
241
|
+
_names[0]["value"],
|
|
242
|
+
)
|
|
235
243
|
_result_dict["name"] = _name
|
|
236
244
|
_result_dict["family_name"] = _name
|
|
237
245
|
_result_dict["given_names"] = None
|
|
@@ -99,5 +99,5 @@ class LevelFormatter(logging.Formatter):
|
|
|
99
99
|
|
|
100
100
|
def format(self, record: logging.LogRecord) -> str:
|
|
101
101
|
idx = bisect(self.formats, (record.levelno,), hi=len(self.formats) - 1)
|
|
102
|
-
|
|
102
|
+
_, formatter = self.formats[idx] # Tuple unpacking level, then formatter
|
|
103
103
|
return formatter.format(record)
|
|
@@ -632,7 +632,7 @@ def get_author_exists(registry_uri, token=None, name=None, identifier=None) -> s
|
|
|
632
632
|
The url of the author if they exist else None
|
|
633
633
|
"""
|
|
634
634
|
if not name and not identifier:
|
|
635
|
-
return
|
|
635
|
+
return ""
|
|
636
636
|
if not token:
|
|
637
637
|
token = local_token()
|
|
638
638
|
_params = {}
|
|
@@ -643,7 +643,7 @@ def get_author_exists(registry_uri, token=None, name=None, identifier=None) -> s
|
|
|
643
643
|
_author_exists = get(registry_uri, "author", token, params=_params)
|
|
644
644
|
if _author_exists:
|
|
645
645
|
return _author_exists[0]["url"]
|
|
646
|
-
return
|
|
646
|
+
return ""
|
|
647
647
|
|
|
648
648
|
|
|
649
649
|
def get_auth_provider(registry_uri: str, token: str = None) -> str:
|
|
@@ -350,13 +350,12 @@ def _get_new_url(
|
|
|
350
350
|
_new_obj_data["authors"].append(_new_author_url)
|
|
351
351
|
_filters["authors"].append(fdp_req.get_obj_id_from_url(_new_author_url))
|
|
352
352
|
|
|
353
|
-
if _obj_type == "author":
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return _author
|
|
353
|
+
if _obj_type == "author" and "identifier" in _new_obj_data:
|
|
354
|
+
_author = fdp_req.get_author_exists(
|
|
355
|
+
dest_uri, token=dest_token, identifier=_new_obj_data["identifier"]
|
|
356
|
+
)
|
|
357
|
+
if _author:
|
|
358
|
+
return _author
|
|
360
359
|
|
|
361
360
|
return fdp_req.post_else_get(
|
|
362
361
|
dest_uri,
|
|
@@ -402,6 +401,8 @@ def sync_user_author(
|
|
|
402
401
|
author_url: str,
|
|
403
402
|
remote_user: str,
|
|
404
403
|
) -> None:
|
|
404
|
+
_ = origin_uri # prevent unused variable warning
|
|
405
|
+
_ = origin_token # prevent unused variable warning
|
|
405
406
|
current_user = fdp_req.get(
|
|
406
407
|
dest_uri, "users", dest_token, params={"username": remote_user}
|
|
407
408
|
)
|
|
@@ -64,6 +64,13 @@ import fair.user_config as fdp_user
|
|
|
64
64
|
import fair.logging as fdp_logging
|
|
65
65
|
import fair.registry.storage as fdp_store
|
|
66
66
|
|
|
67
|
+
GLOBAL_CONFIG_MESSAGE = (
|
|
68
|
+
"Setup will now ask you questions regarding the global configuration"
|
|
69
|
+
)
|
|
70
|
+
REPO_CONFIG_MESSAGE = (
|
|
71
|
+
"Setup will now ask you questions regarding this repo configuration"
|
|
72
|
+
)
|
|
73
|
+
|
|
67
74
|
|
|
68
75
|
class FAIR:
|
|
69
76
|
"""
|
|
@@ -1170,17 +1177,13 @@ class FAIR:
|
|
|
1170
1177
|
if not os.path.exists(fdp_com.global_fdpconfig()):
|
|
1171
1178
|
try:
|
|
1172
1179
|
if not self._testing:
|
|
1173
|
-
click.echo(
|
|
1174
|
-
"Setup will now ask you questions regarding the global configuration"
|
|
1175
|
-
)
|
|
1180
|
+
click.echo(GLOBAL_CONFIG_MESSAGE)
|
|
1176
1181
|
self._global_config = fdp_conf.global_config_query(registry, local)
|
|
1177
1182
|
except (fdp_exc.CLIConfigurationError, click.Abort) as e:
|
|
1178
1183
|
self._clean_reset(_fair_dir, e)
|
|
1179
1184
|
try:
|
|
1180
1185
|
if not self._testing:
|
|
1181
|
-
click.echo(
|
|
1182
|
-
"Setup will now ask you questions regarding this repo configuration"
|
|
1183
|
-
)
|
|
1186
|
+
click.echo(REPO_CONFIG_MESSAGE)
|
|
1184
1187
|
self._local_config = fdp_conf.local_config_query(
|
|
1185
1188
|
self._global_config,
|
|
1186
1189
|
first_time_setup=_first_time,
|
|
@@ -1191,9 +1194,7 @@ class FAIR:
|
|
|
1191
1194
|
elif not using:
|
|
1192
1195
|
try:
|
|
1193
1196
|
if not self._testing:
|
|
1194
|
-
click.echo(
|
|
1195
|
-
"Setup will now ask you questions regarding this repo configuration"
|
|
1196
|
-
)
|
|
1197
|
+
click.echo(REPO_CONFIG_MESSAGE)
|
|
1197
1198
|
self._local_config = fdp_conf.local_config_query(
|
|
1198
1199
|
self._global_config, local=local
|
|
1199
1200
|
)
|
|
@@ -1208,14 +1209,10 @@ class FAIR:
|
|
|
1208
1209
|
yaml.dump(self._global_config, f)
|
|
1209
1210
|
else:
|
|
1210
1211
|
if not self._testing:
|
|
1211
|
-
click.echo(
|
|
1212
|
-
"Setup will now ask you questions regarding the global configuration"
|
|
1213
|
-
)
|
|
1212
|
+
click.echo(GLOBAL_CONFIG_MESSAGE)
|
|
1214
1213
|
self._global_config = fdp_conf.read_global_fdpconfig()
|
|
1215
1214
|
if not self._testing:
|
|
1216
|
-
click.echo(
|
|
1217
|
-
"Setup will now ask you questions regarding this repo configuration"
|
|
1218
|
-
)
|
|
1215
|
+
click.echo(REPO_CONFIG_MESSAGE)
|
|
1219
1216
|
self._local_config = fdp_conf.read_local_fdpconfig(self._session_loc)
|
|
1220
1217
|
|
|
1221
1218
|
if export_as:
|
|
@@ -171,7 +171,7 @@ class Stager:
|
|
|
171
171
|
self.add_to_staging(job_id, "job")
|
|
172
172
|
self.change_stage_status(job_id, "job", stage)
|
|
173
173
|
|
|
174
|
-
def find_registry_entry_for_file(self, local_uri: str, file_path: str) ->
|
|
174
|
+
def find_registry_entry_for_file(self, local_uri: str, file_path: str) -> dict:
|
|
175
175
|
"""Performs a rough search for a file in the local registry
|
|
176
176
|
|
|
177
177
|
Parameters
|
|
@@ -312,7 +312,7 @@ class Stager:
|
|
|
312
312
|
|
|
313
313
|
_config_url = self.find_registry_entry_for_file(
|
|
314
314
|
fdp_conf.get_local_uri(), _config_rel_path
|
|
315
|
-
)
|
|
315
|
+
).get("url")
|
|
316
316
|
|
|
317
317
|
# Check for job script file
|
|
318
318
|
_config_yaml = os.path.join(_directory, fdp_com.USER_CONFIG_FILE)
|
|
@@ -355,7 +355,9 @@ class Stager:
|
|
|
355
355
|
_rel_script_path = _script_path.split(fdp_com.JOBS_DIR)[1]
|
|
356
356
|
_rel_script_path = f"{fdp_com.JOBS_DIR}{_rel_script_path}"
|
|
357
357
|
|
|
358
|
-
result = self.find_registry_entry_for_file(local_uri, _rel_script_path)
|
|
358
|
+
result = self.find_registry_entry_for_file(local_uri, _rel_script_path).get(
|
|
359
|
+
"url"
|
|
360
|
+
)
|
|
359
361
|
|
|
360
362
|
self._logger.debug("Script URL: %s", result)
|
|
361
363
|
|
|
@@ -318,7 +318,7 @@ class JobConfiguration(MutableMapping):
|
|
|
318
318
|
for register_entry in register_block:
|
|
319
319
|
_new_entry = register_entry.copy()
|
|
320
320
|
if "namespace_name" not in register_entry:
|
|
321
|
-
if
|
|
321
|
+
if "use" not in register_entry:
|
|
322
322
|
_new_entry["use"] = {}
|
|
323
323
|
else:
|
|
324
324
|
if "namespace" in register_entry["use"]:
|
|
@@ -569,8 +569,8 @@ class JobConfiguration(MutableMapping):
|
|
|
569
569
|
|
|
570
570
|
if not _cmd or not _out_file:
|
|
571
571
|
raise fdp_exc.UserConfigError(
|
|
572
|
-
"Configuration file must contain either a valid
|
|
573
|
-
|
|
572
|
+
"Configuration file must contain either a valid \
|
|
573
|
+
'script' or 'script_path' entry under 'run_metadata'"
|
|
574
574
|
)
|
|
575
575
|
|
|
576
576
|
self.set_script(_out_file)
|
|
@@ -808,7 +808,13 @@ class JobConfiguration(MutableMapping):
|
|
|
808
808
|
# Additional parser for formatted datetime
|
|
809
809
|
_regex_fmt = re.compile(r"\$\{\{\s*([^}${\s]+)\s*\}\}")
|
|
810
810
|
|
|
811
|
-
|
|
811
|
+
# RUN_ID is the code run's UUID, which does not exist until the API
|
|
812
|
+
# creates the code run, so the API substitutes it at finalise
|
|
813
|
+
_unparsed = [
|
|
814
|
+
var for var in _regex_fmt.findall(_conf_str) if var != "RUN_ID"
|
|
815
|
+
]
|
|
816
|
+
|
|
817
|
+
if _unparsed:
|
|
812
818
|
raise fdp_exc.InternalError(f"Failed to parse variables '{_unparsed}'")
|
|
813
819
|
|
|
814
820
|
def _subst_cli_vars(self, job_time: datetime.datetime) -> str:
|
|
@@ -10,7 +10,6 @@ authors = [
|
|
|
10
10
|
classifiers = [
|
|
11
11
|
"License :: OSI Approved :: BSD License",
|
|
12
12
|
"Intended Audience :: Science/Research",
|
|
13
|
-
"Programming Language :: Python :: 3.9",
|
|
14
13
|
"Programming Language :: Python :: 3.10",
|
|
15
14
|
"Programming Language :: Python :: 3.11",
|
|
16
15
|
"Programming Language :: Python :: 3.12",
|
|
@@ -24,7 +23,7 @@ classifiers = [
|
|
|
24
23
|
]
|
|
25
24
|
description = "Synchronization interface for the SCRC FAIR Data Pipeline registry"
|
|
26
25
|
name = "fair-cli"
|
|
27
|
-
version = "0.9.
|
|
26
|
+
version = "0.9.9"
|
|
28
27
|
|
|
29
28
|
homepage = "https://www.fairdatapipeline.org/"
|
|
30
29
|
|
|
@@ -56,7 +55,7 @@ click = "^8.0.0"
|
|
|
56
55
|
email-validator = "^1.1.3"
|
|
57
56
|
pre-commit = "^3"
|
|
58
57
|
pydantic = {extras = ["email"], version = "^1.9.0"}
|
|
59
|
-
python = "^3.
|
|
58
|
+
python = "^3.10.0,<4.0"
|
|
60
59
|
requests = "^2.23.0"
|
|
61
60
|
rich = ">=10.2.3,<12.0.0"
|
|
62
61
|
semver = "^2.13.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|