digitalhub 0.13.0b2__py3-none-any.whl → 0.13.0b4__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.

Files changed (61) hide show
  1. digitalhub/__init__.py +1 -1
  2. digitalhub/context/api.py +5 -5
  3. digitalhub/context/builder.py +3 -5
  4. digitalhub/context/context.py +9 -1
  5. digitalhub/entities/_base/material/entity.py +3 -3
  6. digitalhub/entities/_commons/metrics.py +64 -30
  7. digitalhub/entities/_commons/utils.py +36 -9
  8. digitalhub/entities/_processors/base.py +150 -79
  9. digitalhub/entities/_processors/context.py +363 -212
  10. digitalhub/entities/_processors/utils.py +74 -30
  11. digitalhub/entities/artifact/utils.py +28 -13
  12. digitalhub/entities/dataitem/crud.py +10 -2
  13. digitalhub/entities/dataitem/table/entity.py +3 -3
  14. digitalhub/entities/dataitem/utils.py +84 -35
  15. digitalhub/entities/model/utils.py +28 -13
  16. digitalhub/entities/task/_base/models.py +12 -3
  17. digitalhub/factory/factory.py +25 -3
  18. digitalhub/factory/utils.py +11 -3
  19. digitalhub/runtimes/_base.py +1 -1
  20. digitalhub/runtimes/builder.py +18 -1
  21. digitalhub/stores/client/__init__.py +12 -0
  22. digitalhub/stores/client/_base/api_builder.py +14 -0
  23. digitalhub/stores/client/_base/client.py +93 -0
  24. digitalhub/stores/client/_base/key_builder.py +28 -0
  25. digitalhub/stores/client/_base/params_builder.py +14 -0
  26. digitalhub/stores/client/api.py +10 -5
  27. digitalhub/stores/client/builder.py +3 -1
  28. digitalhub/stores/client/dhcore/api_builder.py +17 -0
  29. digitalhub/stores/client/dhcore/client.py +276 -58
  30. digitalhub/stores/client/dhcore/configurator.py +336 -141
  31. digitalhub/stores/client/dhcore/error_parser.py +35 -1
  32. digitalhub/stores/client/dhcore/params_builder.py +113 -17
  33. digitalhub/stores/client/dhcore/utils.py +32 -14
  34. digitalhub/stores/client/local/api_builder.py +17 -0
  35. digitalhub/stores/client/local/client.py +6 -8
  36. digitalhub/stores/credentials/api.py +8 -8
  37. digitalhub/stores/credentials/configurator.py +176 -3
  38. digitalhub/stores/credentials/enums.py +17 -3
  39. digitalhub/stores/credentials/handler.py +73 -45
  40. digitalhub/stores/credentials/ini_module.py +59 -27
  41. digitalhub/stores/credentials/store.py +33 -1
  42. digitalhub/stores/data/_base/store.py +8 -3
  43. digitalhub/stores/data/api.py +20 -16
  44. digitalhub/stores/data/builder.py +69 -13
  45. digitalhub/stores/data/s3/configurator.py +64 -23
  46. digitalhub/stores/data/s3/store.py +30 -27
  47. digitalhub/stores/data/s3/utils.py +9 -9
  48. digitalhub/stores/data/sql/configurator.py +76 -25
  49. digitalhub/stores/data/sql/store.py +180 -91
  50. digitalhub/utils/exceptions.py +6 -0
  51. digitalhub/utils/file_utils.py +53 -30
  52. digitalhub/utils/generic_utils.py +41 -33
  53. digitalhub/utils/git_utils.py +24 -14
  54. digitalhub/utils/io_utils.py +19 -18
  55. digitalhub/utils/uri_utils.py +31 -31
  56. {digitalhub-0.13.0b2.dist-info → digitalhub-0.13.0b4.dist-info}/METADATA +1 -1
  57. {digitalhub-0.13.0b2.dist-info → digitalhub-0.13.0b4.dist-info}/RECORD +60 -61
  58. digitalhub/entities/_commons/types.py +0 -9
  59. {digitalhub-0.13.0b2.dist-info → digitalhub-0.13.0b4.dist-info}/WHEEL +0 -0
  60. {digitalhub-0.13.0b2.dist-info → digitalhub-0.13.0b4.dist-info}/licenses/AUTHORS +0 -0
  61. {digitalhub-0.13.0b2.dist-info → digitalhub-0.13.0b4.dist-info}/licenses/LICENSE +0 -0
