Skip to content

Commit

Permalink
Introduced import_by_environment() function.
Browse files Browse the repository at this point in the history
  • Loading branch information
idlesign committed Oct 22, 2017
1 parent 154647a commit 24418f3
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 7 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ envbox changelog
================


Unreleased
----------
+ Introduced 'import_by_environment()' function.


v0.1.0
------
+ Basic functionality.
+ Basic functionality.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Features

* Environment type detection (extendable system);
* Convenient ``os.environ`` proxying (with optional values casting into Python natives);
* Automatic submodule-for-environment import tool;
* CLI for environment type probing.


Expand Down
42 changes: 40 additions & 2 deletions docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Quickstart


Basic usage
~~~~~~~~~~~
-----------

.. code-block:: python
Expand Down Expand Up @@ -37,7 +37,7 @@ Basic usage
Environment type aliases
~~~~~~~~~~~~~~~~~~~~~~~~
------------------------

.. code-block:: python
Expand All @@ -50,3 +50,41 @@ Environment type aliases
# we correctly identify it as production environment.
get_environment().is_production # True
Automatic submodule import
--------------------------

**envbox** features ``import_by_environment()`` function which automatically imports symbols of a submodule
of a package for given (or detected) environment into globals of an entry-point submodule.

.. note:: This could be useful not only for Django-projects where submodule-based settings definition is rather usual
but also for varius other cases.


Example::

- project
--- __init__.py
--- settings.py
--- settings_development.py

1. Here ``project`` is a package available for import (note ``__init__.py``).

2. ``settings.py`` is an entry point module for settings using ``import_by_environment()``.

.. code-block:: python
from envbox import import_by_environment
current_env = import_by_environment()
print('Environment type: %s' % current_env)
3. ``settings_development.py`` is one of module files for certain environment (development).

4. ``import_by_environment()`` call in ``settings.py`` makes symbols from ``settings_development.py``
available from ``settings.py``.

2 changes: 1 addition & 1 deletion envbox/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .base import get_environment
from .base import get_environment, import_by_environment
from .envs import DEVELOPMENT, TESTING, STAGING, PRODUCTION, Environment


Expand Down
58 changes: 58 additions & 0 deletions envbox/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from os.path import dirname, basename
from inspect import currentframe
from importlib import import_module

from .detectors import DETECTORS, get_detector
from .envs import Environment, DEVELOPMENT, get_type

Expand Down Expand Up @@ -40,3 +44,57 @@ def get_environment(default=DEVELOPMENT, detectors=None, detectors_opts=None):
env = env()

return env


def import_by_environment(environment=None, module_name_pattern='settings_%s', silent=False):
"""Automatically imports symbols of a submodule of a package for given
(or detected) environment into globals of an entry-point submodule.
Example::
- project
--- __init__.py
--- settings.py
--- settings_development.py
* Here ``project`` is a package available for import (note ``__init__.py``).
* ``settings.py`` is an entry point module for settings using ``import_by_environment()``.
* ``settings_development.py`` is one of module files for certain environment (development).
* ``import_by_environment()`` call in ``settings.py`` makes symbols from ``settings_development.py``
available from ``settings.py``.
:param Environment environment:
:param str|unicode module_name_pattern: Environment submodule name pattern.
``%s`` will be replaced with environment name.
:param bool silent: If ``True`` no import error (if any) will be raised.
:rtype: Environment|None
:returns: ``Environment`` object if module is imported or ``None``.
"""
environment = environment or get_environment()

module_name_pattern = '.' + module_name_pattern

settings_module = currentframe().f_back
package_name = basename(dirname(settings_module.f_code.co_filename))

result = None

try:
env_module = import_module(module_name_pattern % environment, package_name)
settings_module.f_globals.update(env_module.__dict__)

result = environment

except (ImportError, SystemError):
if not silent:
raise

return result
2 changes: 0 additions & 2 deletions tests/test_envs.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ def test_set_get():
env.one = 3
assert env.one == '3'

print(dict(env.getmany_casted('PYTHON')))


def test_set_get_many():

Expand Down
16 changes: 15 additions & 1 deletion tests/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from envbox import get_environment, PRODUCTION
from envbox import get_environment, PRODUCTION, import_by_environment


def test_get_environment():
Expand All @@ -22,3 +22,17 @@ def test_get_environment():

assert env.is_production
assert env == PRODUCTION


def test_autoimport(monkeypatch):

with pytest.raises(SystemError):
import_by_environment()

def import_it(*args):
return type('DummyModule', (object,), {})

monkeypatch.setattr('envbox.base.import_module', import_it)
env = import_by_environment()

assert env.is_development

0 comments on commit 24418f3

Please sign in to comment.