77
77
else :
78
78
import builtins
79
79
80
+ OS_MODULE = 'nt' if sys .platform == 'win32' else 'posix'
80
81
PATH_MODULE = 'ntpath' if sys .platform == 'win32' else 'posixpath'
81
82
82
83
def load_doctests (loader , tests , ignore , module ,
@@ -284,7 +285,7 @@ class Patcher(object):
284
285
285
286
IS_WINDOWS = sys .platform in ('win32' , 'cygwin' )
286
287
287
- SKIPNAMES = {'os' , 'path' , 'io' , 'genericpath' }
288
+ SKIPNAMES = {'os' , 'path' , 'io' , 'genericpath' , OS_MODULE , PATH_MODULE }
288
289
if pathlib :
289
290
SKIPNAMES .add ('pathlib' )
290
291
@@ -338,8 +339,32 @@ def __init__(self, additional_skip_names=None,
338
339
module_name )
339
340
self ._fake_module_classes [name ] = fake_module
340
341
342
+ # handle patching function imported separately like
343
+ # `from os import stat`
344
+ # each patched function name has to be looked up separately
345
+ self ._fake_module_functions = {}
346
+ for mod_name , fake_module in self ._fake_module_classes .items ():
347
+ modnames = (
348
+ (mod_name , OS_MODULE ) if mod_name == 'os' else (mod_name ,)
349
+ )
350
+ for fct_name in fake_module .dir ():
351
+ self ._fake_module_functions [fct_name ] = (
352
+ modnames ,
353
+ getattr (fake_module , fct_name ),
354
+ mod_name
355
+ )
356
+ # special handling for functions in os.path
357
+ fake_module = fake_filesystem .FakePathModule
358
+ for fct_name in fake_module .dir ():
359
+ self ._fake_module_functions [fct_name ] = (
360
+ ('genericpath' , PATH_MODULE ),
361
+ getattr (fake_module , fct_name ),
362
+ PATH_MODULE
363
+ )
364
+
341
365
# Attributes set by _refresh()
342
366
self ._modules = {}
367
+ self ._fct_modules = {}
343
368
self ._stubs = None
344
369
self .fs = None
345
370
self .fake_open = None
@@ -366,6 +391,12 @@ def _find_modules(self):
366
391
Later, `setUp()` will stub these with the fake file system
367
392
modules.
368
393
"""
394
+ def is_fct (module , name ):
395
+ fct = module .__dict__ .get (name )
396
+ return (fct is not None and
397
+ (inspect .isfunction (fct ) or inspect .isbuiltin (fct )) and
398
+ fct .__module__ in self ._fake_module_functions [name ][0 ])
399
+
369
400
module_names = list (self ._fake_module_classes .keys ()) + [PATH_MODULE ]
370
401
for name , module in set (sys .modules .items ()):
371
402
try :
@@ -378,6 +409,7 @@ def _find_modules(self):
378
409
# where py.error has no __name__ attribute
379
410
# see https://github.com/pytest-dev/py/issues/73
380
411
continue
412
+
381
413
modules = {name : mod for name , mod in module .__dict__ .items ()
382
414
if inspect .ismodule (mod ) and
383
415
mod .__name__ in module_names
@@ -387,6 +419,11 @@ def _find_modules(self):
387
419
self ._modules .setdefault (name , set ()).add ((module ,
388
420
mod .__name__ ))
389
421
422
+ functions = [name for name in self ._fake_module_functions
423
+ if is_fct (module , name )]
424
+ for name in functions :
425
+ self ._fct_modules .setdefault (name , set ()).add (module )
426
+
390
427
def _refresh (self ):
391
428
"""Renew the fake file system and set the _isStale flag to `False`."""
392
429
if self ._stubs is not None :
@@ -416,10 +453,17 @@ def setUp(self, doctester=None):
416
453
# file() was eliminated in Python3
417
454
self ._stubs .smart_set (builtins , 'file' , self .fake_open )
418
455
self ._stubs .smart_set (builtins , 'open' , self .fake_open )
419
- for name in self ._modules :
420
- for module , attr in self . _modules [ name ] :
456
+ for name , modules in self ._modules . items () :
457
+ for module , attr in modules :
421
458
self ._stubs .smart_set (module , name , self .fake_modules [attr ])
422
459
460
+ for name , modules in self ._fct_modules .items ():
461
+ _ , method , mod_name = self ._fake_module_functions [name ]
462
+ fake_module = self .fake_modules [mod_name ]
463
+ attr = method .__get__ (fake_module , fake_module .__class__ )
464
+ for module in modules :
465
+ self ._stubs .smart_set (module , name , attr )
466
+
423
467
self ._dyn_patcher = DynamicPatcher (self )
424
468
sys .meta_path .insert (0 , self ._dyn_patcher )
425
469
0 commit comments