@@ -4,8 +4,11 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ from datetime import datetime, timezone
8
+
7
9
  from botocore.config import Config
8
10
 
11
+ from digitalhub.stores.client.dhcore.utils import refresh_token
9
12
  from digitalhub.stores.credentials.configurator import Configurator
10
13
  from digitalhub.stores.credentials.enums import CredsEnvVar
11
14
 
@@ -17,17 +20,19 @@ class S3StoreConfigurator(Configurator):
17
20
  """
18
21
 
19
22
  keys = [
20
- CredsEnvVar.S3_ENDPOINT_URL,
21
- CredsEnvVar.S3_ACCESS_KEY_ID,
22
- CredsEnvVar.S3_SECRET_ACCESS_KEY,
23
- CredsEnvVar.S3_REGION,
24
- CredsEnvVar.S3_SIGNATURE_VERSION,
25
- CredsEnvVar.S3_SESSION_TOKEN,
23
+ CredsEnvVar.S3_ENDPOINT_URL.value,
24
+ CredsEnvVar.S3_ACCESS_KEY_ID.value,
25
+ CredsEnvVar.S3_SECRET_ACCESS_KEY.value,
26
+ CredsEnvVar.S3_REGION.value,
27
+ CredsEnvVar.S3_SIGNATURE_VERSION.value,
28
+ CredsEnvVar.S3_SESSION_TOKEN.value,
29
+ CredsEnvVar.S3_PATH_STYLE.value,
30
+ CredsEnvVar.S3_CREDENTIALS_EXPIRATION.value,
26
31
  ]
27
32
  required_keys = [
28
- CredsEnvVar.S3_ENDPOINT_URL,
29
- CredsEnvVar.S3_ACCESS_KEY_ID,
30
- CredsEnvVar.S3_SECRET_ACCESS_KEY,
33
+ CredsEnvVar.S3_ENDPOINT_URL.value,
34
+ CredsEnvVar.S3_ACCESS_KEY_ID.value,
35
+ CredsEnvVar.S3_SECRET_ACCESS_KEY.value,
31
36
  ]
32
37
 
33
38
  def __init__(self):
@@ -38,30 +43,43 @@ class S3StoreConfigurator(Configurator):
38
43
  # Configuration methods
39
44
  ##############################
40
45
 
41
- def load_configs(self) -> None:
42
- # Load from env
43
- env_creds = {var.value: self._creds_handler.load_from_env(var.value) for var in self.keys}
46
+ def load_env_vars(self) -> None:
47
+ """
48
+ Loads the credentials from the environment variables.
49
+
50
+ Returns
51
+ -------
52
+ None
53
+ """
54
+ env_creds = self._creds_handler.load_from_env(self.keys)
44
55
  self._creds_handler.set_credentials(self._env, env_creds)
45
56
 
46
- # Load from file
47
- file_creds = {var.value: self._creds_handler.load_from_file(var.value) for var in self.keys}
48
- self._creds_handler.set_credentials(self._file, file_creds)
57
+ def load_file_vars(self) -> None:
58
+ """
59
+ Loads the credentials from a file.
49
60
 
50
- def get_client_config(self, origin: str) -> dict:
61
+ Returns
62
+ -------
63
+ None
51
64
  """
52
- Get S3 credentials (access key, secret key, session token and other config).
65
+ file_creds = self._creds_handler.load_from_file(self.keys)
66
+ self._creds_handler.set_credentials(self._file, file_creds)
53
67
 
54
- Parameters
55
- ----------
56
- origin : str
57
- The origin of the credentials.
68
+ def get_client_config(self) -> dict:
69
+ """
70
+ Gets S3 credentials (access key, secret key, session token, and other config).
58
71
 
59
72
  Returns
60
73
  -------
61
74
  dict
