1+ import sys
2+ import types
3+ from contextlib import contextmanager
4+ from unittest .mock import MagicMock , Mock , patch
5+
16import pytest
27
38# we have to import the pytest plugin fixtures here,
2025)
2126
2227
28+ SENTINEL = object ()
29+
30+
2331@pytest .fixture (scope = 'session' , autouse = True )
2432def setup_default_app_trap ():
2533 from celery ._state import set_default_app
@@ -31,6 +39,117 @@ def app(celery_app):
3139 return celery_app
3240
3341
42+ @contextmanager
43+ def module_context_manager (* names ):
44+ """Mock one or modules such that every attribute is a :class:`Mock`."""
45+ yield from _module (* names )
46+
47+
48+ def _module (* names ):
49+ prev = {}
50+
51+ class MockModule (types .ModuleType ):
52+
53+ def __getattr__ (self , attr ):
54+ setattr (self , attr , Mock ())
55+ return types .ModuleType .__getattribute__ (self , attr )
56+
57+ mods = []
58+ for name in names :
59+ try :
60+ prev [name ] = sys .modules [name ]
61+ except KeyError :
62+ pass
63+ mod = sys .modules [name ] = MockModule (name )
64+ mods .append (mod )
65+ try :
66+ yield mods
67+ finally :
68+ for name in names :
69+ try :
70+ sys .modules [name ] = prev [name ]
71+ except KeyError :
72+ try :
73+ del sys .modules [name ]
74+ except KeyError :
75+ pass
76+
77+
78+ class _patching :
79+
80+ def __init__ (self , monkeypatch , request ):
81+ self .monkeypatch = monkeypatch
82+ self .request = request
83+
84+ def __getattr__ (self , name ):
85+ return getattr (self .monkeypatch , name )
86+
87+ def __call__ (self , path , value = SENTINEL , name = None ,
88+ new = MagicMock , ** kwargs ):
89+ value = self ._value_or_mock (value , new , name , path , ** kwargs )
90+ self .monkeypatch .setattr (path , value )
91+ return value
92+
93+ def object (self , target , attribute , * args , ** kwargs ):
94+ return _wrap_context (
95+ patch .object (target , attribute , * args , ** kwargs ),
96+ self .request )
97+
98+ def _value_or_mock (self , value , new , name , path , ** kwargs ):
99+ if value is SENTINEL :
100+ value = new (name = name or path .rpartition ('.' )[2 ])
101+ for k , v in kwargs .items ():
102+ setattr (value , k , v )
103+ return value
104+
105+ def setattr (self , target , name = SENTINEL , value = SENTINEL , ** kwargs ):
106+ # alias to __call__ with the interface of pytest.monkeypatch.setattr
107+ if value is SENTINEL :
108+ value , name = name , None
109+ return self (target , value , name = name )
110+
111+ def setitem (self , dic , name , value = SENTINEL , new = MagicMock , ** kwargs ):
112+ # same as pytest.monkeypatch.setattr but default value is MagicMock
113+ value = self ._value_or_mock (value , new , name , dic , ** kwargs )
114+ self .monkeypatch .setitem (dic , name , value )
115+ return value
116+
117+ def modules (self , * mods ):
118+ modules = []
119+ for mod in mods :
120+ mod = mod .split ('.' )
121+ modules .extend (reversed ([
122+ '.' .join (mod [:- i ] if i else mod ) for i in range (len (mod ))
123+ ]))
124+ modules = sorted (set (modules ))
125+ return _wrap_context (module_context_manager (* modules ), self .request )
126+
127+
128+ def _wrap_context (context , request ):
129+ ret = context .__enter__ ()
130+
131+ def fin ():
132+ context .__exit__ (* sys .exc_info ())
133+ request .addfinalizer (fin )
134+ return ret
135+
136+
137+ @pytest .fixture ()
138+ def patching (monkeypatch , request ):
139+ """Monkeypath.setattr shortcut.
140+ Example:
141+ .. code-block:: python
142+ >>> def test_foo(patching):
143+ >>> # execv value here will be mock.MagicMock by default.
144+ >>> execv = patching('os.execv')
145+ >>> patching('sys.platform', 'darwin') # set concrete value
146+ >>> patching.setenv('DJANGO_SETTINGS_MODULE', 'x.settings')
147+ >>> # val will be of type mock.MagicMock by default
148+ >>> val = patching.setitem('path.to.dict', 'KEY')
149+ """
150+ return _patching (monkeypatch , request )
151+
152+
34153@pytest .fixture (autouse = True )
35154def test_cases_shortcuts (request , app , patching ):
36155 if request .instance :
0 commit comments