Skip to content

Commit c191294

Browse files
committed
Refactoring and import tests
1 parent 1cab771 commit c191294

File tree

5 files changed

+111
-21
lines changed

5 files changed

+111
-21
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ lint:
66
flake8 piper
77

88
unit:
9+
pytest -vs tests/import_tests.py
910
pytest -vs tests/base_test.py
1011
pytest -vs tests/envs_test.py::TestCompose
1112
pytest -vs tests/envs_test.py::TestVenv

piper/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from piper.configurations import get_configuration
2-
from piper.imports import activate_safe_import
2+
from piper.imports import _set_import_functions
33

44
configuration = get_configuration()
5+
56
if configuration.ignore_import_errors:
67
"""
7-
Piper activates safe import if configured True.
8+
Piper activates safe import globally for piper work if configured True.
89
This ignores any import errors for safe imports in piper.base.executors
910
"""
10-
activate_safe_import()
11+
_set_import_functions(ignore=True)

piper/configurations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class Configuration:
1313

1414
env: str = 'compose'
1515
ignore_import_errors: bool = True
16+
safe_import_activated: bool = False
1617

1718
# start time and counter
1819
wait_on_iter = 0.5

piper/imports.py

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,52 @@ def __getattr__(self, name):
2828
__all__ = []
2929

3030

31+
def _piper_was_touched_in_frame(frame_before=1):
32+
call_function_frame = inspect.currentframe().f_back
33+
frame = call_function_frame
34+
for i in range(frame_before):
35+
frame = frame.f_back
36+
37+
result = False
38+
f_locals = frame.f_locals
39+
f_globals = frame.f_globals
40+
all_variables = f_locals | f_globals
41+
42+
if all_variables.values():
43+
all_variables = [v for v in all_variables.values() if v is not None]
44+
if len(all_variables) > 0:
45+
variables_have_piper_package = any("piper" in v.__package__ \
46+
for v in all_variables if hasattr(v, "__package__") and type(v.__package__) == str)
47+
variables_have_piper_module = any("piper" in v.__module__ \
48+
for v in all_variables if hasattr(v, "__module__") and type(v.__module__) == str)
49+
result = variables_have_piper_module | variables_have_piper_package
50+
51+
return result
52+
53+
54+
def _from_piper_file_but_not_piper(name: str, globals={}):
55+
is_import_from_piper_source_code = "__file__" in globals and "piper/" in globals["__file__"]
56+
not_piper_import = not ("piper" in name)
57+
result = is_import_from_piper_source_code and not_piper_import
58+
59+
return result
60+
61+
3162
def try_import(name, globals={}, locals={}, fromlist=[], level=0):
3263
"""
3364
This import replace real Python import with fake import which returns warning only and PiperDummyModule.
3465
This works for everything under piper/ frameworks files by filename but not for piper import (like piper.base)
3566
And this also works for every file where you import something from piper firstly !
3667
"""
37-
is_piper_imported_from_previous_module = False
38-
prev_module_locals = inspect.currentframe().f_back.f_locals
39-
prev_module_globals = inspect.currentframe().f_back.f_globals
40-
prev_modules = prev_module_locals | prev_module_globals
41-
42-
if prev_modules.values():
43-
prev_modules = [v for v in prev_modules.values() if v is not None and hasattr(v, "__module__")]
44-
if len(prev_modules) > 0:
45-
is_piper_imported_from_previous_module = \
46-
any("piper" in v.__module__ for v in prev_modules if type(v.__module__) == str)
68+
if not (configuration.ignore_import_errors or configuration.safe_import_activated):
69+
logger.info("Ignore import errors is off in Configuration and deactivated")
70+
return real_import(name, globals, locals, fromlist, level)
4771

