import os
import re
import sys
from datetime import datetime, timezone
from importlib import import_module
from flask import current_app
[docs]class AttrDict(dict):
"""
A dictionary that allows using the dot operator to get and set keys.
"""
def __getattr__(self, key):
return self[key]
def __setattr__(self, key, value):
self[key] = value
def __repr__(self):
return f'{self.__class__.__name__}({dict.__repr__(self)})'
[docs]class ConfigProperty:
"""
Allows extension classes to create properties that proxy to the config value,
eg ``app.config[key]``.
If key is left unspecified, in will be injected by ``ConfigPropertyMetaclass``,
defaulting to ``f'{ext_class_name}_{property_name}'.upper()``.
"""
def __init__(self, key=None):
self.key = key
def __get__(self, instance, cls):
return current_app.config[self.key]
[docs]def cwd_import(module_name):
"""
Attempt to import a module from the current working directory.
Raises ``ImportError`` if not found, or the found module isn't from the current
working directory.
"""
if not sys.path or sys.path[0] != os.getcwd():
sys.path.insert(0, os.getcwd())
module = import_module(module_name)
expected_path = os.path.join(os.getcwd(), module_name.replace('.', os.sep))
expected_paths = [f'{expected_path}.py', os.path.join(expected_path, '__init__.py')]
if module.__file__ not in expected_paths:
raise ImportError(f'expected {expected_paths[0]}, got {module.__file__}')
return module
def format_docstring(docstring):
"""
Strips whitespace from docstrings (both on the ends, and in the middle, replacing
all sequential occurrences of whitespace with a single space).
"""
if not docstring:
return ''
return re.sub(r'\s+', ' ', docstring).strip()
[docs]def get_boolean_env(name, default):
"""
Converts environment variables to boolean values. Truthy is defined as:
``value.lower() in {'true', 'yes', 'y', '1'}`` (everything else is falsy).
"""
default = 'true' if default else 'false'
return os.getenv(name, default).lower() in {'true', 'yes', 'y', '1'}
[docs]def safe_import_module(module_name):
"""
Like :func:`importlib.import_module`, except it does not raise ``ImportError``
if the requested ``module_name`` is not found.
"""
try:
return import_module(module_name)
except ImportError as e:
m = re.match(r"No module named '([\w\.]+)'", str(e))
if not m or not module_name.startswith(m.group(1)):
raise e
[docs]def utcnow():
"""
Returns a current timezone-aware ``datetime.datetime`` in UTC.
"""
# timeit shows that datetime.now(tz=timezone.utc) is 24% slower
return datetime.utcnow().replace(tzinfo=timezone.utc)
__all__ = [
'AttrDict',
'ConfigProperty',
'ConfigPropertyMetaclass',
'cwd_import',
'format_docstring',
'get_boolean_env',
'safe_import_module',
'utcnow',
]