Skip to content

Commit

Permalink
Merge branch 'hotfix-plugin-errors' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
markjonestx committed Jul 5, 2017
2 parents ba95b90 + d4e80c6 commit 27ddf98
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 60 deletions.
2 changes: 1 addition & 1 deletion PyPWA/builtin_plugins/process/_data_split.py
Expand Up @@ -95,7 +95,7 @@ def __call__(self, data):
elif isinstance(data, list):
return self.__list_split(data)
else:
raise ValueError("Unknown data type: %s!")
raise ValueError("Unknown data type: %s!" % type(data))


class SetupData(object):
Expand Down
20 changes: 6 additions & 14 deletions PyPWA/core/shared/plugin_loader.py
Expand Up @@ -95,7 +95,7 @@ def __init__(self):
self.__path_handler = _AppendPath()

def fetch_modules(self, package):
# type: (Union[str, types.ModuleType]) -> object
# type: (Union[str, types.ModuleType]) -> List[object]
found_module = self.__load_module(package)
return self.__process_module(found_module)

Expand Down Expand Up @@ -128,7 +128,7 @@ def __get_module(self, package):

def __process_module_error(self, error):
# type: (Exception) -> None
self.__LOGGER.exception(error)
self.__LOGGER.warning(error)

def __process_module(self, potential_module):
# type: (types.ModuleType) -> List[types.ModuleType]
Expand Down Expand Up @@ -264,7 +264,7 @@ def add_plugin_location(self, location):
self.__process_single_module(location)

def __process_multiple_modules(self, locations):
# type: (Union[List[str, Set[str]]]) -> None
# type: (Union[List[str], Set[str]]) -> None
for location in locations:
self.__process_single_module(location)

Expand All @@ -282,26 +282,18 @@ def __process_single_module(self, location):
)

def __append_modules(self, modules):
# type: (List[type]) -> None
# type: (List[object]) -> None
for the_module in modules:
self.__STORAGE.PLUGINS.append(the_module)

def get_by_name(self, name, fail=True):
def get_by_name(self, name):
# type: (str, bool) -> Callable[Any, Any]
for plugin in self.__STORAGE.PLUGINS:
if hasattr(plugin, name):
possible_answer = getattr(plugin, name)
if callable(possible_answer):
return possible_answer
if fail:
raise ImportError
else:
return self.__empty_function

@staticmethod
def __empty_function(*args, **kwargs):
# type: (Any, Any) -> None
pass
raise ImportError("Failed to find %s!" % name)

