fair-cli 0.7.3__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {fair_cli-0.7.3 → fair_cli-0.8.0}/CITATION.cff +1 -1
  2. {fair_cli-0.7.3 → fair_cli-0.8.0}/PKG-INFO +2 -2
  3. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/cli.py +9 -5
  4. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/common.py +8 -1
  5. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/configuration/__init__.py +82 -30
  6. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/configuration/validation.py +3 -0
  7. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/history.py +8 -2
  8. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/identifiers.py +58 -7
  9. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/register.py +33 -12
  10. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/registry/requests.py +12 -10
  11. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/registry/server.py +14 -14
  12. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/registry/storage.py +7 -2
  13. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/registry/sync.py +83 -20
  14. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/session.py +57 -24
  15. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/testing.py +3 -2
  16. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/user_config/__init__.py +8 -0
  17. {fair_cli-0.7.3 → fair_cli-0.8.0}/pyproject.toml +14 -15
  18. {fair_cli-0.7.3 → fair_cli-0.8.0}/CHANGELOG.md +0 -0
  19. {fair_cli-0.7.3 → fair_cli-0.8.0}/LICENSE +0 -0
  20. {fair_cli-0.7.3 → fair_cli-0.8.0}/README.md +0 -0
  21. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/__init__.py +0 -0
  22. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/exceptions.py +0 -0
  23. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/files.txt +0 -0
  24. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/logging.py +0 -0
  25. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/registry/__init__.py +0 -0
  26. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/registry/file_formats.json +0 -0
  27. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/registry/file_types.py +0 -0
  28. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/registry/versioning.py +0 -0
  29. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/run.py +0 -0
  30. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/staging.py +0 -0
  31. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/templates/__init__.py +0 -0
  32. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/templates/config.jinja +0 -0
  33. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/templates/hist.jinja +0 -0
  34. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/user_config/globbing.py +0 -0
  35. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/user_config/validation.py +0 -0
  36. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/utilities.py +0 -0
  37. {fair_cli-0.7.3 → fair_cli-0.8.0}/fair/virtualenv.py +0 -0
@@ -36,4 +36,4 @@ license: BSD-2-Clause
36
36
  message: If you use this software, please cite it using these metadata.
37
37
  repository-code: https://github.com/FAIRDataPipeline/FAIR-CLI/
38
38
  title: "The FAIR Data Pipeline command line tool"
39
- version: 0.7.3
39
+ version: 0.8.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fair-cli
3
- Version: 0.7.3
3
+ Version: 0.8.0
4
4
  Summary: Synchronization interface for the SCRC FAIR Data Pipeline registry
5
5
  Home-page: https://www.fairdatapipeline.org/
6
6
  License: BSD-2-Clause
@@ -29,7 +29,7 @@ Requires-Dist: Jinja2 (>=3.0.1,<4.0.0)
29
29
  Requires-Dist: PyYAML (>=5.4.1,<7.0.0)
30
30
  Requires-Dist: click (>=8.0.0,<9.0.0)
31
31
  Requires-Dist: email-validator (>=1.1.3,<2.0.0)
32
- Requires-Dist: netCDF4 (>=1.5.8,<2.0.0)
32
+ Requires-Dist: fake-useragent (>=1,<2)
33
33
  Requires-Dist: pre-commit (>=2.15.0,<3.0.0)
34
34
  Requires-Dist: pydantic[email] (>=1.9.0,<2.0.0)
35
35
  Requires-Dist: requests (>=2.23.0,<3.0.0)
@@ -105,8 +105,7 @@ def status(verbose, debug) -> None:
105
105
  except fdp_exc.FAIRCLIException as e:
106
106
  e.err_print()
107
107
  if e.level.lower() == "error":
108
- if e.level.lower() == "error":
109
- sys.exit(e.exit_code)
108
+ sys.exit(e.exit_code)
110
109
 
111
110
  @cli.group(invoke_without_command=True)
112
111
  @click.option("--debug/--no-debug", help="Run in debug mode", default=False)
@@ -118,8 +117,10 @@ def list(ctx, debug, remote) -> None:
118
117
  ctx.obj = {}
119
118
  ctx.obj['DEBUG'] = debug
120
119
  ctx.obj['REMOTE'] = remote
