I have a test that makes sure a specific (helpful) error message is raised, when a required package is not available.
def foo(caller):
try:
import pkg
except ImportError:
raise ImportError(f'Install "pkg" to use {caller}')
pkg.bar()
with pytest.raises(ImportError, match='Install "pkg" to use test_function'):
foo('test_function')
However, pkg is generally available, as other tests rely on it.
Currently, I set up an additional virtual env without pkg just for this test. This seems like overkill.
Is it possible to "hide" an installed package within a module or function?
I ended up with the following pytest-only solution, which appears to be more robust in the setting of a larger project.
import builtins
import pytest
#pytest.fixture
def hide_available_pkg(monkeypatch):
import_orig = builtins.__import__
def mocked_import(name, *args, **kwargs):
if name == 'pkg':
raise ImportError()
return import_orig(name, *args, **kwargs)
monkeypatch.setattr(builtins, '__import__', mocked_import)
#pytest.mark.usefixtures('hide_available_pkg')
def test_message():
with pytest.raises(ImportError, match='Install "pkg" to use test_function'):
foo('test_function')
You can mock builtins.__import__.
from unittest import mock
import pytest
def foo(caller):
try:
import pkg
except ImportError:
raise ImportError(f'Install "pkg" to use {caller}')
pkg.bar()
with mock.patch("builtins.__import__", side_effect=ImportError):
with pytest.raises(ImportError, match='Install "pkg" to use test_function'):
foo('test_function')
Related
I am looking for a way to add soft requirements to my python module.
My module will use some external pip resources that the user may or may not install depending on their use case. I would like this to be an option rather than a requirement but a big chunk of code will require the external modules. I simply want to return an error if a class is used that does not have the correct module installed.
example:
#tkc_ext.py
try:
from tkcalendar import DateEntry
tkc_imported = True
except:
tkc_imported = False
class tkcNotImportedError(Exception):
def __init__(self):
print("tkcalendar module is not installed.\n please install it to use these widgets")
class cDateEntry(DateEntry):
def __init__(self, master, **kwargs):
if not tkc_import:
raise tkcNotImportedError
##insert code here
I'm not sure how I can disable the cDateEntry class if tkcalendar has not been installed as it will error on the class line due to DateEntry not being imported, and i cant error out before that as it will simply error if the file is imported.
Folks,
I have a problem during including file.py to test_file.py namely:
file.py uses Robot library BuiltIn:
from robot.libraries.BuiltIn import BuiltIn
DEFAULT_IPHY_TTI_TRACE_DIR =
os.path.join(BuiltIn().get_variable_value('${OUTPUT_DIR}'), 'iphy_tti_trace')
And when I try to include file.py in my test_file.py
import pytest
#import file.py
I receive:
test_file.py:8: in <module>
/opt/ute/python/lib/python2.7/site-packages/robot/libraries/BuiltIn.py:1331: in get_variable_value
return self._variables[self._get_var_name(name)]
/opt/ute/python/lib/python2.7/site-packages/robot/libraries/BuiltIn.py:75: in _variables
return self._namespace.variables
/opt/ute/python/lib/python2.7/site-packages/robot/libraries/BuiltIn.py:71: in _namespace
return self._get_context().namespace
/opt/ute/python/lib/python2.7/site-packages/robot/libraries/BuiltIn.py:66: in _get_context
raise RobotNotRunningError('Cannot access execution context')
E RobotNotRunningError: Cannot access execution context
How can I mock this? This is posible at all?
Sure, the issue is just that you can't mock the BuiltIn class where it is used (in file.py). You have to mock the class where it is declared (in robot.libraries.BuiltIn).
Using mocks:
from unittest.mock import patch, MagicMock
def _test_default_iphy_tti_trace_dir():
with patch('robot.libraries.BuiltIn.BuiltIn.get_variable_value', return_value='/foo/bar'):
import file
assert file.DEFAULT_IPHY_TTI_TRACE_DIR == '/foo/bar/iphy_tti_trace'
Using monkeypatch fixture:
def test_default_iphy_tti_trace_dir(monkeypatch):
def mocked_get(self, name):
return '/foo/bar'
monkeypatch.setattr('robot.libraries.BuiltIn.BuiltIn.get_variable_value', mocked_get)
import file
assert file.DEFAULT_IPHY_TTI_TRACE_DIR == '/foo/bar/iphy_tti_trace'
Also note that the mocking is done for the scope of a single test only, so you can't import file on top of the test module as the BuiltIn will be unpatched there, raising the context error.
My Python package depends on an external library for a few of it's functions. This is a non-Python package and can be difficult to install, so I'd like users to still be able to use my package but have it fail when using any functions that depend on this non-Python package.
What is the standard practice for this? I could only import the non-Python package inside the methods that use it, but I really hate doing this
My current setup:
myInterface.py
myPackage/
--classA.py
--classB.py
The interfaces script myInterface.py imports classA and classB and classB imports the non-Python package. If the import fails I print a warning. If myMethod is called and the package isn't installed there will be some error downstream but I do not catch it anywhere, nor do I warn the user.
classB is imported every time the interface script is called so I can't have anything fail there, which is why I included the pass. Like I said above, I could import inside the method and have it fail there, but I really like keeping all of my imports in one place.
From classB.py
try:
import someWeirdPackage
except ImportError:
print("Cannot import someWeirdPackage")
pass
class ClassB():
...
def myMethod():
swp = someWeirdPackage()
...
If you are only importing one external library, I would go for something along these lines:
try:
import weirdModule
available = True
except ImportError:
available = False
def func_requiring_weirdmodule():
if not available:
raise ImportError('weirdModule not available')
...
The conditional and error checking is only needed if you want to give more descriptive errors. If not you can omit it and let python throw the corresponding error when trying to calling a non-imported module, as you do in your current setup.
If multiple functions do use weirdModule, you can wrap the checking into a function:
def require_weird_module():
if not available:
raise ImportError('weirdModule not available')
def f1():
require_weird_module()
...
def f2():
require_weird_module()
...
On the other hand, if you have multiple libraries to be imported by different functions, you can load them dynamically. Although it doesn't look pretty, python caches them and there is nothing wrong with it. I would use importlib
import importlib
def func_requiring_weirdmodule():
weirdModule = importlib.import_module('weirdModule')
Again, if multiple of your functions import complicated external modules you can wrap them into:
def import_external(name):
return importlib.import_module(name)
def f1():
weird1 = import_external('weirdModule1')
def f2():
weird2 = import_external('weirdModule2')
And last, you could create a handler to prevent importing the same module twice, something along the lines of:
class Importer(object):
__loaded__ = {}
#staticmethod
def import_external(name):
if name in Importer.__loaded__:
return Importer.__loaded__[name]
mod = importlib.import_module(name)
Importer.__loaded__[name] = mod
return mod
def f1():
weird = Importer.import_external('weird1')
def f2():
weird = Importer.import_external('weird1')
Although I'm pretty sure that importlib does caching behing the scenes and you don't really need for manual caching.
In short, although it does look ugly, there is nothing wrong with importing modules dynamically in python. In fact, a lot of libraries rely on this. On the other hand, if it is just for an special case of 3 methods accessing 1 external function, do use your approach or my first one in case you cant to add custom sception handling.
I'm not really sure that there's any best practice in this situation, but I would redefine the function if it's not supported:
def warn_import():
print("Cannot import someWeirdPackage")
try:
import someWeirdPackage
external_func = someWeirdPackage
except ImportError:
external_func = warn_import
class ClassB():
def myMethod(self):
swp = external_func()
b = ClassB()
b.myMethod()
You can create two separate classes for the two cases. The first will be used when the the package exist . The second will used when the package does not exist.
class ClassB1():
def myMethod(self):
print("someWeirdPackage exist")
# do something
class ClassB2(ClassB1):
def myMethod(self):
print("someWeirdPackage does not exist")
# do something or raise Exception
try:
import someWeirdPackage
class ClassB(ClassB1):
pass
except ImportError:
class ClassB(ClassB2):
pass
You can also use given below approach to overcome the problem that you're facing.
class UnAvailableName(object):
def __init__(self, name):
self.target = name
def __getattr_(self, attr):
raise ImportError("{} is not available.".format(attr))
try:
import someWeirdPackage
except ImportError:
print("Cannot import someWeirdPackage")
someWeirdPackage = someWeirdPackage("someWeirdPackage")
class ClassB():
def myMethod():
swp = someWeirdPackage.hello()
a = ClassB()
a.myMethod()
I am using a custom logging module in my project. If it is not available, I'd like to substitute it with a dummy instead of raising an ImportError.
Here's the code which currently does that:
try:
import logger
except ImportError:
print 'Couldn\'t load logger'
class DummyLogger(object):
def __init__(self):
pass
def log(self, image):
pass
logger = DummyLogger()
I don't think it's an elegant solution. It works, sure, but it ain't nice. Is there a better way?
I would put the dummy implementation into a separate module, called dummy_loggger, and write:
try:
import logger
except ImportError:
import dummy_logger as logger
I've done this in the past with JSON parsers:
try:
import ujson as json # very fast but might not be available in some cases
except ImportError:
import json
You can make it more concise quite easily:
try:
import logger
except ImportError:
print 'Couldn\'t load logger'
class logger(object):
#classmethod
def log(cls, image):
pass
Note that, even in your current version, the empty __init__ should be removed -- it adds no value.
I have a python script that does this at the begining of the script:
def initialize(module_name):
return importlib.import_module(module_name) # import module from string
I want to write a test that 'mocks' out the module name like so:
def test():
# assemble module at run time
module_obj = {'name': Object, 'another_name': AnotherObject}
# inject to the "import system"
inject_mock_module('mymodule', module_obj)
# assert that the import went correctly
assert my_module_mock == initialize('mymodule')
How do I do this? First, specifically, how to I create module_obj and how do I define inject_mock_module? This needs to work in both 2.7+ and 3.3+
Use the mock library to mock out the import_module() function. As of Python 3.3, you can import that module as unittest.mock:
try:
# Python 3.3+
from unittest.mock import patch
except ImportError:
# External dependency
from mock import patch
def test():
module_obj = {'name': Object, 'another_name': AnotherObject}
with patch('importlib.import_module', new=module_obj.get):
assert initialize('name') is module_obj['name']