qwak-core 0.4.272__py3-none-any.whl → 0.4.273__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.
Files changed (62) hide show
  1. frogml_storage/__init__.py +1 -0
  2. frogml_storage/artifactory/__init__.py +1 -0
  3. frogml_storage/artifactory/_artifactory_api.py +315 -0
  4. frogml_storage/authentication/login/__init__.py +1 -0
  5. frogml_storage/authentication/login/_login_cli.py +239 -0
  6. frogml_storage/authentication/login/_login_command.py +74 -0
  7. frogml_storage/authentication/models/__init__.py +3 -0
  8. frogml_storage/authentication/models/_auth.py +24 -0
  9. frogml_storage/authentication/models/_auth_config.py +70 -0
  10. frogml_storage/authentication/models/_login.py +22 -0
  11. frogml_storage/authentication/utils/__init__.py +17 -0
  12. frogml_storage/authentication/utils/_authentication_utils.py +281 -0
  13. frogml_storage/authentication/utils/_login_checks_utils.py +114 -0
  14. frogml_storage/base_storage.py +140 -0
  15. frogml_storage/constants.py +56 -0
  16. frogml_storage/exceptions/checksum_verification_error.py +3 -0
  17. frogml_storage/exceptions/validation_error.py +4 -0
  18. frogml_storage/frog_ml.py +668 -0
  19. frogml_storage/http/__init__.py +1 -0
  20. frogml_storage/http/http_client.py +83 -0
  21. frogml_storage/logging/__init__.py +1 -0
  22. frogml_storage/logging/_log_config.py +45 -0
  23. frogml_storage/logging/log_utils.py +21 -0
  24. frogml_storage/models/__init__.py +1 -0
  25. frogml_storage/models/_download_context.py +54 -0
  26. frogml_storage/models/dataset_manifest.py +13 -0
  27. frogml_storage/models/entity_manifest.py +93 -0
  28. frogml_storage/models/frogml_dataset_version.py +21 -0
  29. frogml_storage/models/frogml_entity_type_info.py +50 -0
  30. frogml_storage/models/frogml_entity_version.py +34 -0
  31. frogml_storage/models/frogml_model_version.py +21 -0
  32. frogml_storage/models/model_manifest.py +60 -0
  33. frogml_storage/models/serialization_metadata.py +15 -0
  34. frogml_storage/utils/__init__.py +12 -0
  35. frogml_storage/utils/_environment.py +21 -0
  36. frogml_storage/utils/_input_checks_utility.py +104 -0
  37. frogml_storage/utils/_storage_utils.py +15 -0
  38. frogml_storage/utils/_url_utils.py +27 -0
  39. qwak/__init__.py +1 -1
  40. qwak/clients/instance_template/client.py +6 -4
  41. qwak/clients/prompt_manager/model_descriptor_mapper.py +21 -19
  42. qwak/feature_store/_common/artifact_utils.py +3 -3
  43. qwak/feature_store/data_sources/base.py +4 -4
  44. qwak/feature_store/data_sources/batch/athena.py +3 -3
  45. qwak/feature_store/feature_sets/streaming.py +3 -3
  46. qwak/feature_store/feature_sets/streaming_backfill.py +1 -1
  47. qwak/feature_store/online/client.py +6 -6
  48. qwak/feature_store/sinks/streaming/factory.py +1 -1
  49. qwak/inner/build_logic/phases/phase_010_fetch_model/fetch_strategy_manager/strategy/git/git_strategy.py +3 -3
  50. qwak/inner/di_configuration/account.py +23 -24
  51. qwak/inner/tool/auth.py +2 -2
  52. qwak/llmops/provider/openai/provider.py +3 -3
  53. qwak/model/tools/adapters/output.py +1 -1
  54. qwak/model/utils/feature_utils.py +12 -8
  55. qwak/model_loggers/artifact_logger.py +7 -7
  56. qwak/tools/logger/logger.py +1 -1
  57. qwak_core-0.4.273.dist-info/METADATA +415 -0
  58. {qwak_core-0.4.272.dist-info → qwak_core-0.4.273.dist-info}/RECORD +59 -23
  59. _qwak_proto/__init__.py +0 -0
  60. _qwak_proto/qwak/__init__.py +0 -0
  61. qwak_core-0.4.272.dist-info/METADATA +0 -53
  62. {qwak_core-0.4.272.dist-info → qwak_core-0.4.273.dist-info}/WHEEL +0 -0
