aboutsummaryrefslogtreecommitdiffstats
path: root/bsie/utils
diff options
context:
space:
mode:
Diffstat (limited to 'bsie/utils')
-rw-r--r--bsie/utils/__init__.py9
-rw-r--r--bsie/utils/errors.py45
-rw-r--r--bsie/utils/filematcher/parser.py6
-rw-r--r--bsie/utils/loading.py54
4 files changed, 107 insertions, 7 deletions
diff --git a/bsie/utils/__init__.py b/bsie/utils/__init__.py
index 3981dc7..9cb60ed 100644
--- a/bsie/utils/__init__.py
+++ b/bsie/utils/__init__.py
@@ -4,21 +4,24 @@ Part of the bsie module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2022
"""
-# imports
+# standard imports
import typing
# inner-module imports
from . import bsfs
+from . import filematcher
from . import namespaces as ns
from . import node
-from . import filematcher
+from .loading import safe_load, unpack_qualified_name
# exports
__all__: typing.Sequence[str] = (
- 'filematcher',
'bsfs',
+ 'filematcher',
'node',
'ns',
+ 'safe_load',
+ 'unpack_qualified_name',
)
## EOF ##
diff --git a/bsie/utils/errors.py b/bsie/utils/errors.py
new file mode 100644
index 0000000..5fafd5b
--- /dev/null
+++ b/bsie/utils/errors.py
@@ -0,0 +1,45 @@
+"""Common BSIE exceptions.
+
+Part of the bsie module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import typing
+
+# exports
+__all__: typing.Sequence[str] = (
+ 'BuilderError',
+ 'ExtractorError',
+ 'LoaderError',
+ 'ReaderError',
+ )
+
+
+## code ##
+
+class _BSIEError(Exception):
+ """Generic BSIE error."""
+
+class BuilderError(_BSIEError):
+ """The Builder failed to create an instance."""
+
+class LoaderError(BuilderError):
+ """Failed to load a module or class."""
+
+class ExtractorError(_BSIEError):
+ """The Extractor failed to process the given content."""
+
+class ReaderError(_BSIEError):
+ """The Reader failed to read the given file."""
+
+class ProgrammingError(_BSIEError):
+ """An assertion-like error that indicates a code-base issue."""
+
+class UnreachableError(ProgrammingError):
+ """Bravo, you've reached a point in code that should logically not be reachable."""
+
+class ParserError(_BSIEError):
+ """Failed to parse due to invalid syntax or structures."""
+
+## EOF ##
diff --git a/bsie/utils/filematcher/parser.py b/bsie/utils/filematcher/parser.py
index 0654742..2f82875 100644
--- a/bsie/utils/filematcher/parser.py
+++ b/bsie/utils/filematcher/parser.py
@@ -7,16 +7,14 @@ Author: Matthias Baumgartner, 2021
# standard imports
import typing
-# non-standard imports
+# external imports
import pyparsing
from pyparsing import printables, alphas8bit, punc8bit, QuotedString, Word, \
delimitedList, Or, CaselessKeyword, Group, oneOf, Optional
-# bsie imports
-from bsie.base import errors
-
# inner-module imports
from . import matcher
+from .. import errors
# exports
__all__: typing.Sequence[str] = (
diff --git a/bsie/utils/loading.py b/bsie/utils/loading.py
new file mode 100644
index 0000000..eb05c35
--- /dev/null
+++ b/bsie/utils/loading.py
@@ -0,0 +1,54 @@
+"""
+
+Part of the bsie module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# standard imports
+import importlib
+import typing
+
+# inner-module imports
+from . import errors
+
+# exports
+__all__: typing.Sequence[str] = (
+ 'safe_load',
+ 'unpack_qualified_name',
+ )
+
+
+## code ##
+
+def safe_load(module_name: str, class_name: str):
+ """Get a class from a module. Raise BuilderError if anything goes wrong."""
+ try:
+ # load the module
+ module = importlib.import_module(module_name)
+ except Exception as err:
+ # cannot import module
+ raise errors.LoaderError(f'cannot load module {module_name}') from err
+
+ try:
+ # get the class from the module
+ cls = getattr(module, class_name)
+ except Exception as err:
+ # cannot find the class
+ raise errors.LoaderError(f'cannot load class {class_name} from module {module_name}') from err
+
+ return cls
+
+
+def unpack_qualified_name(name):
+ """Split a name into its module and class component (dot-separated)."""
+ if not isinstance(name, str):
+ raise TypeError(name)
+ if '.' not in name:
+ raise ValueError('name must be a qualified class name.')
+ module_name, class_name = name[:name.rfind('.')], name[name.rfind('.')+1:]
+ if module_name == '':
+ raise ValueError('name must be a qualified class name.')
+ return module_name, class_name
+
+
+## EOF ##