Repository navigation
bpo-34572: change _pickle unpickling to use import rather than retrieving from sys.modules #9047
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
Changes from 1 commit
e47c5e1
595bdbb
f72802a
fa5dbeb
64a7f95
202d4e2
a0425ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import sys | ||
| import importlib | ||
| import os | ||
| import shutil | ||
| import threading | ||
| import unittest | ||
| from test.support import TESTFN, forget | ||
| import pickle | ||
|
|
||
| slow_import_module = """ | ||
| import time | ||
|
|
||
| time.sleep(%(delay)s) | ||
|
|
||
| class ToBePickled(object): | ||
| pass | ||
| """ | ||
|
|
||
|
|
||
| class ThreadedPickleTests(unittest.TestCase): | ||
| def test_pickle_thread_imports(self): | ||
| # https://bugs.python.org/issue34572 | ||
| delay = 0.01 | ||
| os.mkdir(TESTFN) | ||
| self.addCleanup(shutil.rmtree, TESTFN) | ||
| sys.path.insert(0, TESTFN) | ||
| self.addCleanup(sys.path.remove, TESTFN) | ||
| contents = slow_import_module % {'delay': delay} | ||
| with open(os.path.join(TESTFN, "slowimport.py"), "wb") as f: | ||
| f.write(contents.encode('utf-8')) | ||
| self.addCleanup(forget, "slowimport") | ||
|
|
||
| import slowimport | ||
| obj = slowimport.ToBePickled() | ||
| pickle_bytes = pickle.dumps(obj) | ||
| del obj | ||
|
|
||
| # Then try to unpickle two of these simultaneously | ||
| def do_unpickle(): | ||
| results.append(pickle.loads(pickle_bytes)) | ||
|
|
||
| for repeat_test in range(10): | ||
| results = [] | ||
| forget("slowimport") | ||
| self.assertNotIn("slowimport", sys.modules) | ||
| importlib.invalidate_caches() | ||
|
Member
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. Can this affect any of the importlib tests?
Contributor
Author
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. Any advice on how I can address this? FWIW I pretty much copied this test from I haven't tested without that line, so it may not be necessary. I'll see if the test works (i.e. fails without the patch) reliably without it.
Member
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. Actually, I don't know why it would affect the |
||
| t1 = threading.Thread(target=do_unpickle) | ||
| t2 = threading.Thread(target=do_unpickle) | ||
| t1.start() | ||
| t2.start() | ||
| t1.join() | ||
| t2.join() | ||
| self.assertEqual(len(results), 2) | ||
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 test relies on sleep to work. I do not feel comfortable with such tests as it may fail or not work as intended almost certainly in the slowest or more exotic buildbots that we have. Check out, for example, the discussion in #10413 .
As an additional note, it also needs the
reap_threadsdecorator.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 will make the test a bit more complicated, but I think you can avoid
sleepand make this more deterministic by creating a deliberate deadlock like this:I think there's still a race condition there that doesn't make it deterministic - namely that the thread which is unpickling the object needs to reach the "access the imported attribute" stage before lock A is released in order for it to be a good test, but I think if you fiddle around with the locks and threads a little bit you can get it to work.
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'll be more radical and say I don't think we need a test at all, if we can't make it simpler.
Uh oh!
There was an error while loading. Please reload this page.
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.
@pitrou I think removing the test because it's not guaranteed to test the thing it's intended to test would be a mistake. This still successfully tests what it's intending to test with high probability, which means that a regression would be very unlikely to ever hit master. Unlike other tests which cause erroneous failures, this causes erroneous successes, so its flakiness is unlikely to cause any erroneously broken buildbots.
If left in as a "flaky" test, I'd probably view it similar to a
hypothesistest, which is that it's a probabilistic test that never has false negatives - if the test succeeds you are only partially certain that you have no bugs, but if it fails you know you have a bug.Elsewhere you mention adding a comment about why
PyImport_GetModulewas dropped to prevent regressions - if it's worth a comment (which is not guaranteed to work), it's probably worth at least trying to enforce this with a test. Possibly with a comment that says why the test isn't guaranteed to detect regressions.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.
Well, there are corner cases that we fix without writing tests for, because they are too tricky to reproduce. I think this one is a bit borderline: it's possible to reproduce, but nevertheless it needs a complex machinery.
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 certainly agree that if it's likely to have a negative impact on the test suite for reasons other than that it's not guaranteed to fail in the case of a regression the test should probably be considered optional.
I do think it's worth trying to get a test for this in the test suite, even if just as an exercising in providing a good example of how to test a race condition.