-
Notifications
You must be signed in to change notification settings - Fork 87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added class attribute Patcher.patch_path #130
Conversation
- can be set to False to avoid patching 'path' (see pytest-dev#53) - added optional argument to TestCase constructor to set it
@jmcgeheeiv: Please check if this sufficient for #53. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a test. Perhaps instead of creating an actual file, you could simply set sys.modules['path'] = None
and test whether Patcher
changes it.
"""Creates the test class instance and the stubber used to stub out file system related modules. | ||
|
||
Args: | ||
methodName: the name of the test method (as in unittest.TestCase) | ||
additional_skip_names: names of modules inside of which no module replacement shall be done | ||
(additionally to the hard-coded list: 'os', 'glob', 'path', 'tempfile', 'io') | ||
path_path: if set to False, 'path' modules will not be stubbed out, and 'path' will be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
patch_path, not path_path
"""Creates the test class instance and the stubber used to stub out file system related modules. | ||
|
||
Args: | ||
methodName: the name of the test method (as in unittest.TestCase) | ||
additional_skip_names: names of modules inside of which no module replacement shall be done | ||
(additionally to the hard-coded list: 'os', 'glob', 'path', 'tempfile', 'io') | ||
path_path: if set to False, 'path' modules will not be stubbed out, and 'path' will be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The usage of patch_path
is rather obscure, so let's explain the hell out of it:
patch_path: if False, modules named 'path' will not be patched with the fake 'os.path' module.
Set this to False when you need to import some other module named 'path', for example,
from my_module import path
Irrespective of patch_path, module 'os.path' is still correctly faked if imported the usual way
using 'import os' or 'import os.path'.
Repeat this explanation in the docstring for Patcher
.
@@ -88,6 +90,9 @@ def __init__(self, methodName='runTest'): | |||
super(TestCase, self).__init__(methodName) | |||
if additional_skip_names is not None: | |||
Patcher.SKIPNAMES.update(additional_skip_names) | |||
if not patch_path: | |||
Patcher.patch_path = True | |||
Patcher.SKIPNAMES.remove('path') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reaching inside Patcher
like this breaks encapsulation for Patcher
. Make patch_path
an optional argument to Patcher
, thus hiding the implementation details within Patcher
.
Then again, using instance methods like __init__()
to change class attributes like Patcher.SKIPNAMES
means that one instance can change the behavior of other Patcher
instances. This is bad.
For backward compatability, we must retain class attribute Patcher.SKIPNAMES
, so perhaps add an instance attribute self.skipnames = set(self.SKIPNAMES)
, then conditionally discard 'path' from self.skipnames
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I didn't like this either. I will have another go at this tomorrow, if I find the time, or next week.
@@ -178,7 +189,7 @@ def _findModules(self): | |||
continue | |||
if 'os' in module.__dict__: | |||
self._osModules.add(module) | |||
if 'path' in module.__dict__: | |||
if self.patch_path and 'path' in module.__dict__: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant. Is attribute patch_path
is needed at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the actual check for the replacement of the path module - the removal from SKIPNAMES may or may not be needed additionally.
@@ -88,6 +90,9 @@ def __init__(self, methodName='runTest'): | |||
super(TestCase, self).__init__(methodName) | |||
if additional_skip_names is not None: | |||
Patcher.SKIPNAMES.update(additional_skip_names) | |||
if not patch_path: | |||
Patcher.patch_path = True | |||
Patcher.SKIPNAMES.remove('path') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use set.discard()
so that repeated removals will not raise an exception.
@@ -134,6 +139,12 @@ class Patcher(object): | |||
# it appears that adding 'py', 'pytest', '_pytest' to SKIPNAMES will help | |||
SKIPNAMES = set(['os', 'path', 'tempfile', 'io']) | |||
|
|||
# To avoid stubbing out path because another top=level module named path is present |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I incorporated this excellent explanation into the patch_path
docstring above.
- use instance instead of class attributes - enhanced documentation - added test
@jmcgeheeiv: Please check if the changes and the test are ok, |
Fixed #53. |
path
is stubbed out too aggressively #53)