Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.Sign up
Create a safeguard module to import stdlib objects from #3290
referenced this issue
Mar 28, 2018
To elaborate, the idea is that in normal pytest modules we never import from stdlib or external modules directly, but from a
The issue is that sometimes people will monkey patch some stdlib module or builtin function, and this breaks pytest internals.
For example some pytest module (say
import os # down the module... os.environ['PYTEST_CURRENT_TEST'] = node.id
Now if a user during a test does something like this:
import os def test(monkeypatch): monkeypatch.setattr(os, 'environ', 1)
It will break pytest internally because
That is the problem, the solution @RonnyPfannschmidt suggests is to import all symbols we require early in a
# safeimp.py from os import environ
So the code in
from .safeimp import environ # down the module... environ['PYTEST_CURRENT_TEST'] = node.id
So we get the original
You might start with looking at all imports from the stdlib (like
Then I wonder if it's worth the bother. How common is the use case of someone monkeypatching the standard library so badly that it doesn't behave like the standard library anymore? I sort feel like you deserve what you get if you make os.environ something that doesn't behave reasonably like a mapping.
Seems, it doesn't help or I doing smth wrong :(
import functools from unittest.mock import Mock def test_partial(monkeypatch): monkeypatch.setattr(functools, "partial", Mock()) assert functools.partial.call_count == 1
from _pytest import safeguarded_function as sf ...... if isinstance(obj, sf.functools.partial): ....
You got a point and I kinda agree with you. The problem is that people sometimes do it (for legitimate testing reasons), and they waste a lot of time trying to figure out what happened. If we can save those users time by implementing some safe-guard, I think it is worth doing it. My 2 cents.
What if I import it like
from functools import partial as functools_partial from inspect import isclass as inspect_isclass, CO_VARARGS as inspect_CO_VARARGS, \ CO_VARKEYWORDS as inspect_CO_VARKEYWORDS from sys import exc_info as sys_exc_info
then this will easier to understand what is it:
from _pytest.safeguarded_function import functools_partial ..... if isinstance(obj, functools_partial): obj = obj.func return obj
Because it's really hard to understand what I import:
from _pytest.safeguarded_function import isclass, CO_VARARGS, CO_VARKEYWORDS, exc_info, ref, ib, Factory, s
from functools import \ partial as functools_partial from inspect import \ isclass as inspect_isclass,\ CO_VARARGS as inspect_CO_VARARGS,\ CO_VARKEYWORDS as inspect_CO_VARKEYWORDS from sys import \ exc_info as sys_exc_info from attr import \ ib as attr_ib, \ s as attr_s, \ Factory as attr_Factory from weakref import \ ref as weakref_ref from re import \ search as re_search
Looking at the implementation so far, I'm starting to have second thoughts about this myself because it seems we will be adding a lot of overhead to our development.
Let's backtrack a little, to recap:
Except for #2180, the others could be fixed by using
If the above is true, we could instead improve
with monkeypatch.context() as m: m.setattr(functools, "partial", Mock())
class MonkeyPatch(object): def __enter__(self): print('HEY OPEN!') def __exit__(self, exc_type, exc_val, exc_tb): print('HEY CLOSE!')
def test_partial(monkeypatch): with monkeypatch as m: m.setattr(functools, "partial", Mock()) assert functools.partial.call_count == 1
But how to create another namespace for with statement? I don't understand how i can save