diff --git a/molsysmt/_private/digestion/argument.py b/molsysmt/_private/digestion/argument.py index 16c6993a0..f53fef807 100644 --- a/molsysmt/_private/digestion/argument.py +++ b/molsysmt/_private/digestion/argument.py @@ -2,7 +2,7 @@ from ..exceptions import WrongGetArgumentError -def digest_argument(argument, element): +def digest_argument(argument, element, caller=""): """ Helper function to check the names of keyword arguments passed to get function. @@ -14,6 +14,10 @@ def digest_argument(argument, element): element: str The name of an element. + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- str @@ -27,4 +31,4 @@ def digest_argument(argument, element): if output_argument in attributes: return output_argument else: - raise WrongGetArgumentError(argument) + raise WrongGetArgumentError(argument, caller) diff --git a/molsysmt/_private/digestion/box.py b/molsysmt/_private/digestion/box.py index f7f1fb5a9..cbba6ef47 100644 --- a/molsysmt/_private/digestion/box.py +++ b/molsysmt/_private/digestion/box.py @@ -3,12 +3,12 @@ from ..exceptions import IncorrectShapeError -def digest_box(box): +def digest_box(box, caller=""): # TODO: Function doesn't do anything. return box -def digest_box_values(box_values): +def digest_box_values(box_values, caller=""): """ Checks if box_values has the correct shape. The array should have shape (n, 3) where n is any integer. @@ -23,6 +23,10 @@ def digest_box_values(box_values): box_values : np.ndarray, list or tuple A quantity with the box lengths. + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Raises ------ IncorrectShapeError @@ -37,17 +41,24 @@ def digest_box_values(box_values): if shape[0] == 3: box_values = np.expand_dims(box_values, axis=0) else: - raise IncorrectShapeError(expected_shape="(1, 3)", actual_shape=str(shape)) + raise IncorrectShapeError(expected_shape="(1, 3)", + actual_shape=str(shape), + caller=caller) elif len(shape) == 2: if shape[1] != 3: - raise IncorrectShapeError(expected_shape="(n, 3)", actual_shape=str(shape)) + raise IncorrectShapeError(expected_shape="(n, 3)", + actual_shape=str(shape), + caller=caller + ) else: - raise IncorrectShapeError(expected_shape="(n, 3)", actual_shape=str(shape)) + raise IncorrectShapeError(expected_shape="(n, 3)", + actual_shape=str(shape), + caller=caller) return box_values -def digest_box_lengths_or_angles(box_parameters): +def digest_box_lengths_or_angles(box_parameters, caller=""): """ Checks if box_parameters have the correct shape. Can be used to check box_lengths and box_angles. @@ -56,6 +67,10 @@ def digest_box_lengths_or_angles(box_parameters): box_parameters : puw.Quantity A quantity with the box lengths or box values. + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- puw.Quantity @@ -68,15 +83,15 @@ def digest_box_lengths_or_angles(box_parameters): """ unit = puw.get_unit(box_parameters) box_parameters = puw.get_value(box_parameters) - box_parameters = digest_box_values(box_parameters) + box_parameters = digest_box_values(box_parameters, caller) return box_parameters * unit -def digest_box_lengths(box_lengths): +def digest_box_lengths(box_lengths, caller=""): """ Checks if box_lengths have the correct shape. """ - return digest_box_lengths_or_angles(box_lengths) + return digest_box_lengths_or_angles(box_lengths, caller) -def digest_box_angles(box_angles): +def digest_box_angles(box_angles, caller=""): """ Checks if box_angles have the correct shape. """ - return digest_box_lengths_or_angles(box_angles) + return digest_box_lengths_or_angles(box_angles, caller) diff --git a/molsysmt/_private/digestion/comparison.py b/molsysmt/_private/digestion/comparison.py index 23b99ba93..536c20669 100644 --- a/molsysmt/_private/digestion/comparison.py +++ b/molsysmt/_private/digestion/comparison.py @@ -3,7 +3,7 @@ from ..exceptions import * -def digest_comparison(comparison): +def digest_comparison(comparison, caller=""): # TODO: function doesn't do anything return comparison diff --git a/molsysmt/_private/digestion/coordinates.py b/molsysmt/_private/digestion/coordinates.py index e5df18c4d..27a1ffd4b 100644 --- a/molsysmt/_private/digestion/coordinates.py +++ b/molsysmt/_private/digestion/coordinates.py @@ -1,7 +1,7 @@ from ..exceptions import * -def digest_coordinates(coordinates): +def digest_coordinates(coordinates, caller=""): # TODO: function doesn't do anything return coordinates diff --git a/molsysmt/_private/digestion/digest.py b/molsysmt/_private/digestion/digest.py index 4190794d3..46e21b70b 100644 --- a/molsysmt/_private/digestion/digest.py +++ b/molsysmt/_private/digestion/digest.py @@ -47,8 +47,6 @@ def digest(func): @functools.wraps(func) def wrapper(*args, **kwargs): - # if not check_args: - # return func(*args, **kwargs) if not config.argument_checking: return func(*args, **kwargs) @@ -74,7 +72,8 @@ def wrapper(*args, **kwargs): if ii >= len(args): break try: - digested_value = digestion_functions[argument_name](args[ii]) + digested_value = digestion_functions[argument_name](args[ii], + caller=func.__name__) except KeyError: digested_value = args[ii] all_args[argument_name] = digested_value @@ -89,13 +88,16 @@ def wrapper(*args, **kwargs): # it will appear in kwargs even if is not for argument_name, value in kwargs.items(): try: - digested_value = digestion_functions[argument_name](value) + digested_value = digestion_functions[argument_name](value, + caller=func.__name__) all_args[argument_name] = digested_value except KeyError: if argument_name in args_names: all_args[argument_name] = value else: - digested_argument_name = digest_argument(argument_name, element_name) + digested_argument_name = digest_argument(argument_name, + element_name, + caller=func.__name__) all_args[digested_argument_name] = value return func(**all_args) diff --git a/molsysmt/_private/digestion/element.py b/molsysmt/_private/digestion/element.py index 19516a84f..70d70db17 100644 --- a/molsysmt/_private/digestion/element.py +++ b/molsysmt/_private/digestion/element.py @@ -25,7 +25,7 @@ } -def digest_element(element): +def digest_element(element, caller=""): """ Helper function to check an element type. Raises a BadCallError if the element type is not supported by MolSysMT. @@ -34,6 +34,10 @@ def digest_element(element): element : str The name of the element. + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Raises ------ BadCallError @@ -44,7 +48,7 @@ def digest_element(element): if element_name_lower in elements_from_plural: return elements_from_plural[element_name_lower] if element_name_lower not in elements: - raise WrongElementError("wrong element name") + raise WrongElementError(element, caller) return element_name_lower - raise WrongElementError("element is not a string") + raise WrongElementError(element, caller) diff --git a/molsysmt/_private/digestion/engine.py b/molsysmt/_private/digestion/engine.py index eb115851f..819c660bc 100644 --- a/molsysmt/_private/digestion/engine.py +++ b/molsysmt/_private/digestion/engine.py @@ -16,7 +16,7 @@ engines_from_lowercase = {ii.lower(): ii for ii in engines} -def digest_engine(engine): +def digest_engine(engine, caller=""): """ Check the name of the engine. Parameters @@ -24,6 +24,10 @@ def digest_engine(engine): engine : str The name of the engine + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Raises ------ BadCallError @@ -33,6 +37,5 @@ def digest_engine(engine): try: return engines_from_lowercase[engine.lower()] except KeyError: - # TODO: create a wrong engine error - raise WrongEngineError - raise WrongEngineError + raise WrongEngineError(engine, caller) + raise WrongEngineError(engine, caller) diff --git a/molsysmt/_private/digestion/form.py b/molsysmt/_private/digestion/form.py index 4ae12c080..76612dabb 100644 --- a/molsysmt/_private/digestion/form.py +++ b/molsysmt/_private/digestion/form.py @@ -2,7 +2,7 @@ from ..exceptions import WrongFormError -def digest_form(form): +def digest_form(form, caller=""): """ Checks that the name of the form is correct and returns its name capitalized. For example, if form is mdanalysis.universe it returns mdanalysis.Universe. @@ -12,6 +12,10 @@ def digest_form(form): form: str or list[str] The name or names of the forms + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- str or list[str] @@ -30,11 +34,11 @@ def digest_form(form): try: return _dict_forms_lowercase[form.lower()] except KeyError: - raise WrongFormError(form) + raise WrongFormError(form, caller) -def digest_to_form(to_form): +def digest_to_form(to_form, caller=""): if to_form is None: return None - return digest_form(to_form) + return digest_form(to_form, caller) diff --git a/molsysmt/_private/digestion/indices.py b/molsysmt/_private/digestion/indices.py index 029bd6eff..d4d54aa3a 100644 --- a/molsysmt/_private/digestion/indices.py +++ b/molsysmt/_private/digestion/indices.py @@ -3,7 +3,7 @@ from ..lists_and_tuples import is_list_or_tuple -def digest_indices(indices): +def digest_indices(indices, caller=""): """ Checks if indices are of the expected type and value. Parameters @@ -11,6 +11,10 @@ def digest_indices(indices): indices : str or int or list or tuple or range. The indices + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- str or np.ndarray @@ -28,35 +32,35 @@ def digest_indices(indices): if indices.lower() == 'all': return 'all' else: - raise WrongIndicesError + raise WrongIndicesError(type(indices), caller) elif isinstance(indices, (int, np.int64, np.int32)): indices = np.array([indices], dtype='int64') elif isinstance(indices, (np.ndarray, list, tuple, range)): indices = np.array(indices, dtype='int64') else: - raise WrongIndicesError + raise WrongIndicesError(type(indices), caller) return indices -def digest_atom_indices(atom_indices): +def digest_atom_indices(atom_indices, caller=""): """ Checks if atom indices are of the expected type and value. """ - return digest_indices(atom_indices) + return digest_indices(atom_indices, caller) -def digest_group_indices(group_indices): +def digest_group_indices(group_indices, caller=""): """ Checks if group indices are of the expected type and value. """ - return digest_indices(group_indices) + return digest_indices(group_indices, caller) -def digest_structure_indices(structure_indices): +def digest_structure_indices(structure_indices, caller=""): """ Checks if structure indices are of the expected type and value. """ - return digest_indices(structure_indices) + return digest_indices(structure_indices, caller) -def digest_multiple_structure_indices(structure_indices): +def digest_multiple_structure_indices(structure_indices, caller=""): """ Checks multiple structure indices. """ if is_list_or_tuple(structure_indices): - return [digest_structure_indices(ii) for ii in structure_indices] + return [digest_structure_indices(ii, caller) for ii in structure_indices] else: - return digest_structure_indices(structure_indices) + return digest_structure_indices(structure_indices, caller) diff --git a/molsysmt/_private/digestion/item.py b/molsysmt/_private/digestion/item.py index 16682c14f..1334e99aa 100644 --- a/molsysmt/_private/digestion/item.py +++ b/molsysmt/_private/digestion/item.py @@ -1,7 +1,7 @@ from ..exceptions import WrongFormError -def digest_item(item, form): +def digest_item(item, form, caller=""): """ Check if an item has the expected form. Examples @@ -18,6 +18,10 @@ def digest_item(item, form): form : str Name of the form + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Raises ------ WrongFormError @@ -29,4 +33,4 @@ def digest_item(item, form): try: dict_is_form[form](item) except KeyError: - raise WrongFormError(form) + raise WrongFormError(form, caller) diff --git a/molsysmt/_private/digestion/molecular_system.py b/molsysmt/_private/digestion/molecular_system.py index 39ec11412..59cc76b55 100644 --- a/molsysmt/_private/digestion/molecular_system.py +++ b/molsysmt/_private/digestion/molecular_system.py @@ -1,5 +1,5 @@ -def digest_single_molecular_system(molecular_system): +def digest_single_molecular_system(molecular_system, caller=""): """ Check if an object is a molecular system. Parameters @@ -7,9 +7,14 @@ def digest_single_molecular_system(molecular_system): molecular_system : Any The molecular system. + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- - None + molecular_system : Any + The molecular system. Raises ------ @@ -20,11 +25,11 @@ def digest_single_molecular_system(molecular_system): from molsysmt.basic import is_molecular_system if not is_molecular_system(molecular_system): - raise MolecularSystemNeededError() + raise MolecularSystemNeededError(caller=caller) return molecular_system -def digest_multiple_molecular_systems(molecular_systems): +def digest_multiple_molecular_systems(molecular_systems, caller=""): """ Check if an object is a molecular system. Parameters @@ -32,9 +37,14 @@ def digest_multiple_molecular_systems(molecular_systems): molecular_systems : Any The molecular systems. + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- - None + molecular_system : Any + The molecular system. Raises ------ @@ -46,5 +56,5 @@ def digest_multiple_molecular_systems(molecular_systems): from molsysmt.basic import are_multiple_molecular_systems if not are_multiple_molecular_systems(molecular_systems): - raise MultipleMolecularSystemsNeededError() + raise MultipleMolecularSystemsNeededError(caller=caller) return molecular_systems diff --git a/molsysmt/_private/digestion/output.py b/molsysmt/_private/digestion/output.py index 2f94d234a..6d3d6f1b0 100644 --- a/molsysmt/_private/digestion/output.py +++ b/molsysmt/_private/digestion/output.py @@ -1,10 +1,11 @@ from ..lists_and_tuples import is_list_or_tuple -def digest_output(output): +def digest_output(output, caller=""): """ If 'output is a list or tuple with a single element, it returns that element. If not it returns the original 'output'. """ + # TODO: check this function if is_list_or_tuple(output): if len(output) == 1: output = output[0] diff --git a/molsysmt/_private/digestion/selection.py b/molsysmt/_private/digestion/selection.py index dc639a700..f78189311 100644 --- a/molsysmt/_private/digestion/selection.py +++ b/molsysmt/_private/digestion/selection.py @@ -2,7 +2,7 @@ from ..lists_and_tuples import is_list_or_tuple -def digest_selection(selection, syntaxis="MolSysMT"): +def digest_selection(selection, syntaxis="MolSysMT", caller=""): """ Checks if a given selection is of the expected type. """ # TODO: this function may not check for all selection types. @@ -14,7 +14,7 @@ def digest_selection(selection, syntaxis="MolSysMT"): return selection -def digest_multiple_selections(selections, syntaxis="MolSysMT"): +def digest_multiple_selections(selections, syntaxis="MolSysMT", caller=""): if is_list_or_tuple(selections): return [digest_selection(ii, syntaxis) for ii in selections] else: diff --git a/molsysmt/_private/digestion/step.py b/molsysmt/_private/digestion/step.py index 03fb305ba..3881be5ff 100644 --- a/molsysmt/_private/digestion/step.py +++ b/molsysmt/_private/digestion/step.py @@ -1,7 +1,7 @@ from ..exceptions import * -def digest_step(step): +def digest_step(step, caller=""): """ Checks if step is an integer or null. Parameters @@ -9,6 +9,10 @@ def digest_step(step): step: Any The step + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- int or None @@ -22,4 +26,4 @@ def digest_step(step): """ if step is None or isinstance(step, int): return step - raise WrongStepError() + raise WrongStepError(type(step), caller) diff --git a/molsysmt/_private/digestion/syntaxis.py b/molsysmt/_private/digestion/syntaxis.py index e43c9987a..f1bc1853d 100644 --- a/molsysmt/_private/digestion/syntaxis.py +++ b/molsysmt/_private/digestion/syntaxis.py @@ -11,7 +11,7 @@ syntaxis_from_lower = {ii.lower(): ii for ii in syntaxis} -def digest_syntaxis(syntaxis): +def digest_syntaxis(syntaxis, caller=""): """ Helper function to check if the given syntaxis is supported by MolSysMt. @@ -20,6 +20,10 @@ def digest_syntaxis(syntaxis): syntaxis : str The name of the syntaxis in lower case. + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- syntaxis : str @@ -28,20 +32,20 @@ def digest_syntaxis(syntaxis): from ..exceptions import WrongSyntaxisError if not isinstance(syntaxis, str): - raise WrongSyntaxisError("syntaxis must be a string.") + raise WrongSyntaxisError("syntaxis must be a string.", caller) try: syntaxis = syntaxis_from_lower[syntaxis.lower()] except KeyError: - raise WrongSyntaxisError(f"{syntaxis} is not a valid syntaxis.") + raise WrongSyntaxisError(f"{syntaxis} is not a valid syntaxis.", caller) return syntaxis -def digest_to_syntaxis(to_syntaxis): +def digest_to_syntaxis(to_syntaxis, caller=""): """ Check if to syntaxis is a valid syntaxis. Can also be null. """ if to_syntaxis is None: return None else: - return digest_syntaxis(to_syntaxis) + return digest_syntaxis(to_syntaxis, caller) diff --git a/molsysmt/_private/digestion/time.py b/molsysmt/_private/digestion/time.py index 39a9f862f..707b39e42 100644 --- a/molsysmt/_private/digestion/time.py +++ b/molsysmt/_private/digestion/time.py @@ -1,7 +1,7 @@ from ..exceptions import * -def digest_time(time): +def digest_time(time, caller=""): """ Check if time is null or a float. Parameters @@ -9,6 +9,10 @@ def digest_time(time): time : None or float The time. + caller: str, optional + Name of the function or method that is being digested. + For debugging purposes. + Returns ------- None or float @@ -20,4 +24,4 @@ def digest_time(time): """ if time is None or isinstance(time, (float, bool)): return time - raise WrongTimeError() + raise WrongTimeError(type(time), caller) diff --git a/molsysmt/_private/exceptions/caller_name.py b/molsysmt/_private/exceptions/caller_name.py new file mode 100644 index 000000000..845ad6722 --- /dev/null +++ b/molsysmt/_private/exceptions/caller_name.py @@ -0,0 +1,19 @@ +import inspect + + +def caller_name(skip=3): + """ Returns the name of the function that called this. + + Parameters + ---------- + skip: int + Number of frames to skip. + Use 2 to get the caller in an exception, or 3 in an exception + that is derived from another exception. + + Returns + ------- + str + Name of the caller function. + """ + return inspect.stack()[skip].function diff --git a/molsysmt/_private/exceptions/not_implemented_errors.py b/molsysmt/_private/exceptions/not_implemented_errors.py index c8a36b8f5..e5a1bab8f 100644 --- a/molsysmt/_private/exceptions/not_implemented_errors.py +++ b/molsysmt/_private/exceptions/not_implemented_errors.py @@ -1,3 +1,4 @@ +from molsysmt._private.exceptions.caller_name import caller_name __github_web__ = 'https://github.com/uibcdf/MolSysMT' __github_issues_web__ = __github_web__ + '/issues' @@ -11,16 +12,19 @@ def __init__(self, message=None): if message is None: message = "" - message += ( + call_name = caller_name(skip=3) + + if message is None: + full_message = f"Error in {call_name}. " + else: + full_message = f"Error in {call_name}. " + message + + full_message += ( "The method has not been implemented yet. " f" Check {api_doc} for more information. " f"Write a new issue in {__github_issues_web__} asking for its implementation." ) - super().__init__(message) - - -class NotImplementedConversionError(MolSysNotImplementedError): - pass + super().__init__(full_message) class NotImplementedMethodError(MolSysNotImplementedError): @@ -32,7 +36,12 @@ class NotImplementedSyntaxisError(MolSysNotImplementedError): class NotImplementedConversionError(MolSysNotImplementedError): - pass + """ Exception raised when the conversion between two forms has not been + implemented yet. + """ + def __init__(self, from_form, to_form): + message = f"Error in conversion from {from_form} to {to_form}" + super().__init__(message) class LibraryNotFoundError(MolSysNotImplementedError): diff --git a/molsysmt/_private/exceptions/value_errors.py b/molsysmt/_private/exceptions/value_errors.py index 4b08adabf..79caa4b16 100644 --- a/molsysmt/_private/exceptions/value_errors.py +++ b/molsysmt/_private/exceptions/value_errors.py @@ -1,3 +1,4 @@ +from molsysmt._private.exceptions.caller_name import caller_name __github_web__ = 'https://github.com/uibcdf/MolSysMT' __github_issues_web__ = __github_web__ + '/issues' @@ -5,17 +6,35 @@ class MolSysValueError(ValueError): - """ Base class for value errors. """ + """ Base class for value errors. Ir prints a message + containing info such as the function that raised + the error, and a link to GitHub repository. - def __init__(self, message=None): - if message is None: - message = "" + Parameters + ---------- + message : str, optional + An informative message of the exception. + + caller : str, optional + Name of the function or method that raised the error. + + """ + + def __init__(self, message="", caller=""): - message += ( - f" Check {api_doc} for more information. " + if not caller: + caller = caller_name(skip=3) + + if not message: + full_message = f"Error in {caller}. " + else: + full_message = f"Error in {caller}. " + message + + full_message += ( + f"Check {api_doc} for more information. " f"If you still need help, open a new issue in {__github_issues_web__}." ) - super().__init__(message) + super().__init__(full_message) class NotWithThisMolecularSystemError(MolSysValueError): @@ -23,26 +42,26 @@ class NotWithThisMolecularSystemError(MolSysValueError): the given type of molecular system. """ - def __init__(self, message=None): - if message is None: + def __init__(self, message="", caller=""): + if not message: message = 'This method can not be applied over this molecular system. ' - super().__init__(message) + super().__init__(message, caller) class MolecularSystemNeededError(MolSysValueError): - def __init__(self, message=None): - if message is None: - message = ( f"The method works over a molecular system. " - f"Either no molecular system or multiple systems were provided.") - super().__init__(message) + def __init__(self, message="", caller=""): + if not message: + message = (f"The method works over a molecular system. " + f"Either no molecular system or multiple systems were provided.") + super().__init__(message, caller) class MultipleMolecularSystemsNeededError(MolSysValueError): - def __init__(self, message=None): - if message is None: + def __init__(self, message="", caller=""): + if not message: message = ('This method works only over a single molecular system. ' 'But multiple molecular systems are provided. ') - super().__init__(message) + super().__init__(message, caller) class NotSupportedFormError(MolSysValueError): @@ -50,9 +69,10 @@ class NotSupportedFormError(MolSysValueError): This exception is raised when Sabueso does not recognize the item as a supported form. """ - def __init__(self, form_type): + + def __init__(self, form_type, caller=""): message = f"The input molecular system or item has a not supported form: {form_type} " - super().__init__(message) + super().__init__(message, caller) class NotWithThisFormError(MolSysValueError): @@ -63,17 +83,20 @@ class NotWithThisFormError(MolSysValueError): dihedral angle defined by four atoms can not work over a GROMACS topology file (.top). In this case the method will raise a 'NotWithTisFormError' exception. """ - def __init__(self, form): + + def __init__(self, form, caller=""): message = f"Invalid form: {form} " - super().__init__(message) + super().__init__(message, caller) class WrongGetArgumentError(MolSysValueError): """ Exception raised when the get method is called with a not valid input argument. """ - def __init__(self, argument=None): + + def __init__(self, argument=None, caller=""): message = f"The get method was invoked with not valid input argument: \"{argument}\"" - super().__init__(message) + super().__init__(message, caller) + # TODO: WrongFormError and NotWithThisFormError are redundant. @@ -81,9 +104,10 @@ def __init__(self, argument=None): class WrongFormError(MolSysValueError): """Exception raised when the item has not the correct form expected by a method or a class. """ - def __init__(self, form): + + def __init__(self, form, caller=""): message = f"The input item's should be {form} and is not. " - super().__init__(message) + super().__init__(message, caller) class WrongAtomIndicesError(MolSysValueError): @@ -95,7 +119,12 @@ class WrongSelectionError(MolSysValueError): class WrongIndicesError(MolSysValueError): - pass + """ Exception raised when atom, group, or structure indices are not of the + expected type. + """ + def __init__(self, indices_type, caller=""): + message = f"Wrong indices type: {indices_type}" + super().__init__(message, caller) class WrongOutputFilenameError(MolSysValueError): @@ -107,7 +136,10 @@ class WrongComparisonError(MolSysValueError): class WrongStepError(MolSysValueError): - pass + """ Exception raised when a step is of an incorrect type.""" + def __init__(self, step_type, caller=""): + message = f"Wrong step type: {step_type}" + super().__init__(message, caller) class WrongStructureIndicesError(MolSysValueError): @@ -119,7 +151,10 @@ class WrongSyntaxisError(MolSysValueError): class WrongTimeError(MolSysValueError): - pass + """ Exception raised when time is of an incorrect type.""" + def __init__(self, step_type, caller=""): + message = f"Wrong time type: {step_type}" + super().__init__(message, caller) class WrongToFormError(MolSysValueError): @@ -127,21 +162,33 @@ class WrongToFormError(MolSysValueError): class WrongElementError(MolSysValueError): - pass + """ Exception raised when an element is not supported by MolSysMT. """ + def __init__(self, element, caller=""): + if isinstance(element, str): + message = f"Wrong element name: {element}. " + else: + message = "" + super().__init__(message, caller) class WrongEngineError(MolSysValueError): - pass + """ Exception raised when an engine is not supported by MolSysMT. """ + def __init__(self, engine, caller=""): + if isinstance(engine, str): + message = f"Unsupported engine {engine}. " + else: + message = "" + super().__init__(message, caller) class IncorrectShapeError(MolSysValueError): """Exception raised when a quantity or array doesn't have the correct shape. """ - def __init__(self, expected_shape=None, actual_shape=None): + def __init__(self, expected_shape=None, actual_shape=None, caller=""): message = "" if expected_shape: message = f"Expected shape {expected_shape}. " if actual_shape: message += f"Actual shape {actual_shape}. " - super().__init__(message) + super().__init__(message, caller) diff --git a/molsysmt/tests/exceptions/test_get_caller.py b/molsysmt/tests/exceptions/test_get_caller.py new file mode 100644 index 000000000..2dac58313 --- /dev/null +++ b/molsysmt/tests/exceptions/test_get_caller.py @@ -0,0 +1,121 @@ +from molsysmt._private.exceptions import caller_name as cn +from molsysmt._private.exceptions import WrongElementError +from molsysmt._private.digestion import digest +from molsysmt import get +import pytest + + +def foo(): + """ Returns its name. """ + return cn.caller_name(skip=1) + + +def function_that_calls_another_function(): + return foo() + + +def calls_three_functions(): + return function_that_calls_another_function() + + +def test_get_caller_name_from_function(): + caller_name = foo() + assert caller_name == "foo" + + caller_name = function_that_calls_another_function() + assert caller_name == "foo" + + caller_name = calls_three_functions() + assert caller_name == "foo" + + +class Bar: + + def baz(self): + return cn.caller_name(skip=1) + + def call_foo(self): + return foo() + + +def test_get_caller_name_from_class(): + + bar = Bar() + caller = bar.baz() + assert caller == "baz" + + caller = bar.call_foo() + assert caller == "foo" + + +class BaseError(ValueError): + def __init__(self, skip=2, message=None): + caller_name = cn.caller_name(skip=skip) + + if message is None: + message = f"Error in {caller_name}. " + + message += "Check docs for more information. " + super().__init__(message) + + +class ReportingCallerError(BaseError): + """ An error that reports the name of the function or method + thar raises it. + """ + def __init__(self, argument): + caller_name = cn.caller_name(skip=2) + message = f"Error in {caller_name}. Wrong argument {argument}" + super().__init__(2, message) + + +class OtherError(BaseError): + """ An error that reports the name of the function or method + thar raises it. + """ + def __init__(self): + super().__init__(3, None) + + +def raise_error_function(num): + if num == 1: + raise BaseError + elif num == 2: + raise OtherError + raise ReportingCallerError("num") + + +def test_get_caller_name_from_function_raising_error(): + + with pytest.raises(BaseError) as exc_info: + raise_error_function(1) + + assert "raise_error_function" in str(exc_info) + + with pytest.raises(OtherError) as exc_info: + raise_error_function(2) + + assert "raise_error_function" in str(exc_info) + + with pytest.raises(ReportingCallerError) as exc_info: + raise_error_function(3) + + assert "raise_error_function" in str(exc_info) + assert "Wrong argument num" in str(exc_info) + + +@digest +def decorated_fn(element): + pass + + +def test_function_with_decorator(): + + with pytest.raises(WrongElementError) as exc_info: + decorated_fn(element="cow") + assert "Error in decorated_fn" in str(exc_info) + + with pytest.raises(WrongElementError) as exc_info: + get("molecular system", element="cow") + + assert "Error in get" in str(exc_info) diff --git a/molsysmt/tests/exceptions/test_not_implemeted_error.py b/molsysmt/tests/exceptions/test_not_implemeted_error.py index 9e5f5e26f..f374028d1 100644 --- a/molsysmt/tests/exceptions/test_not_implemeted_error.py +++ b/molsysmt/tests/exceptions/test_not_implemeted_error.py @@ -1,15 +1,36 @@ -from molsysmt._private.exceptions.not_implemented_errors import MolSysNotImplementedError +from molsysmt._private.exceptions.not_implemented_errors import MolSysNotImplementedError, NotImplementedConversionError import pytest + def raise_error(error): raise error +def raise_error_with_argument(error, *args): + raise error(*args) + + def test_not_implemented_error(): with pytest.raises(MolSysNotImplementedError) as exc_info: raise_error(MolSysNotImplementedError) message = ( + "Error in test_not_implemented_error. " + "The method has not been implemented yet. " + f" Check for more information. " + f"Write a new issue in https://github.com/uibcdf/MolSysMT/issues asking for its implementation." + ) + assert message == str(exc_info.value) + + +def test_not_implemented_conversion_error(): + + with pytest.raises(NotImplementedConversionError) as exc_info: + raise_error_with_argument(NotImplementedConversionError, "bar", "foo") + + message = ( + "Error in raise_error_with_argument. " + "Error in conversion from bar to foo" "The method has not been implemented yet. " f" Check for more information. " f"Write a new issue in https://github.com/uibcdf/MolSysMT/issues asking for its implementation." diff --git a/molsysmt/tests/exceptions/test_value_errors.py b/molsysmt/tests/exceptions/test_value_errors.py index e0ff252c3..523052e0c 100644 --- a/molsysmt/tests/exceptions/test_value_errors.py +++ b/molsysmt/tests/exceptions/test_value_errors.py @@ -1,6 +1,7 @@ import pytest from molsysmt._private.exceptions.value_errors import * + def raise_error(error): raise error @@ -11,7 +12,7 @@ def raise_error_with_argument(error, argument): @pytest.fixture def base_error_msg(): - return (" Check for more information. " + return ("Check for more information. " "If you still need help, open a new issue in https://github.com/uibcdf/MolSysMT/issues." ) @@ -19,13 +20,18 @@ def base_error_msg(): def test_molsys_value_error(base_error_msg): with pytest.raises(MolSysValueError) as exc_info: raise_error(MolSysValueError) - assert base_error_msg == str(exc_info.value) + + msg = "Error in test_molsys_value_error. " + msg += base_error_msg + assert msg == str(exc_info.value) def test_raise_not_with_this_molecular_system(base_error_msg): with pytest.raises(NotWithThisMolecularSystemError) as exc_info: raise_error(NotWithThisMolecularSystemError) - msg = "This method can not be applied over this molecular system. " + + msg = "Error in raise_error. " + msg += "This method can not be applied over this molecular system. " msg += base_error_msg assert msg == str(exc_info.value) @@ -33,8 +39,10 @@ def test_raise_not_with_this_molecular_system(base_error_msg): def test_multiple_molecular_systems(base_error_msg): with pytest.raises(MultipleMolecularSystemsNeededError) as exc_info: raise_error(MultipleMolecularSystemsNeededError) - msg = ('This method works only over a single molecular system. ' - 'But multiple molecular systems are provided. ') + + msg = "Error in raise_error. " + msg += ('This method works only over a single molecular system. ' + 'But multiple molecular systems are provided. ') msg += base_error_msg assert msg == str(exc_info.value) @@ -42,7 +50,9 @@ def test_multiple_molecular_systems(base_error_msg): def test_not_supported_form(base_error_msg): with pytest.raises(NotSupportedFormError) as exc_info: raise_error_with_argument(NotSupportedFormError, type("hello")) - msg = "The input molecular system or item has a not supported form: " + + msg = "Error in raise_error_with_argument. " + msg += "The input molecular system or item has a not supported form: " msg += base_error_msg assert msg == str(exc_info.value) @@ -50,7 +60,9 @@ def test_not_supported_form(base_error_msg): def test_not_with_this_form(base_error_msg): with pytest.raises(NotWithThisFormError) as exc_info: raise_error_with_argument(NotWithThisFormError, type("hello")) - msg = "Invalid form: " + + msg = "Error in raise_error_with_argument. " + msg += "Invalid form: " msg += base_error_msg assert msg == str(exc_info.value) @@ -58,6 +70,8 @@ def test_not_with_this_form(base_error_msg): def test_wrong_get_argument(base_error_msg): with pytest.raises(WrongGetArgumentError) as exc_info: raise_error_with_argument(WrongGetArgumentError, "hello") - msg = "The get method was invoked with not valid input argument: \"hello\"" + + msg = "Error in raise_error_with_argument. " + msg += "The get method was invoked with not valid input argument: \"hello\"" msg += base_error_msg assert msg == str(exc_info.value)