-
Notifications
You must be signed in to change notification settings - Fork 173
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
Import submodules accessed by pickled functions #80
Changes from all commits
b31d000
0657c6d
0922273
11709cb
6854eaa
ee40673
3319f50
4b9bfbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,8 @@ | |
import itertools | ||
import platform | ||
import textwrap | ||
import base64 | ||
import subprocess | ||
|
||
try: | ||
# try importing numpy and scipy. These are not hard dependencies and | ||
|
@@ -360,6 +362,92 @@ def f(): | |
self.assertTrue(f2 is f3) | ||
self.assertEqual(f2(), res) | ||
|
||
def test_submodule(self): | ||
# Function that refers (by attribute) to a sub-module of a package. | ||
|
||
# Choose any module NOT imported by __init__ of its parent package | ||
# examples in standard library include: | ||
# - http.cookies, unittest.mock, curses.textpad, xml.etree.ElementTree | ||
|
||
global xml # imitate performing this import at top of file | ||
import xml.etree.ElementTree | ||
def example(): | ||
x = xml.etree.ElementTree.Comment # potential AttributeError | ||
|
||
s = cloudpickle.dumps(example) | ||
|
||
# refresh the environment, i.e., unimport the dependency | ||
del xml | ||
for item in list(sys.modules): | ||
if item.split('.')[0] == 'xml': | ||
del sys.modules[item] | ||
|
||
# deserialise | ||
f = pickle.loads(s) | ||
f() # perform test for error | ||
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 assume that your fix should also handle a case such as follows: global etree
import xml.etree.ElementTree as etree
def example():
x = etree.Comment
... Maybe it would still be worth adding a test to make sure that this import pattern is also supported. 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. Note, such a test would have passed even prior to this pull request. 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. alright but thanks for having added the test as a non-regression test anyway :) |
||
|
||
def test_submodule_closure(self): | ||
# Same as test_submodule except the package is not a global | ||
def scope(): | ||
import xml.etree.ElementTree | ||
def example(): | ||
x = xml.etree.ElementTree.Comment # potential AttributeError | ||
return example | ||
example = scope() | ||
|
||
s = cloudpickle.dumps(example) | ||
|
||
# refresh the environment (unimport dependency) | ||
for item in list(sys.modules): | ||
if item.split('.')[0] == 'xml': | ||
del sys.modules[item] | ||
|
||
f = cloudpickle.loads(s) | ||
f() # test | ||
|
||
def test_multiprocess(self): | ||
# running a function pickled by another process (a la dask.distributed) | ||
def scope(): | ||
import curses.textpad | ||
def example(): | ||
x = xml.etree.ElementTree.Comment | ||
x = curses.textpad.Textbox | ||
return example | ||
global xml | ||
import xml.etree.ElementTree | ||
example = scope() | ||
|
||
s = cloudpickle.dumps(example) | ||
|
||
# choose "subprocess" rather than "multiprocessing" because the latter | ||
# library uses fork to preserve the parent environment. | ||
command = ("import pickle, base64; " | ||
"pickle.loads(base64.b32decode('" + | ||
base64.b32encode(s).decode('ascii') + | ||
"'))()") | ||
assert not subprocess.call([sys.executable, '-c', command]) | ||
|
||
def test_import(self): | ||
# like test_multiprocess except subpackage modules referenced directly | ||
# (unlike test_submodule) | ||
global etree | ||
def scope(): | ||
import curses.textpad as foobar | ||
def example(): | ||
x = etree.Comment | ||
x = foobar.Textbox | ||
return example | ||
example = scope() | ||
import xml.etree.ElementTree as etree | ||
|
||
s = cloudpickle.dumps(example) | ||
|
||
command = ("import pickle, base64; " | ||
"pickle.loads(base64.b32decode('" + | ||
base64.b32encode(s).decode('ascii') + | ||
"'))()") | ||
assert not subprocess.call([sys.executable, '-c', command]) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
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 line creates a regression: #86