Skip to content
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

UnpickleableException.dump blows up when exception comes from setuptools #440

Closed
ghost opened this issue Sep 22, 2015 · 15 comments
Closed

Comments

@ghost
Copy link

ghost commented Sep 22, 2015

Originally reported by: embray (Bitbucket: embray, GitHub: embray)


Another odd one, but pretty bad.

I've found that sometimes when running setuptools.sandbox.run_setup, if an exception occurs in the setup like SandboxViolation, when the ExceptionSaver tries to pickle it it will fail because of:

Traceback (most recent call last):
  File "/usr/local/lib/python3.4/site-packages/setuptools/sandbox.py", line 108, in dump
    return pickle.dumps(type), pickle.dumps(exc)
_pickle.PicklingError: Can't pickle <class 'setuptools.sandbox.SandboxViolation'>: it's not the same object as setuptools.sandbox.SandboxViolation

The code, as written, then just tries to repr the exception and wrap in the an UnpickleableException and then pickle that. But that fails too for the same reason:

  File "/usr/local/lib/python3.4/site-packages/setuptools/sandbox.py", line 242, in run_setup
    raise
  File "/usr/local/lib/python3.4/contextlib.py", line 77, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.4/site-packages/setuptools/sandbox.py", line 195, in setup_context
    yield
  File "/usr/local/lib/python3.4/contextlib.py", line 77, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.4/site-packages/setuptools/sandbox.py", line 154, in save_modules
    yield saved
  File "/usr/local/lib/python3.4/site-packages/setuptools/sandbox.py", line 128, in __exit__
    self._saved = UnpickleableException.dump(type, exc)
  File "/usr/local/lib/python3.4/site-packages/setuptools/sandbox.py", line 112, in dump
    return cls.dump(cls, cls(repr(exc)))
  File "/usr/local/lib/python3.4/site-packages/setuptools/sandbox.py", line 108, in dump
    return pickle.dumps(type), pickle.dumps(exc)
_pickle.PicklingError: Can't pickle <class 'setuptools.sandbox.UnpickleableException'>: it's not the same object as setuptools.sandbox.UnpickleableException

This occurs because by the time ExceptionSaver.__exit__ is entered, the hide_setuptools has occurred and there are two copies of the setuptools.sandbox module in play, basically.

This actually tends to lead to a MemoryError before a recursion RuntimeError is reached due to the resulting exponential growth in the size of repr(exc) :(


@ghost
Copy link
Author

ghost commented Sep 22, 2015

Original comment by embray (Bitbucket: embray, GitHub: embray):


By the way I got the above tracebacks only after manually inserting raise statements into the appropriate places to prevent the infinite recursion.

@ghost
Copy link
Author

ghost commented Sep 22, 2015

Original comment by embray (Bitbucket: embray, GitHub: embray):


The following should fix, or at least work around the issue. It's not exactly pretty, but then again this code is not dealing in the realm of sanity in the first place :)

#!diff

diff -r a8635de84a7d setuptools/sandbox.py
--- a/setuptools/sandbox.py     Mon Jun 08 13:37:34 2015 -0400
+++ b/setuptools/sandbox.py     Tue Sep 22 13:54:39 2015 -0400
@@ -107,6 +107,10 @@
         try:
             return pickle.dumps(type), pickle.dumps(exc)
         except Exception:
+            # Prevent an infinite recursion when cls is no longer the
+            # UnpickleableException in sys.modules['setuptools.sandbox']
+            mod = sys.modules[__name__]
+            cls = getattr(mod, cls.__name__)
             return cls.dump(cls, cls(repr(exc)))

@ghost
Copy link
Author

ghost commented Sep 28, 2015

Original comment by embray (Bitbucket: embray, GitHub: embray):


This issue was also just reported here: http://bugs.python.org/setuptools/issue162

@ghost
Copy link
Author

ghost commented Sep 28, 2015

Original comment by embray (Bitbucket: embray, GitHub: embray):


Incidentally, here's an easy way to reproduce this:

$ cat a.py
from setuptools.sandbox import run_setup
run_setup('b.py', [])
$ cat b.py
# Cause a SandboxViolation
open('/etc/blah', 'wb')
$ python a.py

@ghost
Copy link
Author

ghost commented Nov 12, 2015

Original comment by georgevreilly (Bitbucket: georgevreilly, GitHub: georgevreilly):


I'm seeing a similar issue when zope.interface is implicitly installed via setuptools.setup(). The process churns for about a minute until it runs out of memory on a 24GB host.

@ghost
Copy link
Author

ghost commented Nov 13, 2015

Original comment by georgevreilly (Bitbucket: georgevreilly, GitHub: georgevreilly):


If I run pip install -e . instead of python setup.py develop, everything works as expected. See zope.interface issue #28

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


Agreed this is pretty bad. Thanks for the patch suggestion.

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


For reference, the UnpickleableException was added in 45b985d2636f398, released in Setuptools 12.0.

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


Prevent infinite recursion when UnpickleableException occurs in a sandbox context. Fixes #440.

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


Refine test to not make additional references to exceptions in setuptools.sandbox, but instead create a bespoke unpickleable exception. Ref #440.

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


Add test capturing infinite recursion. Ref #440.

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


So while I've resolved the failure here (and included a regression test to prevent it in the future), there's something more sinister going on. These infinite recursion were caused by SandboxViolations, which are pickleable. They're just not pickleable when the instance being pickled is bound to a class that was referenced outside the sandbox. That is, the same issue that caused the infinite recursion in the UnpickleableException also affects SandboxViolations and maybe other Exceptions in the sandbox module itself.

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


Always import for SandboxViolation so it's pickleable. Ref #440.

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


Add another test capturing violated expectation that SandboxViolation would be raised in a sandbox/hidden_setuptools context. Ref #440.

@ghost
Copy link
Author

ghost commented Dec 13, 2015

Original comment by jaraco (Bitbucket: jaraco, GitHub: jaraco):


Released as 18.8.1.

@ghost ghost added critical bug labels Mar 29, 2016
@ghost ghost closed this as completed Mar 29, 2016
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

0 participants