wmaee.core.utils

  1import os
  2import uuid
  3import shutil
  4import contextlib
  5import collections.abc
  6from typing import Dict, Type, Any, Optional
  7
  8
  9# this beautiful solution was taken from:
 10# https://stackoverflow.com/questions/2059482/temporarily-modify-the-current-processs-environment
 11@contextlib.contextmanager
 12def override_environ(*remove: str, **update):
 13    """
 14    Temporarily updates the ``os.environ`` dictionary in-place.
 15
 16    The ``os.environ`` dictionary is updated in-place so that the modification
 17    is sure to work in all situations.
 18
 19    :param remove: Environment variables to remove.
 20    :type remove: str
 21    :param update: Dictionary of environment variables and values to add/update.
 22    """
 23    env = os.environ
 24    update = update or {}
 25    remove = remove or []
 26
 27    # List of environment variables being updated or removed.
 28    stomped = (set(update.keys()) | set(remove)) & set(env.keys())
 29    # Environment variables and values to restore on exit.
 30    update_after = {k: env[k] for k in stomped}
 31    # Environment variables and values to remove on exit.
 32    remove_after = frozenset(k for k in update if k not in env)
 33
 34    try:
 35        env.update(update)
 36        [env.pop(k, None) for k in remove]
 37        yield
 38    finally:
 39        env.update(update_after)
 40        [env.pop(k) for k in remove_after]
 41
 42
 43# class working_directory(object):
 44#     """
 45#     A convenience class which syntactic sugar, allowing the user to change the directories.
 46#     Can also be nested.
 47#     """
 48
 49#     def __init__(self, name: Optional[str] = None, prefix: Optional[str] = None, delete: bool =False):
 50#         """
 51#         Constructs a working_directory object
 52#         :param name: name of the directory if None is given `os.getcwd()` will be used (default is `None`)
 53#         :type name: Optional[str]
 54#         :param prefix: a prefix where to locate the directory (default is `None`)
 55#         :type prefix: Optional[str]
 56#         :param delete: whether to delete the directory after a with clause (default is `False`)
 57#         :type delete: bool
 58#         """
 59#         self._name = str(uuid.uuid4()) if not name else name
 60#         self._delete = delete
 61#         self._curr_dir = os.getcwd()
 62#         self._active = False
 63#         if prefix is not None:
 64#             self._name = os.path.join(prefix, self._name)
 65
 66#     def __enter__(self):
 67#         if not os.path.exists(self._name):
 68#             os.mkdir(self._name)
 69#         os.chdir(self._name)
 70#         self._active = True
 71#         return self
 72
 73#     def __exit__(self, exc_type, exc_val, exc_tb):
 74#         os.chdir(self._curr_dir)
 75#         if self._delete:
 76#             shutil.rmtree(self._name)
 77#         self._active = False
 78
 79#     @property
 80#     def name(self):
 81#         return self._name
 82
 83#     @property
 84#     def active(self):
 85#         return self._active
 86
 87
 88def merge(*dicts: Dict[Any, Any], factory: Type = dict, **kwargs) -> Dict[Any, Any]:
 89    """
 90    Merge all specified dictionaries in {dicts} into a single one. Moreover, {kwargs} will be included. In case
 91    duplicate keys exist, the order of passing the dictionaries will determine which values will sustain for the keys.
 92    The last updated is carried out using {kwargs}
 93
 94    :param dicts: the dictionaries to merge
 95    :type dicts: Dict[Any, Any]
 96    :param factory: the constructor of the mapping type to create (default is `dict`)
 97    :type factory: Type
 98    :return: a merged dictionary of type {factory}
 99    :rtype: Dict[Any, Any]
100    """
101
102    merged = factory()
103    for dictionary in dicts:
104        merged.update(dictionary)
105    merged.update(kwargs)
106    return merged
107
108
109def _wrap_in_iterable(o, factory=tuple):
110    return factory((o,))
111
112
113def ensure_iterable(o, exclude=(str, bytes), factory=tuple):
114    """
115    wraps an object {o} into an iterable it is not and iterable. the type of the iterable is specified by {factory}
116    :param o: the object to wrap
117    :param exclude: type list of iterable objects which need wrapping (default is (str, bytes))
118    :type exclude: Iterable[type]
119    :param factory: the Iterable type in which {o} should be wrapped
120    :type factory: type
121    :rtype: Iterable
122    """
123    if isinstance(o, collections.abc.Iterable):
124        return o if not isinstance(o, exclude) else _wrap_in_iterable(o, factory=factory)
125    else:
126        return _wrap_in_iterable(o, factory=factory)
@contextlib.contextmanager
def override_environ(*remove: str, **update):
13@contextlib.contextmanager
14def override_environ(*remove: str, **update):
15    """
16    Temporarily updates the ``os.environ`` dictionary in-place.
17
18    The ``os.environ`` dictionary is updated in-place so that the modification
19    is sure to work in all situations.
20
21    :param remove: Environment variables to remove.
22    :type remove: str
23    :param update: Dictionary of environment variables and values to add/update.
24    """
25    env = os.environ
26    update = update or {}
27    remove = remove or []
28
29    # List of environment variables being updated or removed.
30    stomped = (set(update.keys()) | set(remove)) & set(env.keys())
31    # Environment variables and values to restore on exit.
32    update_after = {k: env[k] for k in stomped}
33    # Environment variables and values to remove on exit.
34    remove_after = frozenset(k for k in update if k not in env)
35
36    try:
37        env.update(update)
38        [env.pop(k, None) for k in remove]
39        yield
40    finally:
41        env.update(update_after)
42        [env.pop(k) for k in remove_after]