def get_by_class(self, template):
# type: (type) -> List[type]
Expand Down
7 changes: 6 additions & 1 deletion PyPWA/progs/shell/fit/__init__.py
Expand Up @@ -95,7 +95,12 @@ class ShellFitting(options.Main):
"setup name": str,
"qfactor location": str,
"data location": str,
"internal data": dict,
"internal data": {
"quality factor": str,
"binned data": str,
"event errors": str,
"expected values": str
},
"accepted monte carlo location": str,
"save name": str
}
Expand Down
125 changes: 91 additions & 34 deletions PyPWA/progs/shell/loaders.py
Expand Up @@ -21,10 +21,10 @@
-----------------------------------------
- DataLoading - takes a data parsing object and use it to load data
for the two programs in a way that the data can be easily repacked into
for the two programs in a way that the data can be easily repacked into
processes.
- FunctionLoader - used to load the setup and processing functions in a
- FunctionLoader - used to load the setup and processing functions in a
predictable way.
"""

Expand Down Expand Up @@ -107,11 +107,15 @@ def __process_data(self):
self.__event_errors = self.__extract_data(
self.__internal_names["event errors"]
)
else:
self.__event_errors = numpy.ones(len(self.__data))

if "expected values" in self.__internal_names:
self.__expected_values = self.__extract_data(
self.__internal_names["expected values"]
)
else:
self.__expected_values = numpy.ones(len(self.__data))

def __extract_data(self, column):
# type: (str) -> ndarray
Expand Down Expand Up @@ -190,49 +194,102 @@ def expected_values(self):
return self.__expected_values


class FunctionLoader(object):
class _ProcessFunctionLoader(object):

__LOGGER = logging.getLogger(__name__ + ".FunctionLoader")
__LOGGER = logging.getLogger(__name__ + "._ProcessingLoader")

def __init__(self, location, process_name, setup_name=None):
# type: (str, str, Opt[str]) -> None
self.__loader = plugin_loader.PluginLoader()
self.__loader.add_plugin_location(location)
self.__process_name = process_name
self.__setup_name = setup_name
self.__process = None # type: shell_types.users_processing
self.__setup = None # type: shell_types.users_setup
self.__load_functions()

def __load_functions(self):
self.__load_process()
self.__load_setup()

def __load_process(self):
self.__process = self.__loader.get_by_name(self.__process_name)

def __load_setup(self):
if isinstance(self.__setup_name, str):
self.__setup = self.__loader.get_by_name(self.__setup_name, False)
self.__LOGGER.debug("Found setup type %s" % repr(self.__setup))
self.__set_none_to_empty()

def __set_none_to_empty(self):
if self.__setup is None:
self.__LOGGER.info("No setup function found, settings to empty.")
self.__setup = self.__empty
def __init__(self, loader, name):
# type: (plugin_loader.PluginLoader, str) -> None
self.__loader = loader
self.__process_name = name
self.__function = None # type: shell_types.users_processing
self.__try_to_load_processing_function()

def __try_to_load_processing_function(self):
try:
self.__load_processing_function()
except Exception as error:
self.__handle_processing_error(error)

def __load_processing_function(self):
self.__function = self.__loader.get_by_name(self.__process_name)

def __handle_processing_error(self, error):
self.__LOGGER.critical("Failed to load %s!" % self.__process_name)
raise error

@property
def process(self):
# type: () -> shell_types.users_processing
return self.__function


class _SetupFunctionLoader(object):

__LOGGER = logging.getLogger(__name__ + "._SetupFunctionLoader")

def __init__(self, loader, name):
# type: (plugin_loader.PluginLoader, str) -> None
self.__loader = loader
self.__function = None # type: shell_types.users_setup
self.__process_setup_name(name)

def __process_setup_name(self, name):
# type: (Opt[str]) -> None
if isinstance(name, str):
self.__try_to_load_setup_function(name)
else:
self.__set_setup_to_empty()

def __try_to_load_setup_function(self, name):
# type: (str) -> None
try:
self.__load_setup_function(name)
except Exception as error:
self.__handle_setup_error(name, error)

def __load_setup_function(self, name):
# type: (str) -> None
self.__function = self.__loader.get_by_name(name)

def __handle_setup_error(self, name, error):
# type: (str, Exception) -> None
self.__LOGGER.critical("%s failed to load!" % name)
self.__LOGGER.exception(error)
self.__set_setup_to_empty()

def __set_setup_to_empty(self):
self.__LOGGER.info("No setup function found, settings to empty.")
self.__function = self.__empty_function

@staticmethod
def __empty():
def __empty_function():
# type: () -> None
pass

@property
def setup(self):
# type: () -> shell_types.users_setup
return self.__function


class FunctionLoader(object):

__LOGGER = logging.getLogger(__name__ + ".FunctionLoader")

def __init__(self, location, process_name, setup_name=None):
# type: (str, str, Opt[str]) -> None
loader = plugin_loader.PluginLoader()
loader.add_plugin_location(location)
self.__process_loader = _ProcessFunctionLoader(loader, process_name)
self.__setup_loader = _SetupFunctionLoader(loader, setup_name)

@property
def process(self):
# type: () -> shell_types.users_processing
return self.__process
return self.__process_loader.process

@property
def setup(self):
# type: () -> shell_types.users_setup
return self.__setup
return self.__setup_loader.setup
15 changes: 7 additions & 8 deletions tests/core/shared/test_plugin_loader.py
Expand Up @@ -51,11 +51,15 @@ def test_processing_is_found(plugin_loader_with_plugins):


def test_minuit_is_found(plugin_loader_with_plugins):
check_template_is_in_list(minuit.MinuitOptions, plugin_loader_with_plugins)
check_template_is_in_list(
minuit.MinuitOptions, plugin_loader_with_plugins
)


def test_nestle_is_found(plugin_loader_with_plugins):
check_template_is_in_list(nestle.NestleOptions, plugin_loader_with_plugins)
check_template_is_in_list(
nestle.NestleOptions, plugin_loader_with_plugins
)


@pytest.mark.xfail(
Expand All @@ -79,12 +83,7 @@ def test_finds_meaning(python_sheet_loader):

def test_cant_find_nothing(python_sheet_loader):
with pytest.raises(ImportError):
fun = python_sheet_loader.get_by_name("nothing", True)


def test_returns_empty(python_sheet_loader):
fun = python_sheet_loader.get_by_name("nothing", False)
assert isinstance(fun(), type(None))
fun = python_sheet_loader.get_by_name("nothing")


def test_can_load_non_existant_file():
Expand Down
6 changes: 4 additions & 2 deletions tests/progs/shell/test_loaders.py
Expand Up @@ -99,8 +99,10 @@ def test_binned_sum_with_internal_names(data_with_internal_names):


def test_error_sum_with_internal_names(data_with_internal_names):
assert numpy.sum(data_with_internal_names.event_errors) == \
4.2857024214064667
numpy.testing.assert_approx_equal(
numpy.sum(data_with_internal_names.event_errors),
4.2857024214064667
)


def test_expected_sum_with_internal_names(data_with_internal_names):
Expand Down

0 comments on commit 27ddf98

Please sign in to comment.