62
- The credentials.
75
+ Dictionary containing S3 credentials and configuration.
63
76
  """
64
- creds = self.get_credentials(origin)
77
+ creds = self.get_credentials(self._origin)
78
+ expired = creds[CredsEnvVar.S3_CREDENTIALS_EXPIRATION.value]
79
+ if self._origin == self._file and self._is_expired(expired):
80
+ refresh_token()
81
+ self.load_file_vars()
82
+ creds = self.get_credentials(self._origin)
65
83
  return {
66
84
  "endpoint_url": creds[CredsEnvVar.S3_ENDPOINT_URL.value],
67
85
  "aws_access_key_id": creds[CredsEnvVar.S3_ACCESS_KEY_ID.value],
@@ -72,3 +90,26 @@ class S3StoreConfigurator(Configurator):
72
90
  signature_version=creds[CredsEnvVar.S3_SIGNATURE_VERSION.value],
73
91
  ),
74
92
  }
93
+
94
+ @staticmethod
95
+ def _is_expired(timestamp: str | None) -> bool:
96
+ """
97
+ Determines whether a given timestamp is after the current UTC time.
98
+
99
+ Parameters
100
+ ----------
101
+ timestamp : str or None
102
+ Timestamp string in the format 'YYYY-MM-DDTHH:MM:SSZ'.
103
+
104
+ Returns
105
+ -------
106
+ bool
107
+ True if the given timestamp is later than the current UTC time,
108
+ otherwise False.
109
+ """
110
+ if timestamp is None:
111
+ return False
112
+ dt = datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%SZ")
113
+ dt = dt.replace(tzinfo=timezone.utc)
114
+ now = datetime.now(timezone.utc) + datetime.timedelta(seconds=120)
115
+ return dt < now
@@ -15,11 +15,10 @@ import botocore.client # pylint: disable=unused-import
15
15
  from boto3.s3.transfer import TransferConfig
16
16
  from botocore.exceptions import ClientError, NoCredentialsError
17
17
 
18
- from digitalhub.stores.credentials.enums import CredsOrigin
19
18
  from digitalhub.stores.data._base.store import Store
20
19
  from digitalhub.stores.data.s3.utils import get_bucket_name
21
20
  from digitalhub.stores.readers.data.api import get_reader_by_object
22
- from digitalhub.utils.exceptions import StoreError
21
+ from digitalhub.utils.exceptions import ConfigError, StoreError
23
22
  from digitalhub.utils.file_utils import get_file_info_from_s3, get_file_mime_type
24
23
  from digitalhub.utils.types import SourcesOrListOfSources
25
24
 
@@ -614,55 +613,59 @@ class S3Store(Store):
614
613
  """
615
614
  return boto3.client("s3", **cfg)
616
615
 
617
- def _check_factory(self, root: str) -> tuple[S3Client, str]:
616
+ def _check_factory(self, s3_path: str, retry: bool = True) -> tuple[S3Client, str]:
618
617
  """
619
- Check if the S3 bucket is accessible by sending a head_bucket request.
618
+ Checks if the S3 bucket collected from the URI is accessible.
619
+
620
+ Parameters
621
+ ----------
622
+ s3_path : str
623
+ Path to the S3 bucket (e.g., 's3://bucket/path').
624
+ retry : bool, optional
625
+ Whether to retry the operation if a ConfigError is raised. Default is True.
620
626
 
621
627
  Returns
622
628
  -------
623
- tuple[S3Client, str]
624
- A tuple containing the S3 client object and the name of the S3 bucket.
625
- """
626
- bucket = self._get_bucket(root)
629
+ tuple of S3Client and str
630
+ Tuple containing the S3 client object and the name of the S3 bucket.
627
631
 
628
- # Try to get client from environment variables
632
+ Raises
633
+ ------
634
+ ConfigError
635
+ If access to the specified bucket is not available and retry is False.
636
+ """
637
+ bucket = self._get_bucket(s3_path)
629
638
  try:
630
- cfg = self._configurator.get_client_config(CredsOrigin.ENV.value)
639
+ cfg = self._configurator.get_client_config()
631
640
  client = self._get_client(cfg)
632
641
  self._check_access_to_storage(client, bucket)
633
-
634
- # Fallback to file
635
- except StoreError:
636
- cfg = self._configurator.get_client_config(CredsOrigin.FILE.value)
637
- client = self._get_client(cfg)
638
- self._check_access_to_storage(client, bucket)
639
-
640
- return client, bucket
642
+ return client, bucket
643
+ except ConfigError as e:
644
+ if retry:
645
+ self._configurator.eval_change_origin()
646
+ return self._check_factory(s3_path, False)
647
+ raise e
641
648
 
642
649
  def _check_access_to_storage(self, client: S3Client, bucket: str) -> None:
643
650
  """
