- Notifications
You must be signed in to change notification settings - Fork957
Multiple fixes and enhancements for monkey pluggability#1449
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:master
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -119,6 +119,7 @@ def patch_psycopg(event): | ||
from __future__ import absolute_import | ||
from __future__ import print_function | ||
import sys | ||
from importlib import import_module | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I was concerned about this extra import either leading to more imports that might cause issue with monkey-patching, or with it taking time for cases that we might not need it. But on Python 3, it's imported by default anyway during startup, so those are completely moot. On Python 2, it's a trivial file with no imports besides | ||
__all__ = [ | ||
'patch_all', | ||
@@ -255,11 +256,18 @@ def get_original(mod_name, item_name): | ||
_NONE = object() | ||
def patch_item(module, attr, newitem, _patch_module=False): | ||
olditem = getattr(module, attr, _NONE) | ||
if olditem is not _NONE: | ||
saved.setdefault(module.__name__, {}).setdefault(attr, olditem) | ||
setattr(module, attr, newitem) | ||
if _patch_module: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I'm not sure this is a good idea in general. One example that springs to mind is pickling; another is that it may tend to produce very confusing If this is something that's for very specialized use-cases, perhaps that would be better served in the hook functions for the module? (See below for some general comments about underscore-prefixed things.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Reviewing#1447 I see that pickling was specifically mentioned as the motivation for this. I'm still not convinced, I'd need to see a specific example to understand what problem this solves that can't be solved by the object itself. | ||
if olditem is not None and newitem is not None: | ||
try: | ||
newitem.__module__ = olditem.__module__ | ||
except (TypeError, AttributeError): | ||
# We will let this fail quietly | ||
pass | ||
def remove_item(module, attr): | ||
@@ -290,7 +298,8 @@ def patch_module(target_module, source_module, items=None, | ||
_warnings=None, | ||
_notify_will_subscribers=True, | ||
_notify_did_subscribers=True, | ||
_call_hooks=True, | ||
_patch_module=False): | ||
""" | ||
patch_module(target_module, source_module, items=None) | ||
@@ -336,7 +345,8 @@ def patch_module(target_module, source_module, items=None, | ||
return False | ||
for attr in items: | ||
patch_item(target_module, attr, getattr(source_module, attr), | ||
_patch_module=_patch_module) | ||
if _call_hooks: | ||
__call_module_hook(source_module, 'did', target_module, items, _warnings) | ||
@@ -357,11 +367,13 @@ def _patch_module(name, | ||
_warnings=None, | ||
_notify_will_subscribers=True, | ||
_notify_did_subscribers=True, | ||
_call_hooks=True, | ||
_patch_module=False, | ||
_package_prefix='gevent.'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. In the tradition of 'explicit is better than implicit', it would probably be better to make this a required argument instead of defaulting. Instead of an extra argument, what if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I have no problem with any aesthetic API changes (argument requirement, order, naming, refactoring). | ||
gevent_module =import_module(_package_prefix + name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Probably better named There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I have no problem with any aesthetic API changes (argument requirement, order, naming, refactoring). | ||
module_name = getattr(gevent_module, '__target__', name) | ||
target_module =import_module(module_name) | ||
patch_module(target_module, gevent_module, items=items, | ||
_warnings=_warnings, | ||
@@ -386,7 +398,8 @@ def _patch_module(name, | ||
_warnings=_warnings, | ||
_notify_will_subscribers=False, | ||
_notify_did_subscribers=False, | ||
_call_hooks=False, | ||
_patch_module=_patch_module) | ||
saved[alternate_name] = saved[module_name] | ||
return gevent_module, target_module | ||
@@ -951,9 +964,15 @@ def patch_signal(): | ||
_patch_module("signal") | ||
def _get_patch_all_state(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. This function doesn't seem to be used here. Here's my general philosophy about the underscore prefixed functions and arguments in this module: They're an implementation detail, subject to change. (That's obvious.) But we also know that, as external plugins get going, until we feel ready to publish some sort of official monkey-patching API, they'll be used outside the gevent package. If functionality that gevent itself doesn't use is only used by those external plugins, its much more likely that that functionality is going to break in a way that is not caught by the gevent tests, or be refactored away without planning too. So it's best to keep the implementation details to a minimum and be sure that gevent has a use-case for them all. In this case, it looks like it would be easy to make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. This function is supposed to be used externally by plugins. I was hesitant to make it public, but then again maybe I should've. I have no problem with any aesthetic API changes (argument requirement, order, naming, refactoring). | ||
key = '_gevent_saved_patch_all' | ||
return saved.get(key) | ||
def _check_repatching(**module_settings): | ||
_warnings = [] | ||
key = '_gevent_saved_patch_all' | ||
module_settings.update(module_settings['kwargs']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. It's not clear to me what difference this makes. Could you clarify please? It may be missing a test case, or perhaps that's the reason for the broken test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Oh. Silly me. I just reviewed#1447 and see where the problem was mentioned. So this either is the reason for the test failure, or it definitely needs a specific test. | ||
del module_settings['kwargs'] | ||
if saved.get(key, module_settings) != module_settings: | ||
_queue_warning("Patching more than once will result in the union of all True" | ||