pytest failed to import local module (importing built-in module instead)

Sam Tatasurya

I have the following Python 2.7 simplified project structure:

project/
  ├── libs/
  |     └── zipfile.py
  ├── tests/
  |     ├── __init__.py
  |     └── test_hello.py
  ├── hello.py
  └── main.py

I want this project to use the patched version of one of Python built-in modules (which in this example is zipfile) located in libs. Note that this is an external requirement, and I cannot change the project structure.


Below are the simplified implementation of each file:

libs/zipfile.py

def is_zipfile(filename):
    return "Patched zipfile called"

tests/test_hello.py

from hello import hello

def test_hello():
    assert hello() == "Patched zipfile called"

hello.py

import os
import sys

libs_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "libs"))
if libs_path not in sys.path:
    sys.path.insert(1, libs_path)

import zipfile

def hello():
    print(zipfile.__file__)  # to check which zipfile module is imported
    result = zipfile.is_zipfile("some_path")
    return result

main.py

from hello import hello

def main():
    print(hello())

if __name__ == "__main__":
    main()

When running the program directly (python main.py), I got the expected result:

/home/project/libs/zipfile.pyc
Patched zipfile called

However, when running pytest with project as the working directory (pytest -s), it failed:

/usr/lib/python2.7/zipfile.pyc
================================== FAILURES ===================================
_________________________________ test_hello __________________________________

    def test_hello():
>       assert hello() == "Patched zipfile called"
E       assert False == 'Patched zipfile called'
E        +  where False = hello()

tests/test_hello.py:4: AssertionError
========================== 1 failed in 0.13 seconds ===========================

I've tried a couple solutions presented in this SO post, such as running python -m pytest, but none has worked for me. Is there a way to successfully run this test in a non-hacky way?

Anthony Sottile

The reason your patched zipfile module is not being imported is it has already been imported before the test is started (likely by pytest or one of its dependencies)

I verified this by putting this at the top of hello.py:

if 'zipfile' in sys.modules:
    raise AssertionError('zipfile already imported')

I then get:

$ ./venv/bin/python -mpytest tests
============================= test session starts ==============================
platform linux -- Python 3.6.7, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
rootdir: /tmp/x, inifile:
collected 0 items / 1 errors                                                   

==================================== ERRORS ====================================
_____________________ ERROR collecting tests/test_hello.py _____________________
tests/test_hello.py:1: in <module>
    from hello import hello
hello.py:5: in <module>
    raise AssertionError('zipfile already imported')
E   AssertionError: zipfile already imported
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.14 seconds ============================

You could delete zipfile from sys.modules and then perhaps your copy would be the only one imported:

sys.modules.pop('zipfile', None)

That said, all of this seems like potentially a bad idea as anyone who has already imported that module will have access to the old zipfile and stubbing out stdlib implementation has high potential to break third party libraries which don't expect that.

You might have ~slightly better luck by patching out individual methods on the zipfile module directly (using something like mock.patch.object(zipfile, 'fn', ...)

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Failed to resolve local AAR built from local source module

Mocking a module import in pytest

In python, questions on importing a module that import another module?

Failed to import module slackclient

pytest module import ImportError: No module named

Python project not importing a local module

How to import a local module?

Can not import local module

Import from local relative path instead of pip installed module?

Override importing module's built-in functions from imported module

Import forked module in Python instead of installed module

Import a local module in Python - ImportError: Not module named *

Pyinstaller "failed to import the site module"

Gradle project sync failed in importing another module

How to import local module golang?

How to import local typescript module?

Configure SublimeREPL to import local module

Cannot import local module python

Import * from module by importing via string

Arbitrarily modify and import a python module after importing

pytest cannot import module while python can

How to mock the module import of a class in pytest

Importing a local module that has conditional export

Why does importing a function from a module import the whole module?

"from module import class" importing other classes from same module

Does importing a module mean embedding the code of the module at the line of the import statement?

Import current module from an imported module in python Django (importing loops)

Python: Does importing "sys" module also import "os" module?

importing custom module , don't load other import from the module