644
- Check if the S3 bucket is accessible by sending a head_bucket request.
651
+ Checks if the S3 bucket is accessible by sending a head_bucket request.
645
652
 
646
653
  Parameters
647
654
  ----------
648
655
  client : S3Client
649
- The S3 client object.
656
+ S3 client object.
650
657
  bucket : str
651
- The name of the S3 bucket.
652
-
653
- Returns
654
- -------
655
- None
658
+ Name of the S3 bucket.
656
659
 
657
660
  Raises
658
661
  ------
659
- ClientError:
662
+ ConfigError
660
663
  If access to the specified bucket is not available.
661
664
  """
662
665
  try:
663
666
  client.head_bucket(Bucket=bucket)
664
667
  except (ClientError, NoCredentialsError) as err:
665
- raise StoreError(f"No access to s3 bucket! Error: {err}")
668
+ raise ConfigError(f"No access to s3 bucket! Error: {err}")
666
669
 
667
670
  @staticmethod
668
671
  def _get_key(path: str) -> str:
@@ -16,34 +16,34 @@ from digitalhub.utils.exceptions import StoreError
16
16
 
17
17
  def get_bucket_name(path: str) -> str:
18
18
  """
19
- Get bucket name from path.
19
+ Extract the bucket name from an S3 path.
20
20
 
21
21
  Parameters
22
22
  ----------
23
23
  path : str
24
- The source path to get the key from.
24
+ S3 URI (e.g., 's3://bucket/key').
25
25
 
26
26
  Returns
27
27
  -------
28
28
  str
29
- The bucket name.
29
+ The bucket name extracted from the URI.
30
30
  """
31
31
  return urlparse(path).netloc
32
32
 
33
33
 
34
34
  def get_bucket_and_key(path: str) -> tuple[str, str]:
35
35
  """
36
- Get bucket and key from path.
36
+ Extract the bucket name and key from an S3 path.
37
37
 
38
38
  Parameters
39
39
  ----------
40
40
  path : str
41
- The source path to get the key from.
41
+ S3 URI (e.g., 's3://bucket/key').
42
42
 
43
43
  Returns
44
44
  -------
45
- tuple[str, str]
46
- The bucket and key.
45
+ tuple of str
46
+ Tuple containing (bucket, key) extracted from the URI.
47
47
  """
48
48
  parsed = urlparse(path)
49
49
  return parsed.netloc, parsed.path
@@ -51,7 +51,7 @@ def get_bucket_and_key(path: str) -> tuple[str, str]:
51
51
 
52
52
  def get_s3_source(bucket: str, key: str, filename: Path) -> None:
53
53
  """
54
- Get S3 source.
54
+ Download an object from S3 and save it to a local file.
55
55
 
56
56
  Parameters
57
57
  ----------
@@ -60,7 +60,7 @@ def get_s3_source(bucket: str, key: str, filename: Path) -> None:
60
60
  key : str
61
61
  S3 object key.
62
62
  filename : Path
63
- Path where to save the function source.
63
+ Local path where the downloaded object will be saved.
64
64
 
65
65
  Returns
66
66
  -------
@@ -10,23 +10,35 @@ from digitalhub.stores.credentials.enums import CredsEnvVar
10
10
 
11
11
  class SqlStoreConfigurator(Configurator):
12
12
  """
13
- Configure the store by getting the credentials from user
14
- provided config or from environment.
13
+ SQL store configuration manager for database connections.
14
+
15
+ Handles credential management and configuration for SQL database
16
+ connections. Loads credentials from environment variables or
17
+ configuration files and provides connection string generation
18
+ for database access.
19
+
20
+ Attributes
21
+ ----------
22
+ keys : list[str]
23
+ List of all supported credential keys for SQL connections.
24
+ required_keys : list[str]
25
+ List of mandatory credential keys that must be provided.
15
26
  """
16
27
 
17
28
  keys = [
18
- CredsEnvVar.DB_USERNAME,
19
- CredsEnvVar.DB_PASSWORD,
20
- CredsEnvVar.DB_HOST,
21
- CredsEnvVar.DB_PORT,
22
- CredsEnvVar.DB_DATABASE,
29
+ CredsEnvVar.DB_USERNAME.value,
30
+ CredsEnvVar.DB_PASSWORD.value,
31
+ CredsEnvVar.DB_HOST.value,
32
+ CredsEnvVar.DB_PORT.value,
33
+ CredsEnvVar.DB_DATABASE.value,
34
+ CredsEnvVar.DB_PLATFORM.value,
23
35
  ]