@@ -0,0 +1,83 @@
1
+ import os
2
+ from typing import Optional, Tuple
3
+
4
+ import requests
5
+ from requests.adapters import HTTPAdapter
6
+ from urllib3 import Retry
7
+
8
+ import frogml_storage
9
+ from frogml_storage.logging import logger
10
+
11
+
12
+ class HTTPClient:
13
+
14
+ def __init__(
15
+ self, auth: Tuple[str, str], session: Optional[requests.Session] = None
16
+ ):
17
+ self.auth = auth
18
+ # add default headers
19
+ if session is None:
20
+ self.session = self._create_session()
21
+ self._add_default_headers()
22
+ self.timeout = os.getenv("JFML_TIMEOUT", default=30)
23
+
24
+ @staticmethod
25
+ def _create_session():
26
+ session = requests.Session()
27
+ adapter = HTTPAdapter(
28
+ max_retries=RetryWithLog(
29
+ total=5, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]
30
+ )
31
+ )
32
+ session.mount("http://", adapter)
33
+ session.mount("https://", adapter)
34
+ return session
35
+
36
+ def post(self, url, data=None, params=None):
37
+ return self.session.post(
38
+ url, auth=self.auth, timeout=self.timeout, data=data, params=params
39
+ )
40
+
41
+ def get(self, url, params=None, stream=False):
42
+ return self.session.get(url, auth=self.auth, params=params, stream=stream)
43
+
44
+ def put(self, url, payload=None, files=None, stream=False, headers=None, json=None):
45
+ return self.session.request(
46
+ method="PUT",
47
+ url=url,
48
+ data=payload,
49
+ auth=self.auth,
50
+ files=files,
51
+ stream=stream,
52
+ timeout=self.timeout,
53
+ headers=headers,
54
+ json=json,
55
+ )
56
+
57
+ def delete(self, url):
58
+ return self.session.request(
59
+ method="DELETE",
60
+ url=url,
61
+ auth=self.auth,
62
+ timeout=self.timeout,
63
+ )
64
+
65
+ def head(self, url, params=None, stream=False):
66
+ return self.session.head(url, auth=self.auth, params=params, stream=stream)
67
+
68
+ def _add_default_headers(self):
69
+ self.session.headers.update(
70
+ {"User-Agent": "frogml-sdk-python/{}".format(frogml_storage.__version__)}
71
+ )
72
+
73
+
74
+ class RetryWithLog(Retry):
75
+ """
76
+ Adding extra logs before making a retry request
77
+ """
78
+
79
+ def __init__(self, *args, **kwargs):
80
+ history = kwargs.get("history")
81
+ if history is not None:
82
+ logger.debug(f"Error: ${history[-1].error}\nretrying...")
83
+ super().__init__(*args, **kwargs)
@@ -0,0 +1 @@
1
+ from ._log_config import logger
@@ -0,0 +1,45 @@
1
+ import logging.config
2
+ import os
3
+ import sys
4
+
5
+ log_level = (
6
+ "DEBUG"
7
+ if os.getenv("JFML_DEBUG", "false").casefold() == "true".casefold()
8
+ else "INFO"
9
+ )
10
+ log_file = f'{os.path.expanduser("~")}/.frogml/frogml-log-history.log'
11
+ os.makedirs(os.path.dirname(log_file), exist_ok=True)
12
+
13
+ DEFAULT_LOGGING = {
14
+ "version": 1,
15
+ "formatters": {
16
+ "standard": {
17
+ "format": "%(asctime)s - %(levelname)s - %(name)s.%(module)s.%(funcName)s:%(lineno)d - %(message)s"
18
+ },
19
+ },
20
+ "handlers": {
21
+ "console": {
22
+ "class": "logging.StreamHandler",
23
+ "formatter": "standard",
24
+ "stream": sys.stdout,
25
+ },
26
+ "file": {
27
+ "class": "logging.FileHandler",
28
+ "formatter": "standard",
29
+ "filename": log_file,
30
+ },
31
+ },
32
+ "loggers": {
33
+ __name__: {
34
+ "level": log_level,
35
+ "handlers": ["console", "file"],
36
+ "propagate": False,
37
+ },
38
+ },
39
+ }
40
+
41
+ if os.getenv("IS_LOGGER_SHADED") is not None:
42
+ logger = logging.getLogger(__name__)
43
+ else:
44
+ logging.config.dictConfig(DEFAULT_LOGGING)
45
+ logger = logging.getLogger(__name__)
@@ -0,0 +1,21 @@
1
+ from frogml_storage.models.frogml_entity_type_info import FrogMLEntityTypeInfo
2
+
3
+
4
+ # The following method affect e2e tests.
5
+ def build_download_success_log(
6
+ entity_type_info: FrogMLEntityTypeInfo, entity_name: str, version: str
7
+ ) -> str:
8
+ return (
9
+ f'{entity_type_info.entity_type.capitalize()}: "{entity_name}", version: "{version}"'
10
+ f" has been downloaded successfully"
11
+ )
12
+
13
+
14
+ # The following method affect e2e tests.
15
+ def build_upload_success_log(
16
+ entity_type_info: FrogMLEntityTypeInfo, entity_name: str, version: str
17
+ ) -> str:
18
+ return (
19
+ f'{entity_type_info.entity_type.capitalize()}: "{entity_name}", version: "{version}"'
20
+ f" has been uploaded successfully"
21
+ )
@@ -0,0 +1 @@
1
+ from ._download_context import DownloadContext
@@ -0,0 +1,54 @@
1
+ from typing import Optional
2
+
3
+ from frogml_storage.models.entity_manifest import Checksums
4
+
5
+
6
+ class DownloadContext(object):
7
+ """
8
+ A class to represent the arguments for a download operation.
9
+
10
+ Attributes
11
+ ----------
12
+ repo_key : str
13
+ The key of the repository where the artifact is located.
14
+ source_url : str
15
+ The source relative URL of the artifact, relative to artifactory url and the repo key.
16
+ target_path : str
17
+ The target path where the artifact will be downloaded to.
18
+ exists_locally : bool
19
+ A flag indicating whether the artifact already exists locally in the target path.
20
+ artifact_checksum: Checksums
21
+ The checksum of the artifact.
22
+ """
23
+
24
+ repo_key: str
25
+ source_url: str
26
+ target_path: str
27
+ exists_locally: bool = False
28
+ artifact_checksum: Optional[Checksums]
29
+
30
+ def __init__(
31
+ self,
32
+ repo_key: str,
33
+ source_url: str,
34
+ target_path: str,
35
+ exists_locally: bool = False,
36
+ artifact_checksum: Optional[Checksums] = None,
37
+ ):
38
+ self.repo_key = repo_key
39
+ self.source_url = source_url
40
+ self.target_path = target_path
41
+ self.exists_locally = exists_locally
42
+ self.artifact_checksum = artifact_checksum
43
+
44
+ def __eq__(self, other):
45
+ if not isinstance(other, DownloadContext):
46
+ return False
47
+
48
+ return (
49
+ self.repo_key == other.repo_key
50
+ and self.source_url == other.source_url
51
+ and self.target_path == other.target_path
52
+ and self.exists_locally == other.exists_locally
53
+ and self.artifact_checksum == other.artifact_checksum
54
+ )
@@ -0,0 +1,13 @@
1
+ from typing import List
2
+
3
+ from pydantic import Field
4
+
5
+ from frogml_storage.models.entity_manifest import Artifact, EntityManifest
6
+
7
+
8
+ class DatasetManifest(EntityManifest):
9
+ """
10
+ Represent a dataset manifest file
11
+ """
12
+
13
+ artifacts: List[Artifact] = Field(serialization_alias="dataset_artifacts")
@@ -0,0 +1,93 @@
1
+ import os
2
+ from abc import ABC
3
+ from typing import List, Optional
4
+
5
+ from pydantic import BaseModel
6
+
7
+ from frogml_storage.utils import calc_content_sha2, calculate_sha2
8
+
9
+
10
+ class Checksums(BaseModel):
11
+ sha2: str
12
+
13
+ @classmethod
14
+ def calc_checksums(cls, file_path: str) -> "Checksums":
15
+ return cls(sha2=calculate_sha2(file_path))
16
+
17
+ @classmethod
18
+ def calc_content_checksums(cls, content: str) -> "Checksums":
19
+ return cls(sha2=calc_content_sha2(content))
20
+
21
+
22
+ class Artifact(BaseModel):
23
+ artifact_path: str
24
+ size: int
25
+ checksums: Checksums
26
+
27
+ def __eq__(self, other):
28
+ if not isinstance(other, Artifact):
29
+ return False
30
+ return (
31
+ self.artifact_path == other.artifact_path
32
+ and self.size == other.size
33
+ and self.checksums == other.checksums
34
+ )
35
+
36
+
37
+ class EntityManifest(BaseModel, ABC):
38
+ """
39
+ Represent an entity manifest file
40
+
41
+ Attributes:
42
+ created_date: The date the model | dataset were uploaded to Artifactory
43
+ artifacts: A list of artifacts that belong to the model | dataset
44
+ id: <organization>/<entity_name> - exists only for downloaded EntityInfo
45
+ version: The entity version - exists only for downloaded EntityInfo
46
+ """
47
+
48
+ created_date: str
49
+ artifacts: List[Artifact]
50
+ id: Optional[str] = None
51
+ version: Optional[str] = None
52
+
53
+ def add_file(self, file_path: str, checksums: Checksums, rel_path: str) -> None:
54
+ self.artifacts.append(
55
+ Artifact(
56
+ artifact_path=rel_path,
57
+ size=os.path.getsize(file_path),
58
+ checksums=checksums,
59
+ )
60
+ )
61
+
62
+ def add_content_file(self, rel_path: str, content: str) -> None:
63
+ checksums = Checksums.calc_content_checksums(content)
64
+ self.artifacts.append(
65
+ Artifact(
66
+ artifact_path=rel_path,
67
+ size=len((content, "utf-8")),
68
+ checksums=checksums,
69
+ )
70
+ )
71
+
72
+ @classmethod
73
+ def from_json(cls, json_str: str) -> "EntityManifest":
74
+ return cls.model_validate_json(json_str)
75
+
76
+ def to_json(self) -> str:
77
+ return self.model_dump_json(by_alias=True, exclude_none=True)
78
+
79
+ def __eq__(self, other):
80
+ if not isinstance(other, EntityManifest):
81
+ return False
82
+ if self.id != other.id:
83
+ return False
84
+ if self.version != other.version:
85
+ return False
86
+ if self.created_date != other.created_date:
87
+ return False
88
+ if len(self.artifacts) != len(other.artifacts):
89
+ return False
90
+ for self_artifact, other_artifact in zip(self.artifacts, other.artifacts):
91
+ if self_artifact != other_artifact:
92
+ return False
93
+ return True
@@ -0,0 +1,21 @@
1
+ from frogml_storage.models.frogml_entity_version import FrogMLEntityVersion
2
+
3
+
4
+ class FrogMLDatasetVersion(FrogMLEntityVersion):
5
+ """
6
+ Represent metadata of an uploaded dataset version.
7
+
8
+ Inherits:
9
+ FrogMLEntityVersion: Base class for entity versions.
10
+ """
11
+
12
+ @classmethod
13
+ def from_entity_version(
14
+ cls, entity_version: FrogMLEntityVersion
15
+ ) -> "FrogMLDatasetVersion":
16
+ return cls(
17
+ entity_name=entity_version.entity_name,
18
+ version=entity_version.version,
19
+ namespace=entity_version.namespace,
20
+ entity_manifest=entity_version.entity_manifest,
21
+ )
@@ -0,0 +1,50 @@
1
+ from enum import Enum
2
+
3
+ from typing_extensions import Self
4
+
5
+ from frogml_storage.constants import (
6
+ BODY_PART_DATASET_MANIFEST_STREAM,
7
+ BODY_PART_MODEL_MANIFEST_STREAM,
8
+ DATASET_METADATA_FILE_NAME,
9
+ DATASET_UI_DIRECTORY,
10
+ MODEL_METADATA_FILE_NAME,
11
+ MODEL_UI_DIRECTORY,
12
+ ROOT_FROGML_DATASET_UI_DIRECTORY,
13
+ ROOT_FROGML_MODEL_UI_DIRECTORY,
14
+ )
15
+
16
+
17
+ # noinspection PyEnum
18
+ class FrogMLEntityTypeInfo(Enum):
19
+ MODEL = (
20
+ MODEL_UI_DIRECTORY,
21
+ ROOT_FROGML_MODEL_UI_DIRECTORY,
22
+ MODEL_METADATA_FILE_NAME,
23
+ BODY_PART_MODEL_MANIFEST_STREAM,
24
+ )
25
+ DATASET = (
26
+ DATASET_UI_DIRECTORY,
27
+ ROOT_FROGML_DATASET_UI_DIRECTORY,
28
+ DATASET_METADATA_FILE_NAME,
29
+ BODY_PART_DATASET_MANIFEST_STREAM,
30
+ )
31
+
32
+ def __init__(
33
+ self: Self,
34
+ entity_type: str,
35
+ folder_name: str,
36
+ metadata_file_name: str,
37
+ body_part_stream: str,
38
+ ):
39
+ self.entity_type: str = entity_type
40
+ self.folder_name: str = folder_name
41
+ self.metadata_file_name: str = metadata_file_name
42
+ self.body_part_stream: str = body_part_stream
43
+
44
+ @classmethod
45
+ def from_string(cls, entity_type_string: str) -> "FrogMLEntityTypeInfo":
46
+ for entity_type in cls:
47
+ if entity_type.entity_type.lower() == entity_type_string.lower():
48
+ return entity_type
49
+
50
+ raise ValueError(f"No enum constant found for entityType: {entity_type_string}")
@@ -0,0 +1,34 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from frogml_storage.models.entity_manifest import EntityManifest
6
+
7
+
8
+ class FrogMLEntityVersion(BaseModel):
9
+ """
10
+ Represent a metadata of uploaded entity
11
+
12
+ Attributes:
13
+ entity_name: The entity name (model | dataset)
14
+ namespace: The namespace of the model | dataset
15
+ version: The version of the model | dataset
16
+ entity_manifest: The entity manifest file
17
+ """
18
+
19
+ entity_name: str
20
+ namespace: Optional[str] = None
21
+ version: str
22
+ entity_manifest: Optional[EntityManifest] = None
23
+
24
+ def __eq__(self, other):
25
+ if not isinstance(other, FrogMLEntityVersion):
26
+ return False
27
+ if self.version != other.version:
28
+ return False
29
+ if self.namespace != other.namespace:
30
+ return False
31
+ if self.entity_name != other.entity_name:
32
+ return False
33
+
34
+ return True
@@ -0,0 +1,21 @@
1
+ from frogml_storage.models.frogml_entity_version import FrogMLEntityVersion
2
+
3
+
4
+ class FrogMLModelVersion(FrogMLEntityVersion):
5
+ """
6
+ Represent metadata of an uploaded model version.
7
+
8
+ Inherits:
9
+ FrogMLEntityVersion: Base class for entity versions.
10
+ """
11
+
12
+ @classmethod
13
+ def from_entity_version(
14
+ cls, entity_version: FrogMLEntityVersion
15
+ ) -> "FrogMLModelVersion":
16
+ return cls(
17
+ entity_name=entity_version.entity_name,
18
+ version=entity_version.version,
19
+ namespace=entity_version.namespace,
20
+ entity_manifest=entity_version.entity_manifest,
21
+ )
@@ -0,0 +1,60 @@
1
+ import os
2
+ from typing import List, Optional
3
+
4
+ from pydantic import ConfigDict, Field
5
+
6
+ from frogml_storage.models.entity_manifest import Artifact, Checksums, EntityManifest
7
+ from frogml_storage.models.serialization_metadata import SerializationMetadata
8
+
9
+
10
+ class ModelManifest(EntityManifest):
11
+ """
12
+ Represent a model manifest file
13
+
14
+ Attributes:
15
+ model_format: If the entity is model, holds model format information
16
+ dependency_artifacts: If the entity is model, holds a list of files specifying the model dependencies
17
+ code_artifacts: If the entity is model, specifies the archive file artifact
18
+ """
19
+
20
+ artifacts: List[Artifact] = Field(serialization_alias="model_artifacts")
21
+
22
+ model_format: SerializationMetadata
23
+ dependency_artifacts: Optional[List[Artifact]] = None
24
+ code_artifacts: Optional[Artifact] = None
25
+
26
+ # suppress warning on model_format field name.
27
+ # if one day it collides with pydantic field, it will throw an error.
28
+ model_config = ConfigDict(protected_namespaces=())
29
+
30
+ def add_dependency_file(
31
+ self, file_path: str, checksums: Checksums, rel_path: str
32
+ ) -> None:
33
+ if self.dependency_artifacts is None:
34
+ self.dependency_artifacts = []
35
+ self.dependency_artifacts.append(
36
+ Artifact(
37
+ artifact_path=rel_path,
38
+ size=os.path.getsize(file_path),
39
+ checksums=checksums,
40
+ )
41
+ )
42
+
43
+ def __eq__(self, other):
44
+ if not super.__eq__(self, other):
45
+ return False
46
+ if self.model_format != other.model_format:
47
+ return False
48
+ if self.dependency_artifacts != other.dependency_artifacts:
49
+ return False
50
+ if self.dependency_artifacts is not None:
51
+ if len(self.dependency_artifacts) != len(other.dependency_artifacts):
52
+ return False
53
+ for self_artifact, other_artifact in zip(
54
+ self.dependency_artifacts, other.dependency_artifacts
55
+ ):
56
+ if self_artifact != other_artifact:
57
+ return False
58
+ if self.code_artifacts != other.code_artifacts:
59
+ return False
60
+ return True
@@ -0,0 +1,15 @@
1
+ from typing import Dict
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class SerializationMetadata(BaseModel):
7
+ framework: str
8
+ framework_version: str
9
+ serialization_format: str
10
+ runtime: str
11
+ runtime_version: str
12
+
13
+ @classmethod
14
+ def from_json(cls, json_dict: Dict) -> "SerializationMetadata":
15
+ return cls.model_validate(json_dict)
@@ -0,0 +1,12 @@
1
+ from ._input_checks_utility import (
2
+ is_not_none,
3
+ is_valid_thread_number,
4
+ user_input_validation,
5
+ validate_not_folder_paths,
6
+ validate_path_exists,
7
+ )
8
+ from ._storage_utils import calculate_sha2, calc_content_sha2
9
+ from ._url_utils import (
10
+ assemble_artifact_url,
11
+ join_url,
12
+ )
@@ -0,0 +1,21 @@
1
+ import importlib.metadata
2
+ import platform
3
+ from typing import List
4
+
5
+
6
+ def get_environment_dependencies() -> List[str]:
7
+ distributions = importlib.metadata.distributions()
8
+ return sorted(
9
+ [f"{dist.metadata['Name']}=={dist.version}" for dist in distributions]
10
+ )
11
+
12
+
13
+ def get_environment_details() -> List[str]:
14
+ return [
15
+ f"arch={platform.architecture()[0]}",
16
+ f"cpu={platform.processor()}",
17
+ f"platform={platform.platform()}",
18
+ f"python_version={platform.python_version()}",
19
+ f"python_implementation={platform.python_implementation()}",
20
+ f"python_compiler={platform.python_compiler()}",
21
+ ]
@@ -0,0 +1,104 @@
1
+ import os
2
+ import re
3
+ from typing import List, Optional, Union
4
+
5
+ from frogml_storage.logging import logger
6
+ from frogml_storage.constants import FROG_ML_MAX_CHARS_FOR_NAME
7
+ from frogml_storage.exceptions.validation_error import FrogMLValidationError
8
+
9
+ valid_characters_pattern = re.compile(r"^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*$")
10
+
11
+
12
+ def user_input_validation(
13
+ entity_name: Optional[str],
14
+ namespace: Optional[str],
15
+ version: Optional[str],
16
+ properties: Optional[dict[str, str]] = None,
17
+ ) -> bool:
18
+ for arg_name, arg_value in {
19
+ "entity_name": entity_name,
20
+ "namespace": namespace,
21
+ "version": version,
22
+ }.items():
23
+ if arg_value is not None:
24
+ __input_validation(arg_value, arg_name)
25
+
26
+ if properties is not None:
27
+ __user_input_dict_validation(properties)
28
+
29
+ return True
30
+
31
+
32
+ def __user_input_dict_validation(properties: dict[str, str]) -> bool:
33
+ if properties is not None:
34
+ for key, value in properties.items():
35
+ __input_validation(key, "properties")
36
+ __input_validation(value, "properties")
37
+ return True
38
+
39
+
40
+ def is_not_none(arg_name: str, arg_value: Optional[object]) -> bool:
41
+ if arg_value is None:
42
+ raise FrogMLValidationError("{} can't be 'None'.".format(arg_name))
43
+ return True
44
+
45
+
46
+ def __input_validation(field_value: str, field_name: str) -> bool:
47
+ if len(str(field_value)) > FROG_ML_MAX_CHARS_FOR_NAME:
48
+ raise FrogMLValidationError(
49
+ "Max length for {} is 60 characters.".format(field_name.capitalize())
50
+ )
51
+
52
+ if not field_value or not re.match(valid_characters_pattern, str(field_value)):
53
+ raise FrogMLValidationError(
54
+ "Invalid characters detected at {}: {}".format(
55
+ field_name.capitalize(), field_value
56
+ )
57
+ )
58
+
59
+ return True
60
+
61
+
62
+ def is_valid_thread_number(thread_count: str) -> bool:
63
+ try:
64
+ int_thread_count = int(thread_count)
65
+ cpu_count = os.cpu_count()
66
+ if int_thread_count <= 0 or (
67
+ cpu_count is not None and int_thread_count >= cpu_count
68
+ ):
69
+ raise ValueError(
70
+ "Invalid thread count: {}. The default value will be used.".format(
71
+ thread_count
72
+ )
73
+ )
74
+ return True
75
+ except ValueError as e:
76
+ logger.warning("Thread count {}: {}".format(thread_count, e))
77
+ return False
78
+ except TypeError:
79
+ logger.debug("Thread count not configured. The default value will be used.")
80
+ return False
81
+
82
+
83
+ def validate_not_folder_paths(paths: Union[Optional[List[str]], Optional[str]]) -> bool:
84
+ if paths is not None:
85
+ if isinstance(paths, List):
86
+ for path in paths:
87
+ __validate_not_folder_path(path)
88
+ else:
89
+ __validate_not_folder_path(paths)
90
+ return True
91
+
92
+
93
+ def __validate_not_folder_path(path: str) -> bool:
94
+ if os.path.isdir(path):
95
+ raise FrogMLValidationError(
96
+ "file '{}' must be a file, but is a directory.".format(path)
97
+ )
98
+ return True
99
+
100
+
101
+ def validate_path_exists(path: str) -> bool:
102
+ if not os.path.exists(path):
103
+ raise ValueError(f"Provided path does not exists : '{path}'")
104
+ return True
@@ -0,0 +1,15 @@
1
+ import hashlib
2
+
3
+
4
+ def calculate_sha2(path: str) -> str:
5
+ sha256 = hashlib.sha256()
6
+ with open(path, "rb") as f:
7
+ while chunk := f.read(8192):
8
+ sha256.update(chunk)
9
+ return sha256.hexdigest()
10
+
11
+
12
+ def calc_content_sha2(content: str) -> str:
13
+ sha256 = hashlib.sha256()
14
+ sha256.update(content.encode("utf-8"))
15
+ return sha256.hexdigest()