-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38 from timbernat/feature-cleanup
Merge branch "feature cleanup"
- Loading branch information
Showing
10 changed files
with
748 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
__author__ = 'Timotej Bernat' | ||
__email__ = '[email protected]' | ||
|
||
from typing import Callable, Optional, ParamSpec, TypeVar | ||
from typing import Callable, Optional, ParamSpec, TypeVar, Union | ||
|
||
Params = ParamSpec('Params') | ||
ReturnType = TypeVar('ReturnType') | ||
|
@@ -58,7 +58,7 @@ def module_installed(module_name : str) -> bool: | |
|
||
try: # NOTE: opted for this implementation, as it never actually imports the package in question (faster and fewer side-effects) | ||
return find_spec(module_name) is not None | ||
except (ValueError, AttributeError, ModuleNotFoundError): # these could all be raised by | ||
except (ValueError, AttributeError, ModuleNotFoundError): # these could all be raised by a missing module | ||
return False | ||
|
||
def modules_installed(*module_names : list[str]) -> bool: | ||
|
@@ -80,7 +80,7 @@ def modules_installed(*module_names : list[str]) -> bool: | |
|
||
def requires_modules( | ||
*required_module_names : list[str], | ||
missing_module_error : type[Exception]=ImportError, | ||
missing_module_error : Union[Exception, type[Exception]]=ImportError, | ||
) -> Callable[[TCall[..., ReturnType]], TCall[..., ReturnType]]: | ||
''' | ||
Decorator which enforces optional module dependencies prior to function execution | ||
|
@@ -99,12 +99,27 @@ def requires_modules( | |
Raised if any of the specified packages is not found to be installed | ||
Exception message will indicate the name of the specific package found missing | ||
''' | ||
# meta-check to ensure type of raised Exception is valid | ||
if not isinstance(missing_module_error, Exception): | ||
if not (isinstance(missing_module_error, type) and issubclass(missing_module_error, Exception)): | ||
# DEV: this is potentially brittle, depending on how the specific Exception subtype is implemented? | ||
raise TypeError('Must pass either Exception instance or subtype to "missing_module_error') | ||
|
||
def tailored_exception(module_name : str) -> Exception: | ||
'''Accessory function to generate targetted Exceptions based on the provided | ||
mssing_module_error value and the name of a module with no found installation''' | ||
if isinstance(missing_module_error, Exception): | ||
return missing_module_error | ||
|
||
if isinstance(missing_module_error, type): | ||
return missing_module_error(f'No installation found for module "{module_name}"') | ||
|
||
def decorator(func) -> TCall[..., ReturnType]: | ||
@wraps(func) | ||
def req_wrapper(*args : Params.args, **kwargs : Params.kwargs) -> ReturnType: | ||
for module_name in required_module_names: | ||
if not module_installed(module_name): | ||
raise missing_module_error(f'No installation found for module "{module_name}"') | ||
raise tailored_exception(module_name) | ||
else: | ||
return func(*args, **kwargs) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,54 @@ | |
__email__ = '[email protected]' | ||
|
||
from typing import Any | ||
|
||
from textwrap import indent | ||
from enum import StrEnum | ||
|
||
|
||
class Justification(StrEnum): | ||
'''For specifying string justification''' | ||
LEFT = '<' | ||
CENTER = '^' | ||
RIGHT = '>' | ||
Just = Justification # alias for the lazy or hurried | ||
|
||
def procrustean_string( | ||
string : str, | ||
length : int, | ||
padding : str=' ', | ||
just : Justification=Justification.LEFT, | ||
) -> int: | ||
'''Takes a string and a target length and returns a new string which begins | ||
with the same characters as the original string but is clamped to the target length, | ||
truncating or padding if the original string is too long or short, respectively | ||
Parameters | ||
---------- | ||
string : str | ||
The string to stretch or cut | ||
length : int | ||
The target number of characters in the final string | ||
padding : str, default=" " | ||
A single character which shold be used as padding | ||
when strings are too short, by default just a space | ||
MUST BE EXACTLY ONE CHARACTER! | ||
just : Justification, default=Justification.LEFT | ||
Enum specifier of how to justify a padded string | ||
Options are Justification.LEFT, Justification.CENTER, or Justification.RIGHT | ||
Returns | ||
------- | ||
fmt_str : str | ||
A string which begins with the same characters as "string" but has | ||
precisely the specified length, with specified padding as specified | ||
''' | ||
if not (isinstance(length, int) and (length >= 0)): | ||
raise ValueError(f'Target string length must be a non-negative integer (not {length})') | ||
if not len(padding) == 1: | ||
raise IndexError(f'Padding string must contain exactly one character (passed "{padding}")') | ||
|
||
return f'{string[:length]:{padding}{just.value}{length}}' | ||
|
||
def dict_to_indented_str(dict_to_stringify : dict[Any, Any], level_delimiter : str='\t', line_sep : str='\n') -> str: | ||
'''Generate a pretty-printable string from a (possibly nested) dictionary, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,11 @@ | |
|
||
__author__ = 'Timotej Bernat' | ||
__email__ = '[email protected]' | ||
|
||
from .rdkdraw import ( | ||
set_rdkdraw_size, | ||
enable_substruct_highlights, | ||
disable_substruct_highlights, | ||
enable_kekulized_drawing, | ||
disable_kekulized_drawing, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.