diff --git a/kgforge/core/archetypes/mapping.py b/kgforge/core/archetypes/mapping.py index 5ff63344..f78a66cb 100644 --- a/kgforge/core/archetypes/mapping.py +++ b/kgforge/core/archetypes/mapping.py @@ -87,9 +87,9 @@ def load_file(cls, filepath, raise_ex=True): raise OSError - except OSError: + except OSError as e: if raise_ex: - raise FileNotFoundError + raise FileNotFoundError from e return None @classmethod diff --git a/kgforge/core/commons/context.py b/kgforge/core/commons/context.py index 532e2092..b9631f6f 100644 --- a/kgforge/core/commons/context.py +++ b/kgforge/core/commons/context.py @@ -45,8 +45,8 @@ def __init__(self, document: Union[Dict, List, str], iri: Optional[str] = None) elif isinstance(document, str): try: self.document = source_to_json(document) - except Exception: - raise ValueError("context not resolvable") + except Exception as e: + raise ValueError("context not resolvable") from e elif isinstance(document, Dict): self.document = document if "@context" in document else {"@context": document} self.iri = iri diff --git a/kgforge/core/commons/exceptions.py b/kgforge/core/commons/exceptions.py index 4eccc83c..f6b9aae6 100644 --- a/kgforge/core/commons/exceptions.py +++ b/kgforge/core/commons/exceptions.py @@ -40,37 +40,41 @@ class ResolvingError(Exception): # Store operations. -class RegistrationError(Exception): +class RunException(Exception): pass -class UploadingError(Exception): +class RegistrationError(RunException): pass -class RetrievalError(Exception): +class UploadingError(RunException): pass -class DownloadingError(Exception): +class RetrievalError(RunException): pass -class UpdatingError(Exception): +class DownloadingError(RunException): pass -class TaggingError(Exception): +class UpdatingError(RunException): pass -class DeprecationError(Exception): +class TaggingError(RunException): pass -class QueryingError(Exception): +class DeprecationError(RunException): pass -class FreezingError(Exception): +class QueryingError(RunException): + pass + + +class FreezingError(RunException): pass diff --git a/kgforge/core/commons/execution.py b/kgforge/core/commons/execution.py index db1edea9..fe4300e4 100644 --- a/kgforge/core/commons/execution.py +++ b/kgforge/core/commons/execution.py @@ -15,12 +15,12 @@ import inspect import traceback from functools import wraps -from typing import Any, Callable, List, Optional, Tuple, Union +from typing import Any, Callable, List, Optional, Tuple, Union, Type from kgforge.core.resource import Resource from kgforge.core.commons.actions import (Action, Actions, collect_lazy_actions, execute_lazy_actions) -from kgforge.core.commons.exceptions import NotSupportedError +from kgforge.core.commons.exceptions import NotSupportedError, RunException # POLICY Should have only one function called 'wrapper'. See catch(). @@ -98,7 +98,7 @@ def dispatch(data: Union[Resource, List[Resource]], fun_many: Callable, def run(fun_one: Callable, fun_many: Optional[Callable], data: Union[Resource, List[Resource]], - exception: Callable, id_required: bool = False, + exception: Type[RunException], id_required: bool = False, required_synchronized: Optional[bool] = None, execute_actions: bool = False, monitored_status: Optional[str] = None, catch_exceptions: bool = True, **kwargs) -> None: # POLICY Should be called for operations on resources where recovering from errors is needed. @@ -124,7 +124,7 @@ def _run_many(fun: Callable, resources: List[Resource], *args, **kwargs) -> None _run_one(fun, x, *args, **kwargs) -def _run_one(fun: Callable, resource: Resource, exception: Callable, id_required: bool, +def _run_one(fun: Callable, resource: Resource, exception: Type[RunException], id_required: bool, required_synchronized: Optional[bool], execute_actions: bool, monitored_status: Optional[str], catch_exceptions: bool, **kwargs) -> None: try: @@ -155,7 +155,7 @@ def _run_one(fun: Callable, resource: Resource, exception: Callable, id_required if monitored_status: setattr(resource, monitored_status, status) - resource._last_action = Action(fun.__name__, succeeded, exception) + resource.set_last_action(Action(fun.__name__, succeeded, exception)) if not catch_exceptions and exception: raise exception diff --git a/kgforge/core/commons/files.py b/kgforge/core/commons/files.py index 17506a83..fd38f349 100644 --- a/kgforge/core/commons/files.py +++ b/kgforge/core/commons/files.py @@ -29,7 +29,10 @@ def load_file_as_byte(source: str): response.raise_for_status() data = response.content except RequestException as re: - raise AttributeError(f"Failed to load the configuration from {source}. The provided source is not a valid file path or URL: {str(re)}") + raise AttributeError( + f"Failed to load the configuration from {source}. " + f"The provided source is not a valid file path or URL: {str(re)}" + ) from re return data diff --git a/kgforge/core/commons/imports.py b/kgforge/core/commons/imports.py index c0f4b815..888d5168 100644 --- a/kgforge/core/commons/imports.py +++ b/kgforge/core/commons/imports.py @@ -32,9 +32,10 @@ def import_class(configuration: str, forge_module_name: str) -> Callable: class_name, module_import = matched.groups(default=forge_module_import) module = import_module(module_import) return getattr(module, class_name) - except ModuleNotFoundError: - raise ConfigurationError(f"{archetype} module not found for '{configuration}'") - except AttributeError: - raise ConfigurationError(f"{archetype} class not found for '{configuration}'") + except ModuleNotFoundError as exc1: + raise ConfigurationError(f"{archetype} module not found for '{configuration}'") from \ + exc1 + except AttributeError as exc2: + raise ConfigurationError(f"{archetype} class not found for '{configuration}'") from exc2 else: raise ConfigurationError(f"incorrect {archetype} configuration for '{configuration}'") diff --git a/kgforge/core/commons/sparql_query_builder.py b/kgforge/core/commons/sparql_query_builder.py index 7cf7ee1c..64337d5d 100644 --- a/kgforge/core/commons/sparql_query_builder.py +++ b/kgforge/core/commons/sparql_query_builder.py @@ -119,7 +119,10 @@ def build( f"FILTER(?v{index} {sparql_operator_map[f.operator]} {_box_value_as_full_iri(value)})" ) except NotImplementedError as nie: - raise ValueError(f"Operator '{sparql_operator_map[f.operator]}' is not supported with the value '{f.value}': {str(nie)}") + raise ValueError( + f"Operator '{sparql_operator_map[f.operator]}' " + f"is not supported with the value '{f.value}': {str(nie)}" + ) from nie return statements, sparql_filters @staticmethod diff --git a/kgforge/core/conversions/rdf.py b/kgforge/core/conversions/rdf.py index 357822b6..98dbe8e8 100644 --- a/kgforge/core/conversions/rdf.py +++ b/kgforge/core/conversions/rdf.py @@ -14,7 +14,7 @@ from copy import deepcopy -from typing import Union, Dict, List, Tuple, Optional, Callable, Any +from typing import Union, Dict, List, Tuple, Optional, Callable from enum import Enum import json @@ -69,9 +69,9 @@ def as_jsonld( ) -> Union[Dict, List[Dict]]: try: valid_form = Form(form.lower()) - except ValueError: + except ValueError as e: supported_forms = tuple(item.value for item in Form) - raise NotSupportedError(f"supported serialization forms are {supported_forms}") + raise NotSupportedError(f"supported serialization forms are {supported_forms}") from e return dispatch( data, @@ -163,12 +163,12 @@ def _from_jsonld_one(data: Dict) -> Resource: if "@context" in data: try: resolved_context = Context(data["@context"]) - except URLError: - raise ValueError("context not resolvable") - else: - return _remove_ld_keys(data, resolved_context) - else: - raise NotImplementedError("not implemented yet (expanded json-ld)") + except URLError as e: + raise ValueError("context not resolvable") from e + + return _remove_ld_keys(data, resolved_context) + + raise NotImplementedError("not implemented yet (expanded json-ld)") def _as_jsonld_many( @@ -225,7 +225,7 @@ def _as_jsonld_one( resource, store_metadata, context, metadata_context ) except Exception as e: - raise ValueError(e) + raise ValueError(e) from e if store_metadata is True and len(metadata_graph) > 0: metadata_expanded = json.loads(metadata_graph.serialize(format="json-ld")) @@ -335,8 +335,8 @@ def _dicts_to_graph( metadata["@context"] = metadata_context.document["@context"] try: meta_data_graph.parse(data=json.dumps(metadata), format="json-ld") - except Exception: - raise ValueError("generated an invalid json-ld") + except Exception as e: + raise ValueError("generated an invalid json-ld") from e return graph, meta_data_graph @@ -380,8 +380,8 @@ def _resource_context( except (HTTPError, URLError, NotSupportedError): try: context = Context(resource.context, iri) - except URLError: - raise ValueError(f"{resource.context} is not resolvable") + except URLError as e: + raise ValueError(f"{resource.context} is not resolvable") from e else: context = model_context diff --git a/kgforge/core/forge.py b/kgforge/core/forge.py index ac39b5ab..91718dd6 100644 --- a/kgforge/core/forge.py +++ b/kgforge/core/forge.py @@ -441,12 +441,12 @@ def resolve( if isinstance(strategy, ResolvingStrategy) else ResolvingStrategy[strategy] ) - except Exception: + except Exception as e: raise AttributeError( f"Invalid ResolvingStrategy value '{strategy}'. " f"Allowed names are {[name for name, member in ResolvingStrategy.__members__.items()]} " f"and allowed members are {[member for name, member in ResolvingStrategy.__members__.items()]}" - ) + ) from e return rov.resolve( text, target, @@ -491,7 +491,7 @@ def format(self, what: str = None, *args, formatter: Union[Formatter, str] = For f"Invalid Formatter value '{formatter}'. " f"Allowed names are {[name for name, member in Formatter.__members__.items()]} " f"and allowed members are {[member for name, member in Formatter.__members__.items()]}" - ) + ) from e if formatter == Formatter.STR: if what is None: diff --git a/kgforge/core/reshaping.py b/kgforge/core/reshaping.py index ff940018..bf1962ed 100644 --- a/kgforge/core/reshaping.py +++ b/kgforge/core/reshaping.py @@ -99,4 +99,6 @@ def _collect(things: List) -> Iterator[str]: prepared = jsoned if isinstance(jsoned, List) else [jsoned] return list(_collect(prepared)) except Exception as e: - raise exception(f"An error occur when collecting values for path to follow '{follow}': {str(e)}") + raise exception( + f"An error occur when collecting values for path to follow '{follow}': {str(e)}" + ) from e diff --git a/kgforge/core/wrappings/paths.py b/kgforge/core/wrappings/paths.py index fec58995..c2ff8f28 100644 --- a/kgforge/core/wrappings/paths.py +++ b/kgforge/core/wrappings/paths.py @@ -33,10 +33,10 @@ def __init__(self, path: List[str], operator: Union[str, FilterOperator], value: try: self.operator: str = operator.value if isinstance(operator, FilterOperator) \ else FilterOperator(operator).value - except Exception: + except Exception as e: raise ValueError( f"Invalid operator value '{operator}'. Allowed operators are {[member.value for name, member in FilterOperator.__members__.items()]}" - ) + ) from e self.value: Any = value def __eq__(self, other): diff --git a/kgforge/specializations/models/rdf/directory_service.py b/kgforge/specializations/models/rdf/directory_service.py index 789c8e9a..4452f24a 100644 --- a/kgforge/specializations/models/rdf/directory_service.py +++ b/kgforge/specializations/models/rdf/directory_service.py @@ -53,7 +53,7 @@ def resolve_context(self, iri: str) -> Dict: try: context = Context(iri) except FileNotFoundError as e: - raise ValueError(e) + raise ValueError(e) from e self._context_cache.update({iri: context.document}) return context.document diff --git a/kgforge/specializations/models/rdf/service.py b/kgforge/specializations/models/rdf/service.py index b984ad2f..1dcce746 100644 --- a/kgforge/specializations/models/rdf/service.py +++ b/kgforge/specializations/models/rdf/service.py @@ -158,14 +158,16 @@ def materialize(self, iri: URIRef) -> NodeProperties: def validate(self, resource: Resource, type_: str): try: if isinstance(resource.type, list) and type_ is None: - raise ValueError("Resource has list of types as attribute and type_ parameter is not specified. " - "Please provide a type_ parameter to validate against it.") + raise ValueError( + "Resource has list of types as attribute and type_ parameter is not specified. " + "Please provide a type_ parameter to validate against it." + ) if type_ is None: shape_iri = self.types_to_shapes[resource.type] else: shape_iri = self.types_to_shapes[type_] - except AttributeError: - raise TypeError("resource requires a type attribute") + except AttributeError as exc: + raise TypeError("Resource requires a type attribute") from exc data_graph = as_graph(resource, False, self.context, None, None) return self._validate(shape_iri, data_graph) diff --git a/kgforge/specializations/models/rdf_model.py b/kgforge/specializations/models/rdf_model.py index fa9052e6..828d6e2e 100644 --- a/kgforge/specializations/models/rdf_model.py +++ b/kgforge/specializations/models/rdf_model.py @@ -93,8 +93,8 @@ def _generate_context(self) -> Context: def _template(self, type: str, only_required: bool) -> Dict: try: uri = self.service.types_to_shapes[type] - except KeyError: - raise ValueError("type '" + type + "' not found in " + self.source) + except KeyError as exc: + raise ValueError("type '" + type + "' not found in " + self.source) from exc node_properties = self.service.materialize(uri) dictionary = parse_attributes(node_properties, only_required, None) return dictionary @@ -105,8 +105,8 @@ def schema_id(self, type: str) -> str: try: shape_iri = self.service.types_to_shapes[type] return str(self.service.schema_source_id(shape_iri)) - except KeyError: - raise ValueError("type not found") + except KeyError as exc: + raise ValueError("type not found") from exc def validate(self, data: Union[Resource, List[Resource]], execute_actions_before: bool, type_: str) -> None: run(self._validate_one, self._validate_many, data, execute_actions=execute_actions_before, diff --git a/kgforge/specializations/resources/datasets.py b/kgforge/specializations/resources/datasets.py index 40e521af..909b7181 100644 --- a/kgforge/specializations/resources/datasets.py +++ b/kgforge/specializations/resources/datasets.py @@ -103,10 +103,13 @@ def _add_prov_property(self, resource, prov_type, reference_property, reference_ reference = self._forge.reshape(resource, keep, versioned) except AttributeError as ae: if '_rev' in str(ae) and versioned: - raise ValueError(f"Missing resource revision value to build a versioned ({versioned}) reference. " - f"Provide a revision number to the resource (by registering it for example) or set 'versioned' argument to False if no versioned reference is needed.") - else: - raise ae + raise ValueError( + f"Missing resource revision value to build a versioned ({versioned}) reference. " + f"Provide a revision number to the resource (by registering it for example) " + f"or set 'versioned' argument to False if no versioned reference is needed." + ) from ae + + raise ae result = Resource(type=prov_type, **kwargs) result.__setattr__(reference_property, reference) diff --git a/kgforge/specializations/stores/bluebrain_nexus.py b/kgforge/specializations/stores/bluebrain_nexus.py index 12b398af..97f1ec1d 100644 --- a/kgforge/specializations/stores/bluebrain_nexus.py +++ b/kgforge/specializations/stores/bluebrain_nexus.py @@ -228,7 +228,7 @@ def _register_one(self, resource: Resource, schema_id: str) -> None: response.raise_for_status() except nexus.HTTPError as e: - raise RegistrationError(_error_message(e)) + raise RegistrationError(_error_message(e)) from e response_json = response.json() resource.id = response_json["@id"] @@ -284,7 +284,7 @@ def _upload_one(self, path: Path, content_type: str) -> Dict: self.organisation, self.project, file, content_type=mime_type ) except HTTPError as e: - raise UploadingError(_error_message(e)) + raise UploadingError(_error_message(e)) from e return response @@ -365,9 +365,9 @@ def retrieve( ) response.raise_for_status() except HTTPError as e: - raise RetrievalError(_error_message(e)) + raise RetrievalError(_error_message(e)) from e else: - raise RetrievalError(_error_message(er)) + raise RetrievalError(_error_message(er)) from er finally: if retrieve_source and not cross_bucket: @@ -388,7 +388,7 @@ def retrieve( data = response.json() resource = self.service.to_resource(data) except Exception as e: - raise ValueError(e) + raise ValueError(e) from e try: if retrieve_source and not cross_bucket: @@ -399,7 +399,7 @@ def retrieve( self.service.synchronize_resource( resource, data, self.retrieve.__name__, False, False ) - raise ValueError(e) + raise ValueError(e) from e finally: self.service.synchronize_resource( @@ -414,7 +414,7 @@ def _retrieve_filename(self, id_: str) -> Tuple[str, str]: metadata = response.json() return metadata["_filename"], metadata["_mediaType"] except HTTPError as e: - raise DownloadingError(_error_message(e)) + raise DownloadingError(_error_message(e)) from e def _download_many( self, @@ -451,7 +451,7 @@ async def _download(url, path, store_metadata, bucket, semaphore, session): except Exception as e: raise DownloadingError( f"Downloading url {url} from bucket {bucket} failed: {_error_message(e)}" - ) + ) from e with open(path, "wb") as f: data = await response.read() f.write(data) @@ -482,7 +482,7 @@ def _download_one( except Exception as e: raise DownloadingError( f"Downloading from bucket {bucket} failed: {_error_message(e)}" - ) + ) from e with open(path, "wb") as f: for chunk in response.iter_content(chunk_size=4096): @@ -579,7 +579,7 @@ def _update_one(self, resource: Resource, schema_id: str) -> None: ) response.raise_for_status() except HTTPError as e: - raise UpdatingError(_error_message(e)) + raise UpdatingError(_error_message(e)) from e self.service.sync_metadata(resource, response.json()) @@ -627,7 +627,7 @@ def _tag_one(self, resource: Resource, value: str) -> None: ) response.raise_for_status() except HTTPError as e: - raise TaggingError(_error_message(e)) + raise TaggingError(_error_message(e)) from e self.service.sync_metadata(resource, response.json()) @@ -674,7 +674,7 @@ def _deprecate_one(self, resource: Resource) -> None: ) response.raise_for_status() except HTTPError as e: - raise DeprecationError(_error_message(e)) + raise DeprecationError(_error_message(e)) from e self.service.sync_metadata(resource, response.json()) @@ -786,7 +786,7 @@ def search( self.service.synchronize_resource( resource, store_metadata_response, self.search.__name__, False, False ) - raise ValueError(e) + raise ValueError(e) from e finally: self.service.synchronize_resource( resource, store_metadata_response, self.search.__name__, True, False @@ -868,7 +868,7 @@ def _sparql(self, query: str) -> List[Resource]: ) response.raise_for_status() except Exception as e: - raise QueryingError(e) + raise QueryingError(e) from e data = response.json() # FIXME workaround to parse a CONSTRUCT query, this fix depends on @@ -885,7 +885,7 @@ def _elastic(self, query: str) -> List[Resource]: ) response.raise_for_status() except Exception as e: - raise QueryingError(e) + raise QueryingError(e) from e results = response.json() return [ @@ -947,7 +947,7 @@ def _initialize_service( ) params = store_config.pop("params", {}) except Exception as ve: - raise ValueError(f"Store configuration error: {ve}") + raise ValueError(f"Store configuration error: {ve}") from ve return Service( endpoint=endpoint, diff --git a/kgforge/specializations/stores/demo_store.py b/kgforge/specializations/stores/demo_store.py index b6cda16b..4745fa22 100644 --- a/kgforge/specializations/stores/demo_store.py +++ b/kgforge/specializations/stores/demo_store.py @@ -60,8 +60,8 @@ def _register_one(self, resource: Resource, schema_id: str) -> None: metadata_context=None, context_resolver=None) try: record = self.service.create(data) - except StoreLibrary.RecordExists: - raise RegistrationError("resource already exists") + except StoreLibrary.RecordExists as exc: + raise RegistrationError("resource already exists") from exc resource.id = record["data"]["id"] resource._store_metadata = wrap_dict(record["metadata"]) @@ -74,8 +74,8 @@ def retrieve(self, id_: str, version: Optional[Union[int, str]], not_supported(("cross_bucket", True)) try: record = self.service.read(id_, version) - except StoreLibrary.RecordMissing: - raise RetrievalError("resource not found") + except StoreLibrary.RecordMissing as exc: + raise RetrievalError("resource not found") from exc return _to_resource(record) @@ -86,10 +86,10 @@ def _update_one(self, resource: Resource, schema_id: str) -> None: metadata_context=None, context_resolver=None) try: record = self.service.update(data) - except StoreLibrary.RecordMissing: - raise UpdatingError("resource not found") - except StoreLibrary.RecordDeprecated: - raise UpdatingError("resource is deprecated") + except StoreLibrary.RecordMissing as exc1: + raise UpdatingError("resource not found") from exc1 + except StoreLibrary.RecordDeprecated as exc2: + raise UpdatingError("resource is deprecated") from exc2 resource._store_metadata = wrap_dict(record["metadata"]) @@ -99,10 +99,10 @@ def _tag_one(self, resource: Resource, value: str) -> None: version = resource._store_metadata.version try: self.service.tag(rid, version, value) - except StoreLibrary.TagExists: - raise TaggingError("resource version already tagged") - except StoreLibrary.RecordMissing: - raise TaggingError("resource not found") + except StoreLibrary.TagExists as exc1: + raise TaggingError("resource version already tagged") from exc1 + except StoreLibrary.RecordMissing as exc2: + raise TaggingError("resource not found") from exc2 # CRU[D]. @@ -110,10 +110,10 @@ def _deprecate_one(self, resource: Resource) -> None: rid = resource.id try: record = self.service.deprecate(rid) - except StoreLibrary.RecordMissing: - raise DeprecationError("resource not found") - except StoreLibrary.RecordDeprecated: - raise DeprecationError("resource already deprecated") + except StoreLibrary.RecordMissing as exc1: + raise DeprecationError("resource not found") from exc1 + except StoreLibrary.RecordDeprecated as exc2: + raise DeprecationError("resource already deprecated") from exc2 resource._store_metadata = wrap_dict(record["metadata"]) @@ -180,8 +180,8 @@ def read(self, rid: str, version: Optional[Union[int, str]]) -> Dict: record = self.archives[akey] else: record = self.records[rid] - except KeyError: - raise self.RecordMissing + except KeyError as exc: + raise self.RecordMissing from exc return record @@ -189,8 +189,8 @@ def update(self, data: Dict) -> Dict: rid = data.get("id", None) try: record = self.records[rid] - except KeyError: - raise self.RecordMissing + except KeyError as exc: + raise self.RecordMissing from exc metadata = record["metadata"] if metadata["deprecated"]: @@ -205,8 +205,8 @@ def update(self, data: Dict) -> Dict: def deprecate(self, rid: str) -> Dict: try: record = self.records[rid] - except KeyError: - raise self.RecordMissing + except KeyError as exc: + raise self.RecordMissing from exc metadata = record["metadata"] if metadata["deprecated"]: diff --git a/kgforge/specializations/stores/nexus/service.py b/kgforge/specializations/stores/nexus/service.py index 24931c59..46248031 100644 --- a/kgforge/specializations/stores/nexus/service.py +++ b/kgforge/specializations/stores/nexus/service.py @@ -257,16 +257,16 @@ def resolve_context(self, iri: str, local_only: Optional[bool] = False) -> Dict: response = requests.get(url, headers=self.headers) response.raise_for_status() resource = response.json() - except Exception: + except Exception as exc: if not local_only: try: context = Context(context_to_resolve) - except URLError: - raise ValueError(f"{context_to_resolve} is not resolvable") + except URLError as exc2: + raise ValueError(f"{context_to_resolve} is not resolvable") from exc2 document = context.document["@context"] else: - raise ValueError(f"{context_to_resolve} is not resolvable") + raise ValueError(f"{context_to_resolve} is not resolvable") from exc else: # Make sure context is not deprecated if '_deprecated' in resource and resource['_deprecated']: @@ -490,7 +490,8 @@ def synchronize_resource( self.sync_metadata(resource, response) else: action = Action(action_name, succeeded, response) - resource._last_action = action + + resource.set_last_action(action) resource._synchronized = synchronized def default_callback(self, fun_name: str) -> Callable: @@ -526,7 +527,7 @@ def verify( synchronized = resource._synchronized if synchronized is not required_synchronized: be_or_not_be = "be" if required_synchronized is True else "not be" - error = exception(f"resource should {be_or_not_be} synchronized") + error = exception(f"Resource should {be_or_not_be} synchronized") self.synchronize_resource( resource, error, function_name, False, False )