Skip to content

Commit bc74384

Browse files
committed
Add checks to avoid patching debugger modules
- added Patcher.RUNTIME_SKIPMODULES to allow to skip modules whose name starts with specific strings, if the related debuuger is loaded - only done for debugger used in PyCharm and VSCode
1 parent 64861d1 commit bc74384

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ The released versions correspond to PyPI releases.
1717
### Enhancements
1818
* added some support for loading fake modules in `AUTO` patch mode
1919
using `importlib.import_module` (see [#1079](../../issues/1079))
20+
* added some support to avoid patching debugger related modules
21+
(see [#1083](../../issues/1083))
2022

2123
### Performance
2224
* avoid reloading `tempfile` in Posix systems

pyfakefs/fake_filesystem_unittest.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,13 @@ class Patcher:
514514
SKIPMODULES.add(posixpath)
515515
SKIPMODULES.add(fcntl)
516516

517+
# a list of modules detected at run-time
518+
# each tool defines one or more module name prefixes for modules to be skipped
519+
RUNTIME_SKIPMODULES = {
520+
"pydevd": ["_pydevd_", "pydevd", "_pydev_"], # Python debugger (PyCharm/VSCode)
521+
"_jb_runner_tools": ["_jb_"], # JetBrains tools
522+
}
523+
517524
# caches all modules that do not have file system modules or function
518525
# to speed up _find_modules
519526
CACHED_MODULES: Set[ModuleType] = set()
@@ -1045,10 +1052,26 @@ def patch_functions(self) -> None:
10451052
self._stubs.smart_set(module, name, attr)
10461053

10471054
def patch_modules(self) -> None:
1055+
skip_prefix_list = []
1056+
for rt_skip_module, prefixes in self.RUNTIME_SKIPMODULES.items():
1057+
if rt_skip_module in sys.modules:
1058+
skip_prefix_list.extend(prefixes)
1059+
skip_prefixes = tuple(skip_prefix_list)
1060+
10481061
assert self._stubs is not None
10491062
for name, modules in self.FS_MODULES.items():
10501063
for module, attr in modules:
1051-
self._stubs.smart_set(module, name, self.fake_modules[attr])
1064+
try:
1065+
if not skip_prefixes or not module.__name__.startswith(
1066+
skip_prefixes
1067+
):
1068+
self._stubs.smart_set(module, name, self.fake_modules[attr])
1069+
elif attr in self.unfaked_modules:
1070+
self._stubs.smart_set(module, name, self.unfaked_modules[attr])
1071+
except Exception:
1072+
# handle the rare case that a module has no __name__
1073+
pass
1074+
10521075
for name, modules in self.SKIPPED_FS_MODULES.items():
10531076
for module, attr in modules:
10541077
if attr in self.unfaked_modules:

pyfakefs/tests/fake_filesystem_unittest_test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,36 @@ def test_path_succeeds(self):
456456
pyfakefs.tests.import_as_example.return_this_file_path()
457457

458458

459+
class RuntimeSkipModuleTest(fake_filesystem_unittest.TestCase):
460+
"""Emulates skipping a module using RUNTIME_SKIPMODULES.
461+
Not all functionality implemented for skip modules will work here."""
462+
463+
def setUp(self):
464+
Patcher.RUNTIME_SKIPMODULES.update(
465+
{"pyfakefs.tests.import_as_example": ["pyfakefs.tests.import_"]}
466+
)
467+
self.setUpPyfakefs()
468+
469+
def tearDown(self):
470+
del self.patcher.RUNTIME_SKIPMODULES["pyfakefs.tests.import_as_example"]
471+
472+
def test_fake_path_does_not_exist1(self):
473+
self.fs.create_file("foo")
474+
self.assertFalse(pyfakefs.tests.import_as_example.check_if_exists1("foo"))
475+
476+
def test_fake_path_does_not_exist2(self):
477+
self.fs.create_file("foo")
478+
self.assertFalse(pyfakefs.tests.import_as_example.check_if_exists2("foo"))
479+
480+
def test_fake_path_does_not_exist3(self):
481+
self.fs.create_file("foo")
482+
self.assertFalse(pyfakefs.tests.import_as_example.check_if_exists3("foo"))
483+
484+
def test_fake_path_does_not_exist4(self):
485+
self.fs.create_file("foo")
486+
self.assertFalse(pyfakefs.tests.import_as_example.check_if_exists4("foo"))
487+
488+
459489
class FakeExampleModule:
460490
"""Used to patch a function that uses system-specific functions that
461491
cannot be patched automatically."""

0 commit comments

Comments
 (0)