Temporarily updates the os.environ dictionary in-place.

The os.environ dictionary is updated in-place so that the modification is sure to work in all situations.

Parameters
  • remove: Environment variables to remove.
  • update: Dictionary of environment variables and values to add/update.
def merge( *dicts: Dict[Any, Any], factory: Type = <class 'dict'>, **kwargs) -> Dict[Any, Any]:
 90def merge(*dicts: Dict[Any, Any], factory: Type = dict, **kwargs) -> Dict[Any, Any]:
 91    """
 92    Merge all specified dictionaries in {dicts} into a single one. Moreover, {kwargs} will be included. In case
 93    duplicate keys exist, the order of passing the dictionaries will determine which values will sustain for the keys.
 94    The last updated is carried out using {kwargs}
 95
 96    :param dicts: the dictionaries to merge
 97    :type dicts: Dict[Any, Any]
 98    :param factory: the constructor of the mapping type to create (default is `dict`)
 99    :type factory: Type
100    :return: a merged dictionary of type {factory}
101    :rtype: Dict[Any, Any]
102    """
103
104    merged = factory()
105    for dictionary in dicts:
106        merged.update(dictionary)
107    merged.update(kwargs)
108    return merged

Merge all specified dictionaries in {dicts} into a single one. Moreover, {kwargs} will be included. In case duplicate keys exist, the order of passing the dictionaries will determine which values will sustain for the keys. The last updated is carried out using {kwargs}

Parameters
  • dicts: the dictionaries to merge
  • factory: the constructor of the mapping type to create (default is dict)
Returns

a merged dictionary of type {factory}

def ensure_iterable(o, exclude=(<class 'str'>, <class 'bytes'>), factory=<class 'tuple'>):
115def ensure_iterable(o, exclude=(str, bytes), factory=tuple):
116    """
117    wraps an object {o} into an iterable it is not and iterable. the type of the iterable is specified by {factory}
118    :param o: the object to wrap
119    :param exclude: type list of iterable objects which need wrapping (default is (str, bytes))
120    :type exclude: Iterable[type]
121    :param factory: the Iterable type in which {o} should be wrapped
122    :type factory: type
123    :rtype: Iterable
124    """
125    if isinstance(o, collections.abc.Iterable):
126        return o if not isinstance(o, exclude) else _wrap_in_iterable(o, factory=factory)
127    else:
128        return _wrap_in_iterable(o, factory=factory)

wraps an object {o} into an iterable it is not and iterable. the type of the iterable is specified by {factory}

Parameters
  • o: the object to wrap
  • exclude: type list of iterable objects which need wrapping (default is (str, bytes))
  • factory: the Iterable type in which {o} should be wrapped