-
Notifications
You must be signed in to change notification settings - Fork 287
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
Add tests to ensure MultiKernelManager subclass methods are called #627
Add tests to ensure MultiKernelManager subclass methods are called #627
Conversation
jupyter_client/tests/utils.py
Outdated
async def start_kernel(self, kernel_name=None, **kwargs): | ||
self.record('start_kernel') | ||
return await super().start_kernel(kernel_name=kernel_name, **kwargs) |
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.
It looks like we could have a common base class for SyncMKMSubclass
and AsyncMKMSubclass
, rather than duplicating all the methods. All the async methods can be regular functions, they will just return a result (sync) or an awaitable (async).
You could also use a mkm_method
decorator as in:
jupyter_client/jupyter_client/multikernelmanager.py
Lines 27 to 40 in 26a16c0
def kernel_method(f): | |
"""decorator for proxying MKM.method(kernel_id) to individual KMs by ID""" | |
def wrapped(self, kernel_id, *args, **kwargs): | |
# get the kernel | |
km = self.get_kernel(kernel_id) | |
method = getattr(km, f.__name__) | |
# call the kernel's method | |
r = method(*args, **kwargs) | |
# last thing, call anything defined in the actual class method | |
# such as logging messages | |
f(self, kernel_id, *args, **kwargs) | |
# return the method result | |
return r | |
return wrapped |
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.
Thanks @davidbrochart. I'm having trouble visualizing the common base class approach that applies to both sync/async subclasses, could you please give an example?
Regarding the decorator, I'm not very familiar with this kind of thing, would it resemble something like this:
def mkm_recorder(f):
def wrapped(self, *args, **kwargs):
# record this call
self.record(f.__name__)
method = getattr(super(self.__class__, self), f.__name__)
# call the superclass method
r = method(*args, **kwargs)
return r
return wrapped
class AsyncMKMSubclass(RecordCallMixin, AsyncMultiKernelManager):
@mkm_recorder
async def start_kernel(self, kernel_name=None, **kwargs):
""" Record call and defer to superclass """
class SyncMKMSubclass(RecordCallMixin, MultiKernelManager):
@mkm_recorder
def start_kernel(self, kernel_name=None, **kwargs):
""" Record call and defer to superclass """
Thanks.
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 think something like that will do:
class MKMSubclass(RecordCallMixin):
@mkm_recorder
def get_kernel(self, kernel_id):
""" Record call and defer to superclass """
# add all the other common methods here
class SyncMKMSubclass(MKMSubclass, MultiKernelManager):
def _kernel_manager_class_default(self):
return 'jupyter_client.tests.utils.SyncKernelManagerSubclass'
class AsyncMKMSubclass(MKMSubclass, AsyncMultiKernelManager):
def _kernel_manager_class_default(self):
return 'jupyter_client.tests.utils.AsyncKernelManagerSubclass'
Regarding the decorator, this looks fine.
Thanks @davidbrochart - I've pushed an update that implements a decorator. I'd be interested in further refactoring so long as it doesn't affect the goal of ensuring a subclass's methods are called. Hmm, now that I think about it, would this be something accomplished with something like a subclass mixin class? class SyncMKMSubclass(RecordCallMixin, MKMSubclassMixin, MultiKernelManager):
def _kernel_manager_class_default(self):
return 'jupyter_client.tests.utils.SyncKMSubclass'
class AsyncMKMSubclass(RecordCallMixin, MKMSubclassMixin, AsyncMultiKernelManager):
def _kernel_manager_class_default(self):
return 'jupyter_client.tests.utils.AsyncKMSubclass' I'll experiment a bit on that note. Thanks for the ideas! Update: It looks like I'd have to undo the decorator stuff to have the mixin approach work. Since the current approach better-adheres to the respective signatures of the sync/async forms, I think stopping here is good - unless you share some of your magic on this. 😄 |
You can have a look at #628. |
Thank @davidbrochart. Ah - the trick is to use the private attribute |
Yes it was easier to have the super class explicitly named instead of trying to fit into an implicit mechanism, so I took a shortcut here 😄 |
Looks like the CI failures are the result of ipykernel 5.5.1. They literally started occurring as soon as that release was out since my private fork was successful with these changes but happened to pull ipykernel 5.5.0.
Since these failures are unrelated to this PR, I believe we can proceed with this PR's review. |
I deleted ipykernel 5.5.1, cf ipython/ipykernel#607 |
I don't want to be nitpicking, but jupyter_client/jupyter_client/util.py Lines 22 to 25 in 92cb460
|
@blink1073 - thank you. I've restarted the jobs. @davidbrochart - no worries on the 'nitpicking' comment - I agree (with the duplication and don't believe you're being nitpicky 😄 ). However, I'd prefer that refactor be handled outside the scope of this pull request since it isn't necessarily related and has existed for some time (besides the fact that I don't have the bandwidth to deal with that at the moment). Perhaps you could open an issue and someone can contribute those changes? |
No worries Kevin. You're right, we can refactor in a separate PR. Thanks! |
This is a follow-on to #626 to check subclass call sequences relative to the MultiKernelManager base classes.
Also found that CI was skipping the async tests of the recently added
TestKernelManagerShutDownGracefully
resulting in these warnings:which was addressed by adding the
@pytest.mark.asyncio
decorator.