24
36
  required_keys = [
25
- CredsEnvVar.DB_USERNAME,
26
- CredsEnvVar.DB_PASSWORD,
27
- CredsEnvVar.DB_HOST,
28
- CredsEnvVar.DB_PORT,
29
- CredsEnvVar.DB_DATABASE,
37
+ CredsEnvVar.DB_USERNAME.value,
38
+ CredsEnvVar.DB_PASSWORD.value,
39
+ CredsEnvVar.DB_HOST.value,
40
+ CredsEnvVar.DB_PORT.value,
41
+ CredsEnvVar.DB_DATABASE.value,
30
42
  ]
31
43
 
32
44
  def __init__(self):
@@ -37,33 +49,72 @@ class SqlStoreConfigurator(Configurator):
37
49
  # Configuration methods
38
50
  ##############################
39
51
 
40
- def load_configs(self) -> None:
41
- # Load from env
42
- env_creds = {var.value: self._creds_handler.load_from_env(var.value) for var in self.keys}
52
+ def load_env_vars(self) -> None:
53
+ """
54
+ Load database credentials from environment variables.
55
+
56
+ Retrieves SQL database connection credentials from the system
57
+ environment variables and stores them in the configurator's
58
+ credential handler for use in database connections.
59
+
60
+ Returns
61
+ -------
62
+ None
63
+ """
64
+ env_creds = self._creds_handler.load_from_env(self.keys)
43
65
  self._creds_handler.set_credentials(self._env, env_creds)
44
66
 
45
- # Load from file
46
- file_creds = {var.value: self._creds_handler.load_from_file(var.value) for var in self.keys}
67
+ def load_file_vars(self) -> None:
68
+ """
69
+ Load database credentials from configuration file.
70
+
71
+ Retrieves SQL database connection credentials from a
72
+ configuration file and stores them in the configurator's
73
+ credential handler for use in database connections.
74
+
75
+ Returns
76
+ -------
77
+ None
78
+ """
79
+ file_creds = self._creds_handler.load_from_file(self.keys)
47
80
  self._creds_handler.set_credentials(self._file, file_creds)
48
81
 
49
- def get_sql_conn_string(self, origin: str) -> str:
82
+ def get_sql_conn_string(self) -> str:
50
83
  """
51
- Get the connection string from environment variables.
84
+ Generate PostgreSQL connection string from stored credentials.
52
85
 
53
- Parameters
54
- ----------
55
- origin : str
56
- The origin of the credentials.
86
+ Constructs a PostgreSQL connection string using the configured
87
+ database credentials including username, password, host, port,
88
+ and database name.
57
89
 
58
90
  Returns
59
91
  -------
60
92
  str
61
- The connection string.
93
+ A PostgreSQL connection string in the format:
94
+ 'postgresql://username:password@host:port/database'
62
95
  """
63
- creds = self.get_credentials(origin)
96
+ creds = self.get_sql_credentials()
64
97
  user = creds[CredsEnvVar.DB_USERNAME.value]
65
98
  password = creds[CredsEnvVar.DB_PASSWORD.value]
66
99
  host = creds[CredsEnvVar.DB_HOST.value]
67
100
  port = creds[CredsEnvVar.DB_PORT.value]
68
101
  database = creds[CredsEnvVar.DB_DATABASE.value]
69
102
  return f"postgresql://{user}:{password}@{host}:{port}/{database}"
103
+
104
+ def get_sql_credentials(self) -> dict:
105
+ """
106
+ Get all configured database credentials as a dictionary.
107
+
108
+ Retrieves all available database credentials from the configured
109
+ source and returns them as a dictionary with all credential keys
110
+ from self.keys mapped to their values.
111
+
112
+ Returns
113
+ -------
114
+ dict
115
+ Dictionary containing all credential key-value pairs from self.keys.
116
+ Keys correspond to database connection parameters such as
117
+ username, password, host, port, database, and platform.
118
+ """
119
+ creds = self.get_credentials(self._origin)
120
+ return {key: creds.get(key) for key in self.keys}