121
- ctx.invoke(data_products)
122
- ctx.invoke(code_runs)
120
+ _current_args = " ".join(sys.argv)
121
+ if not ("data-products" in _current_args or "code-runs" in _current_args):
122
+ ctx.invoke(data_products)
123
+ ctx.invoke(code_runs)
123
124
 
124
125
  @list.command()
125
126
  @click.pass_context
@@ -161,7 +162,7 @@ def create(debug, output: str) -> None:
161
162
  if output
162
163
  else os.path.join(os.getcwd(), fdp_com.USER_CONFIG_FILE)
163
164
  )
164
- click.echo(f"Generating new user configuration file" f" '{output}'")
165
+ click.echo(f"Generating new user configuration file '{output}'")
165
166
  with fdp_session.FAIR(os.getcwd(), debug=debug) as fair_session:
166
167
  fair_session.make_starter_config(output)
167
168
 
@@ -527,6 +528,7 @@ def run(
527
528
  ):
528
529
  """Initialises a job with the option to specify a bash command"""
529
530
  # Allow no config to be specified, if that is the case use default local
531
+ click.echo("Running run please wait")
530
532
  config = config[0] if config else fdp_com.local_user_config(os.getcwd())
531
533
  try:
532
534
  with fdp_session.FAIR(
@@ -636,6 +638,7 @@ def modify(ctx, label: str, url: str, debug: bool) -> None:
636
638
  )
637
639
  def push(remote: str, debug: bool, dirty: bool):
638
640
  """Push data between the local and remote registry"""
641
+ click.echo("Running push please wait")
639
642
  remote = remote[0] if remote else "origin"
640
643
  try:
641
644
  with fdp_session.FAIR(
@@ -679,6 +682,7 @@ def config_email(user_email: str) -> None:
679
682
  )
680
683
  def pull(config: str, debug: bool, local: bool):
681
684
  """Update local registry from remotes and sources"""
685
+ click.echo("Running pull please wait")
682
686
  config = config[0] if config else fdp_com.local_user_config(os.getcwd())
683
687
  try:
684
688
  with fdp_session.FAIR(
@@ -296,4 +296,11 @@ def set_file_permissions(path: str):
296
296
  for dir in [os.path.join(root,d) for d in dirs]:
297
297
  os.chmod(dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
298
298
  for file in [os.path.join(root, f) for f in files]:
299
- os.chmod(file, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
299
+ os.chmod(file, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
300
+
301
+ def remove_readonly(fn, path, excinfo):
302
+ try:
303
+ os.chmod(path, stat.S_IWRITE)
304
+ fn(path)
305
+ except Exception as exc:
306
+ print("Skipped:", path, "because:\n", exc)
@@ -262,7 +262,7 @@ def remote_git_repo(fair_repo_loc: str) -> str:
262
262
 
263
263
  def get_remote_token(
264
264
  repo_dir: str, remote: str = "origin", local: bool = False
265
- ) -> str:
265
+ ) -> typing.Optional[str]:
266
266
  _local_config = read_local_fdpconfig(repo_dir)
267
267
  if remote not in _local_config["registries"]:
268
268
  raise fdp_exc.CLIConfigurationError(
@@ -378,6 +378,22 @@ def get_current_user_uri(repo_loc: str) -> str:
378
378
  raise fdp_exc.CLIConfigurationError("No user URI identifier defined.")
379
379
  return _uri
380
380
 
381
+ def get_current_user_github(repo_loc: str) -> str:
382
+ """Retrieves the URI identifier for the current user
383
+
384
+ Returns
385
+ -------
386
+ str
387
+ github username
388
+ """
389
+ _local_conf = read_local_fdpconfig(repo_loc)
390
+ try:
391
+ _github = _local_conf["user"]["github"]
392
+ except KeyError:
393
+ _github = None
394
+ if not _github or _github == "None":
395
+ raise fdp_exc.CLIConfigurationError("No user GitHub username defined.")
396
+ return _github
381
397
 
382
398
  def check_registry_exists(registry: str = None) -> typing.Optional[str]:
383
399
  """Checks if fair registry is set up on users machine
@@ -436,10 +452,10 @@ def get_local_port(local_uri: str = None) -> int:
436
452
  return _port
437
453
 
438
454
 
439
- def update_local_port() -> str:
455
+ def update_local_port(registry_dir: str = None) -> str:
440
456
  """Updates the local port in the global configuration from the session port file"""
441
- _current_port = fdp_com.registry_session_port()
442
- _current_address = fdp_com.registry_session_address()
457
+ _current_port = fdp_com.registry_session_port(registry_dir)
458
+ _current_address = fdp_com.registry_session_address(registry_dir)
443
459
 
444
460
  _new_url = f'http://{_current_address}:{_current_port}/api/'
445
461
 
@@ -474,7 +490,7 @@ def _handle_orcid(user_orcid: str) -> typing.Tuple[typing.Dict, str]:
474
490
  user_orcid = click.prompt("ORCID")
475
491
  _user_info = fdp_id.check_orcid(user_orcid.strip())
476
492
 
477
- _user_info["orcid"] = user_orcid
493
+ _user_info["orcid"] = user_orcid.strip()
478
494
 
479
495
  click.echo(
480
496
  f"Found entry: {_user_info['given_names']} {_user_info['family_name']}"
@@ -509,7 +525,7 @@ def _handle_ror(user_ror: str) -> typing.Tuple[typing.Dict, str]:
509
525
  user_ror = click.prompt("ROR ID")
510
526
  _user_info = fdp_id.check_ror(user_ror.strip())
511
527
 
512
- _user_info["ror"] = user_ror
528
+ _user_info["ror"] = user_ror.strip()
513
529
 
514
530
  click.echo(f"Found entry: {_user_info['family_name']} ")
515
531
 
@@ -540,7 +556,7 @@ def _handle_grid(user_grid: str) -> typing.Tuple[typing.Dict, str]:
540
556
  user_grid = click.prompt("GRID ID")
541
557
  _user_info = fdp_id.check_grid(user_grid.strip())
542
558
 
543
- _user_info["grid"] = user_grid
559
+ _user_info["grid"] = user_grid.strip()
544
560
 
545
561
  click.echo(f"Found entry: {_user_info['family_name']} ")
546
562
 
@@ -548,6 +564,22 @@ def _handle_grid(user_grid: str) -> typing.Tuple[typing.Dict, str]:
548
564
 
549
565
  return _user_info, _def_ospace
550
566
 
567
+ def _handle_github(user_github: str) -> typing.Tuple[typing.Dict, str]:
568
+
569
+ _user_info = fdp_id.check_github(user_github.strip())
570
+
571
+ while not _user_info:
572
+ time.sleep(3)
573
+ click.echo(f"Invalid GitHub Username '{user_github}' given.")
574
+ user_github = click.prompt("GitHub Username")
575
+ _user_info = fdp_id.check_github(user_github.strip())
576
+
577
+ _def_ospace = _user_info["github"].lower()
578
+
579
+ click.echo(f"Found entry: {_user_info['github']} ")
580
+ return _user_info, _def_ospace
581
+
582
+
551
583
 
552
584
  def _handle_uuid() -> typing.Tuple[typing.Dict, str]:
553
585
  """Obtain metadata for user where no ID provided
@@ -575,17 +607,24 @@ def _handle_uuid() -> typing.Tuple[typing.Dict, str]:
575
607
  return _user_info, _def_ospace
576
608
 
577
609
 
578
- def _get_user_info_and_namespaces() -> typing.Dict[str, typing.Dict]:
579
- _user_email = click.prompt("Email")
610
+ def _get_user_info_and_namespaces(local: bool = False) -> typing.Dict[str, typing.Dict]:
611
+ _user_email = click.prompt("Email (optional)", default = "")
580
612
 
581
613
  _invalid_input = True
582
614
 
583
615
  while _invalid_input:
584
616
  _id_type = click.prompt(
585
- "User ID system (ORCID/ROR/GRID/None)", default="None"
617
+ "User ID system (GITHUB/ORCID/ROR/GRID/None)", default="GITHUB"
586
618
  )
587
-
588
- if _id_type.upper() == "ORCID":
619
+ if _id_type.upper() == "GITHUB":
620
+ _user_github = click.prompt("GitHub Username")
621
+ _user_info, _def_ospace = _handle_github(_user_github)
622
+ if not _user_info["name"]:
623
+ _user_info["given_names"] = click.prompt("Given Names")
624
+ _user_info["family_name"] = click.prompt("Family Name")
625
+ _user_info["name"] = " ".join([_user_info["given_names"], _user_info["family_name"]])
626
+ _invalid_input = False
627
+ elif _id_type.upper() == "ORCID":
589
628
  _user_orcid = click.prompt("ORCID")
590
629
  _user_info, _def_ospace = _handle_orcid(_user_orcid)
591
630
  _invalid_input = False
@@ -603,14 +642,25 @@ def _get_user_info_and_namespaces() -> typing.Dict[str, typing.Dict]:
603
642
  _user_info["uuid"] = _user_uuid
604
643
  _invalid_input = False
605
644
 
606
- _user_info["email"] = _user_email
607
-
608
645
  _def_ospace = _def_ospace.lower().replace(" ", "").strip()
609
646
  _def_ospace = click.prompt("Default output namespace", default=_def_ospace)
610
647
  _def_ispace = click.prompt("Default input namespace", default=_def_ospace)
611
648
 
612
649
  _namespaces = {"input": _def_ispace, "output": _def_ospace}
613
650
 
651
+ if not "github" in _user_info:
652
+ if local:
653
+ _user_info["github"] = "FAIRDataPipeline"
654
+ else:
655
+ _user_github = click.prompt("GitHub Username")
656
+ _user_github_info = _handle_github(_user_github)[0]
657
+ _user_info["github"] = _user_github_info["github"]
658
+
659
+ if not _user_email:
660
+ _user_info["email"] = f'{_user_info["github"]}@users.noreply.github.com'
661
+ else:
662
+ _user_info["email"] = _user_email
663
+
614
664
  return {"user": _user_info, "namespaces": _namespaces}
615
665
 
616
666
 
@@ -668,21 +718,23 @@ def global_config_query(
668
718
  default=_remote_url.replace("/api/", "/data/"),
669
719
  )
670
720
 
671
- _rem_key_file = click.prompt(
672
- "Remote API Token File",
673
- )
674
- _rem_key_file = os.path.expandvars(_rem_key_file)
675
-
676
- while (
677
- not os.path.exists(_rem_key_file)
678
- or not open(_rem_key_file).read().strip()
679
- ):
680
- click.echo(
681
- f"Token file '{_rem_key_file}' does not exist or is empty, "
682
- "please provide a valid token file."
721
+ _rem_key_valid = False
722
+ while not _rem_key_valid:
723
+ _rem_key = click.prompt(
724
+ f"Remote API Token",
683
725
  )
684
- _rem_key_file = click.prompt("Remote API Token File")
685
- _rem_key_file = os.path.expandvars(_rem_key_file)
726
+ if len(_rem_key) == 40:
727
+ _rem_key_valid = True
728
+ else:
729
+ click.echo("Remote token should be 40 characters long")
730
+
731
+ _rem_key_file = os.path.join(fdp_com.global_config_dir(), "remotetoken.txt")
732
+
733
+ with open(_rem_key_file, 'w') as f:
734
+ f.write(_rem_key)
735
+
736
+ if not os.path.exists(_rem_key_file):
737
+ raise fdp_exc.CLIConfigurationError(f'Token could not be written to {_rem_key_file}')
686
738
 
687
739
  if not fdp_serv.check_server_running():
688
740
  if _ := click.confirm(
@@ -696,7 +748,7 @@ def global_config_query(
696
748
  pathlib.Path(_cache_addr).touch()
697
749
 
698
750
  else:
699
- click.echo("Temporarily launching server to retrieve API token.")
751
+ click.echo("Temporarily launching local registry to retrieve API token.")
700
752
  fdp_serv.launch_server(registry_dir=registry)
701
753
  fdp_serv.stop_server(registry_dir=registry, local_uri=_local_uri)
702
754
  try:
@@ -713,7 +765,7 @@ def global_config_query(
713
765
  if _loc_data_store[-1] != os.path.sep:
714
766
  _loc_data_store += os.path.sep
715
767
 
716
- _glob_conf_dict = _get_user_info_and_namespaces()
768
+ _glob_conf_dict = _get_user_info_and_namespaces(local)
717
769
  _glob_conf_dict["registries"] = {
718
770
  "local": {
719
771
  "uri": _local_uri,
@@ -108,6 +108,9 @@ class User(pydantic.BaseModel):
108
108
  name: typing.Optional[str] = pydantic.Field(
109
109
  None, title="Full Name", description="Full name for the user"
110
110
  )
111
+ github: typing.Optional[str] = pydantic.Field(
112
+ None, title="GitHub Username", description="GitHub Username for the user"
113
+ )
111
114
 
112
115
  class Config:
113
116
  extra = "forbid"
@@ -22,6 +22,8 @@ import os
22
22
  import click
23
23
  import rich
24
24
 
25
+ from pathlib import Path
26
+
25
27
  import fair.common as fdp_com
26
28
  import fair.exceptions as fdp_exc
27
29
  import fair.run as fdp_run
@@ -61,8 +63,10 @@ def show_job_log(repo_loc: str, job_id: str) -> str:
61
63
  str
62
64
  log file location for the given job
63
65
  """
66
+ _job_dir = Path(f"{fdp_com.default_jobs_dir()}")
67
+
64
68
  _sorted_time_dirs = sorted(
65
- glob.glob(os.path.join(fdp_com.default_jobs_dir(), "*")), reverse=True
69
+ glob.glob(os.path.join(_job_dir, "*")), reverse=True
66
70
  )
67
71
 
68
72
  _log_files = [
@@ -107,8 +111,10 @@ def show_history(repo_loc: str, length: int = 10) -> None:
107
111
  max number of entries to display, by default 10
108
112
  """
109
113
 
114
+ _job_dir = Path(f"{fdp_com.default_jobs_dir()}")
115
+
110
116
  _sorted_time_dirs = sorted(
111
- glob.glob(os.path.join(fdp_com.default_jobs_dir(), "*")), reverse=True
117
+ glob.glob(os.path.join(_job_dir, "*")), reverse=True
112
118
  )
113
119
 
114
120
  _log_files = [
@@ -24,15 +24,22 @@ import urllib.parse
24
24
 
25
25
  import requests
26
26
  import requests.exceptions
27
+ import logging
28
+ from urllib3.exceptions import InsecureRequestWarning
29
+ from fake_useragent import UserAgent
30
+
31
+ logger = logging.getLogger("FAIRDataPipeline.Identifiers")
27
32
 
28
33
  ID_URIS = {
29
34
  "orcid": "https://orcid.org/",
30
35
  "ror": "https://ror.org/",
36
+ "github" : "https://github.com/"
31
37
  }
32
38
 
33
39
  QUERY_URLS = {
34
40
  "orcid": "https://pub.orcid.org/v3.0/",
35
41
  "ror": "https://api.ror.org/organizations?query=",
42
+ "github": "https://api.github.com/users/"
36
43
  }
37
44
 
38
45
 
@@ -49,14 +56,16 @@ def check_orcid(orcid: str) -> typing.Dict:
49
56
  typing.Dict
50
57
  metadata from the given ID
51
58
  """
52
-
59
+ orcid = orcid.replace(ID_URIS["orcid"], "")
53
60
  _header = {"Accept": "application/json"}
54
61
  _url = urllib.parse.urljoin(QUERY_URLS["orcid"], orcid)
55
- _response = requests.get(_url, headers=_header)
62
+ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
63
+ _response = requests.get(_url, headers=_header, verify = False, allow_redirects = True)
56
64
 
57
65
  _result_dict: typing.Dict[str, typing.Any] = {}
58
66
 
59
67
  if _response.status_code != 200:
68
+ logger.debug(f"{_url} Responded with {_response.status_code}")
60
69
  return _result_dict
61
70
 
62
71
  _names = _response.json()["person"]["name"]
@@ -72,6 +81,42 @@ def check_orcid(orcid: str) -> typing.Dict:
72
81
 
73
82
  return _result_dict
74
83
 
84
+ def check_github(github: str) -> typing.Dict:
85
+ """Checks if valid ORCID using ORCID public api
86
+
87
+ Parameters
88
+ ----------
89
+ github : str
90
+ github username to be checked
91
+
92
+ Returns
93
+ -------
94
+ typing.Dict
95
+ metadata from the given ID
96
+ """
97
+ _header = {"Accept": "application/json"}
98
+ _url = urllib.parse.urljoin(QUERY_URLS["github"], github)
99
+ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
100
+ _response = requests.get(_url, headers=_header, verify = False, allow_redirects = True)
101
+
102
+ _result_dict: typing.Dict[str, typing.Any] = {}
103
+
104
+ if _response.status_code != 200:
105
+ logger.debug(f"{_url} Responded with {_response.status_code}")
106
+ return _result_dict
107
+
108
+ _login = _response.json()["login"]
109
+ _name = _response.json()["name"]
110
+ if _name:
111
+ _result_dict["family_name"] = _name.split()[-1]
112
+ _result_dict["given_names"] = " ".join(_name.split()[:-1])
113
+
114
+ _result_dict["name"] = _name
115
+ _result_dict["github"] = _login
116
+ _result_dict["uri"] = f'{ID_URIS["github"]}{_login}'
117
+
118
+ return _result_dict
119
+
75
120
 
76
121
  def check_ror(ror: str) -> typing.Dict:
77
122
  """Checks if valid ROR using ROR public api
@@ -86,7 +131,6 @@ def check_ror(ror: str) -> typing.Dict:
86
131
  typing.Dict
87
132
  metadata from the given ID
88
133
  """
89
-
90
134
  _result_dict = _check_generic_ror(ror)
91
135
  if _result_dict:
92
136
  _result_dict["ror"] = ror
@@ -124,13 +168,13 @@ def _check_generic_ror(id: str) -> typing.Dict:
124
168
  typing.Dict
125
169
  metadata from the given ID
126
170
  """
127
-
128
171
  _url = f"{QUERY_URLS['ror']}{id}"
129
172
  _response = requests.get(_url)
130
173
 
131
174
  _result_dict: typing.Dict[str, typing.Any] = {}
132
175
 
133
176
  if _response.status_code != 200:
177
+ logger.debug(f"{_url} Responded with {_response.status_code}")
134
178
  return _result_dict
135
179
 
136
180
  if _response.json()["number_of_results"] == 0:
@@ -164,18 +208,25 @@ def check_id_permitted(identifier: str, retries: int = 5) -> bool:
164
208
  if valid identifier
165
209
  """
166
210
  _n_attempts = 0
211
+ fake_agent = False
167
212
 
168
213
  while _n_attempts < retries:
169
214
  try:
170
- requests.get(identifier).raise_for_status()
215
+ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
216
+ headers = {}
217
+ if fake_agent:
218
+ headers = {'User-Agent':str(UserAgent().chrome)}
219
+ requests.get(identifier, verify = False, allow_redirects = True, headers = headers).raise_for_status()
171
220
  return True
172
221
  except (
173
222
  requests.exceptions.MissingSchema,
174
223
  requests.exceptions.HTTPError,
175
224
  requests.exceptions.ConnectionError,
176
- ):
225
+ ) as e:
177
226
  _n_attempts += 1
178
- time.sleep(1)
227
+ time.sleep(3)
228
+ fake_agent = True
229
+ logger.warning(f"Error identifier: '{identifier}' caused '{e}'")
179
230
  continue
180
231
 
181
232
  return False
@@ -88,7 +88,7 @@ def fetch_registrations(
88
88
  typing.List[str]
89
89
  list of registered object URLs
90
90
  """
91
- _expected_keys = [
91
+ _expected_keys_external_object = [
92
92
  "root",
93
93
  "path",
94
94
  "file_type",
@@ -97,9 +97,24 @@ def fetch_registrations(
97
97
  "public",
98
98
  ]
99
99
 
100
+ _expected_keys_data_product = [
101
+ "root",
102
+ "path",
103
+ "file_type",
104
+ "version",
105
+ "public"
106
+ ]
107
+
100
108
  _stored_objects: typing.List[str] = []
101
109
 
102
110
  for entry in user_config_register:
111
+ logger.debug(f"entry {entry}")
112
+
113
+ if "external_object" in entry:
114
+ _expected_keys = _expected_keys_external_object
115
+ else:
116
+ _expected_keys = _expected_keys_data_product
117
+
103
118
  for key in _expected_keys:
104
119
  if key not in entry and key not in entry["use"]:
105
120
  raise fdp_exc.UserConfigError(
@@ -166,18 +181,22 @@ def fetch_registrations(
166
181
  _search_data["version"] = entry["use"]["version"]
167
182
  _namespace = entry["use"]["namespace"]
168
183
 
169
- if not _identifier and not _unique_name:
170
- raise fdp_exc.UserConfigError(
171
- "Expected either 'unique_name' or 'identifier' in 'register' item"
172
- )
173
-
174
- elif _identifier and _unique_name:
175
- raise fdp_exc.UserConfigError(
176
- "Only one unique identifier may be provided (doi/unique_name)"
177
- )
184
+ if _external_object:
185
+ if not _identifier and not _unique_name:
186
+ raise fdp_exc.UserConfigError(
187
+ "Expected either 'unique_name' or 'identifier' in 'register' item"
188
+ )
178
189
 
190
+ elif _identifier and _unique_name:
191
+ raise fdp_exc.UserConfigError(
192
+ "Only one unique identifier may be provided (doi/unique_name)"
193
+ )
194
+ # Set Remove to True by default so the tempory file gets deleted
195
+ _remove = True
179
196
  if "cache" in entry:
180
197
  _temp_data_file = entry["cache"]
198
+ # Don't delete the tempory file if it's from a cache
199
+ _remove = False
181
200
  else:
182
201
  _local_parsed = urllib.parse.urlparse(local_uri)
183
202
  _local_url = f"{_local_parsed.scheme}://{_local_parsed.netloc}"
@@ -207,7 +226,8 @@ def fetch_registrations(
207
226
  " present with this name, deleting temporary data file",
208
227
  _name,
209
228
  )
210
- os.remove(_temp_data_file)
229
+ if _remove:
230
+ os.remove(_temp_data_file)
211
231
  continue
212
232
 
213
233
  # Item found but not hash matched retrieve a version number
@@ -239,7 +259,8 @@ def fetch_registrations(
239
259
  logger.debug("Saving data file to '%s'", _local_file)
240
260
  shutil.copy(_temp_data_file, _local_file)
241
261
 
242
- os.remove(_temp_data_file)
262
+ if _remove:
263
+ os.remove(_temp_data_file)
243
264
 
244
265
  if "public" in entry:
245
266
  _public = entry["public"]
@@ -42,6 +42,9 @@ import fair.utilities as fdp_util
42
42
  import ssl
43
43
  from requests.adapters import HTTPAdapter
44
44
  from urllib3.poolmanager import PoolManager
45
+ from urllib3.exceptions import InsecureRequestWarning
46
+
47
+ from fake_useragent import UserAgent
45
48
 
46
49
  logger = logging.getLogger("FAIRDataPipeline.Requests")
47
50
 
@@ -528,8 +531,8 @@ def download_file(url: str, chunk_size: int = 8192) -> str:
528
531
  _file = tempfile.NamedTemporaryFile(delete=False)
529
532
  _fname = _file.name
530
533
 
531
- # Copy File if local (Windows fix)
532
- if "file://" in url and platform.system() == "Windows":
534
+ # Copy File if local
535
+ if "file://" in url:
533
536
  _local_fname = url.replace("file://", "")
534
537
  try:
535
538
  shutil.copy2(_local_fname, _fname)
@@ -538,17 +541,16 @@ def download_file(url: str, chunk_size: int = 8192) -> str:
538
541
  f"Failed to download file '{url}'"
539
542
  f" due to connection error: {traceback.format_exc()}"
540
543
  ) from e
541
-
542
544
  else:
543
545
  try:
544
- with urllib.request.urlopen(url) as response, open(
545
- _fname, "wb"
546
- ) as out_file:
547
- shutil.copyfileobj(response, out_file)
548
- except urllib.error.URLError as e:
546
+ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
547
+ headers = {'User-Agent':str(UserAgent().chrome)}
548
+ response = requests.get(url, allow_redirects = True, verify = False, headers = headers)
549
+ open(_fname, 'wb').write(response.content)
550
+ except Exception as e:
549
551
  raise fdp_exc.FAIRCLIException(
550
552
  f"Failed to download file '{url}'"
551
- f" due to connection error: {e.reason}"
553
+ f" due to connection error: {traceback.format_exc()}"
552
554
  ) from e
553
555
 
554
556
  return _fname
@@ -571,7 +573,7 @@ def get_dependency_listing(uri: str, token: str, read_only: bool = False) -> typ
571
573
  """
572
574
  try:
573
575
  _registry_objs = url_get(uri, token)
574
- except:
576
+ except Exception:
575
577
  return {[]}
576
578
 
577
579
  _rtn = {