48-
is_import_from_piper_source_code = "__file__" in globals and "piper/" in globals["__file__"]
49-
not_piper_import = not ("piper" in name)
50-
is_from_source_but_not_piper = is_import_from_piper_source_code and not_piper_import
72+
piper_was_touched_in_previous_frame = _piper_was_touched_in_frame(frame_before=1)
73+
need_to_catch = piper_was_touched_in_previous_frame or _from_piper_file_but_not_piper(name, globals)
5174

52-
if is_piper_imported_from_previous_module and is_from_source_but_not_piper:
53-
logger.info(f"Piper activates safe import for library {name} in piper file {globals['__file__']} ")
75+
if need_to_catch:
76+
logger.info(f"Piper runs safe import for library {name} in piper file {globals['__file__']} ")
5477
try:
5578
return real_import(name, globals, locals, fromlist, level)
5679
except ImportError as e:
@@ -69,7 +92,7 @@ def try_import(name, globals={}, locals={}, fromlist=[], level=0):
6992
real_import = builtins.__import__
7093

7194

72-
def set_ignore_import_errors(ignore: bool = True):
95+
def _set_import_functions(ignore: bool = True):
7396
if ignore:
7497
builtins.__import__ = try_import
7598
else:
@@ -89,12 +112,14 @@ def activate_safe_import():
89112
90113
"""
91114
logger.info(f"Piper activates safe import")
92-
set_ignore_import_errors(ignore=True)
115+
configuration.safe_import_activated = True
116+
_set_import_functions(ignore=True)
93117

94118

95119
def deactivate_safe_import():
96120
logger.info(f"Piper deactivates safe import")
97-
set_ignore_import_errors(ignore=False)
121+
configuration.safe_import_activated = False
122+
_set_import_functions(ignore=configuration.ignore_import_errors)
98123

99124

100125
class safe_import:

tests/import_tests.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import pytest
2+
3+
4+
class TestSafeImport:
5+
6+
def test_safe_import_after_piper(self):
7+
import piper
8+
import foo
9+
from piper.imports import PiperDummyModule
10+
assert isinstance(foo, (PiperDummyModule, ))
11+
12+
def test_safe_import_before_piper(self):
13+
# it doesn't work normally
14+
with pytest.raises(ImportError):
15+
import foo
16+
import piper
17+
18+
def test_safe_import_after_executors_import(self):
19+
from piper.base import executors
20+
import foo
21+
from piper.imports import PiperDummyModule
22+
assert isinstance(foo, (PiperDummyModule, ))
23+
24+
def test_safe_import_after_base_executor(self):
25+
from piper.base.executors import BaseExecutor
26+
import foo
27+
from piper.imports import PiperDummyModule
28+
assert isinstance(foo, (PiperDummyModule, ))
29+
30+
def test_safe_import_after_piper_as_p(self):
31+
import piper as p
32+
import foo
33+
from piper.imports import PiperDummyModule
34+
assert isinstance(foo, (PiperDummyModule,))
35+
36+
def test_not_ignore_error_by_flag(self):
37+
import piper as p
38+
p.configurations.Configuration.ignore_import_errors = False
39+
with pytest.raises(ImportError):
40+
import foo
41+
42+
def test_safe_import_as_context_when_global_off(self):
43+
import piper as p
44+
p.configurations.Configuration.ignore_import_errors = False
45+
with p.imports.safe_import():
46+
import foo
47+
from piper.imports import PiperDummyModule
48+
assert isinstance(foo, (PiperDummyModule,))
49+
# safe import was deactivated
50+
with pytest.raises(ImportError):
51+
import bar
52+
53+
def test_safe_import_as_context_when_global_on(self):
54+
import piper as p
55+
p.configurations.Configuration.ignore_import_errors = True
56+
with p.imports.safe_import():
57+
import foo
58+
from piper.imports import PiperDummyModule
59+
assert isinstance(foo, (PiperDummyModule,))
60+
# safe import was deactivated but global is on
61+
import bar
62+
assert isinstance(bar, (PiperDummyModule,))

0 commit comments

Comments
 (0)