tblib-1.3.2/0000775000175000017500000000000013072512425013052 5ustar ionelionel00000000000000tblib-1.3.2/CONTRIBUTING.rst0000664000175000017500000000523613071773003015521 0ustar ionelionel00000000000000============ Contributing ============ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. Bug reports =========== When `reporting a bug `_ please include: * Your operating system name and version. * Any details about your local setup that might be helpful in troubleshooting. * Detailed steps to reproduce the bug. Documentation improvements ========================== tblib could always use more documentation, whether as part of the official tblib docs, in docstrings, or even on the web in blog posts, articles, and such. Feature requests and feedback ============================= The best way to send feedback is to file an issue at https://github.com/ionelmc/python-tblib/issues. If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. * Remember that this is a volunteer-driven project, and that code contributions are welcome :) Development =========== To set up `python-tblib` for local development: 1. Fork `python-tblib `_ (look for the "Fork" button). 2. Clone your fork locally:: git clone git@github.com:your_name_here/python-tblib.git 3. Create a branch for local development:: git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. 4. When you're done making changes, run all the checks, doc builder and spell checker with `tox `_ one command:: tox 5. Commit your changes and push your branch to GitHub:: git add . git commit -m "Your detailed description of your changes." git push origin name-of-your-bugfix-or-feature 6. Submit a pull request through the GitHub website. Pull Request Guidelines ----------------------- If you need some code review or feedback while you're developing the code just make the pull request. For merging, you should: 1. Include passing tests (run ``tox``) [1]_. 2. Update documentation when there's new API, functionality etc. 3. Add a note to ``CHANGELOG.rst`` about the changes. 4. Add yourself to ``AUTHORS.rst``. .. [1] If you don't have all the necessary python versions available locally you can rely on Travis - it will `run the tests `_ for each change you add in the pull request. It will be slower though ... Tips ---- To run a subset of tests:: tox -e envname -- py.test -k test_myfeature To run all the test environments in *parallel* (you need to ``pip install detox``):: detox tblib-1.3.2/docs/0000775000175000017500000000000013072512425014002 5ustar ionelionel00000000000000tblib-1.3.2/docs/authors.rst0000664000175000017500000000003413071773003016216 0ustar ionelionel00000000000000.. include:: ../AUTHORS.rst tblib-1.3.2/docs/index.rst0000664000175000017500000000036513071773003015647 0ustar ionelionel00000000000000======== Contents ======== .. toctree:: :maxdepth: 2 readme installation usage reference/index contributing authors changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` tblib-1.3.2/docs/usage.rst0000664000175000017500000000007613071773003015643 0ustar ionelionel00000000000000===== Usage ===== To use tblib in a project:: import tblib tblib-1.3.2/docs/installation.rst0000664000175000017500000000012513071773003017233 0ustar ionelionel00000000000000============ Installation ============ At the command line:: pip install tblib tblib-1.3.2/docs/reference/0000775000175000017500000000000013072512425015740 5ustar ionelionel00000000000000tblib-1.3.2/docs/reference/index.rst0000664000175000017500000000007113071773003017577 0ustar ionelionel00000000000000Reference ========= .. toctree:: :glob: tblib* tblib-1.3.2/docs/reference/tblib.rst0000664000175000017500000000013213071773003017562 0ustar ionelionel00000000000000tblib ===== .. testsetup:: from tblib import * .. automodule:: tblib :members: tblib-1.3.2/docs/changelog.rst0000664000175000017500000000003613071773003016462 0ustar ionelionel00000000000000.. include:: ../CHANGELOG.rst tblib-1.3.2/docs/readme.rst0000664000175000017500000000003313071773003015765 0ustar ionelionel00000000000000.. include:: ../README.rst tblib-1.3.2/docs/conf.py0000664000175000017500000000257713072431554015317 0ustar ionelionel00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import os extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.coverage', 'sphinx.ext.doctest', 'sphinx.ext.extlinks', 'sphinx.ext.ifconfig', 'sphinx.ext.napoleon', 'sphinx.ext.todo', 'sphinx.ext.viewcode', ] if os.getenv('SPELLCHECK'): extensions += 'sphinxcontrib.spelling', spelling_show_suggestions = True spelling_lang = 'en_US' source_suffix = '.rst' master_doc = 'index' project = 'tblib' year = '2013-2017' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) version = release = '1.3.2' pygments_style = 'trac' templates_path = ['.'] extlinks = { 'issue': ('https://github.com/ionelmc/python-tblib/issues/%s', '#'), 'pr': ('https://github.com/ionelmc/python-tblib/pull/%s', 'PR #'), } import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] html_theme_options = { 'githuburl': 'https://github.com/ionelmc/python-tblib/' } html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' html_split_index = False html_sidebars = { '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } html_short_title = '%s-%s' % (project, version) napoleon_use_ivar = True napoleon_use_rtype = False napoleon_use_param = False tblib-1.3.2/docs/spelling_wordlist.txt0000664000175000017500000000015513071773003020310 0ustar ionelionel00000000000000builtin builtins classmethod staticmethod classmethods staticmethods args kwargs callstack Changelog Indices tblib-1.3.2/docs/contributing.rst0000664000175000017500000000004113071773003017236 0ustar ionelionel00000000000000.. include:: ../CONTRIBUTING.rst tblib-1.3.2/docs/requirements.txt0000664000175000017500000000005613071773003017267 0ustar ionelionel00000000000000sphinx>=1.3 sphinx-py3doc-enhanced-theme -e . tblib-1.3.2/.travis.yml0000664000175000017500000000427613072261404015172 0ustar ionelionel00000000000000language: python sudo: false cache: pip env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all matrix: - TOXENV=check - TOXENV=docs matrix: include: - python: 2.7 env: - TOXENV=py27,report,coveralls,codecov - python: 3.3 env: - TOXENV=py33,report,coveralls,codecov - python: 3.4 env: - TOXENV=py34,report,coveralls,codecov - python: 3.5 env: - TOXENV=py35,report,coveralls,codecov - python: 3.6 env: - TOXENV=py36,report,coveralls,codecov - python: pypy env: - TOXENV=pypy,report,coveralls,codecov - python: pypy3 env: - TOXENV=pypy3,report,coveralls,codecov before_install: - python --version - uname -a - lsb_release -a install: - pip install -U tox virtualenv - virtualenv --version - easy_install --version - pip --version - tox --version - | set -ex if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then (cd $HOME wget https://bitbucket.org/squeaky/portable-pypy/downloads/pypy-5.7.1-linux_x86_64-portable.tar.bz2 tar xf pypy-5.7.1-linux_x86_64-portable.tar.bz2 pypy-5.7.1-linux_x86_64-portable/bin/pypy -m ensurepip pypy-5.7.1-linux_x86_64-portable/bin/pypy -m pip install -U virtualenv) export PATH=$HOME/pypy-5.7.1-linux_x86_64-portable/bin/:$PATH export TOXPYTHON=$HOME/pypy-5.7.1-linux_x86_64-portable/bin/pypy fi if [[ $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then (cd $HOME wget https://bitbucket.org/squeaky/portable-pypy/downloads/pypy3.5-5.7.1-beta-linux_x86_64-portable.tar.bz2 tar xf pypy3.5-5.7.1-beta-linux_x86_64-portable.tar.bz2 pypy3.5-5.7.1-beta-linux_x86_64-portable/bin/pypy3 -m ensurepip pypy3.5-5.7.1-beta-linux_x86_64-portable/bin/pypy3 -m pip install -U virtualenv) export PATH=$HOME/pypy3.5-5.7.1-beta-linux_x86_64-portable/bin/:$PATH export TOXPYTHON=$HOME/pypy3.5-5.7.1-beta-linux_x86_64-portable/bin/pypy3 fi set +x script: - tox -v after_failure: - more .tox/log/* | cat - more .tox/*/log/* | cat notifications: email: on_success: never on_failure: always tblib-1.3.2/.editorconfig0000664000175000017500000000032713071773003015531 0ustar ionelionel00000000000000# see http://editorconfig.org root = true [*] end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 charset = utf-8 [*.{bat,cmd,ps1}] end_of_line = crlf tblib-1.3.2/src/0000775000175000017500000000000013072512425013641 5ustar ionelionel00000000000000tblib-1.3.2/src/tblib.egg-info/0000775000175000017500000000000013072512425016427 5ustar ionelionel00000000000000tblib-1.3.2/src/tblib.egg-info/top_level.txt0000664000175000017500000000000613072512425021155 0ustar ionelionel00000000000000tblib tblib-1.3.2/src/tblib.egg-info/not-zip-safe0000664000175000017500000000000113072512425020655 0ustar ionelionel00000000000000 tblib-1.3.2/src/tblib.egg-info/dependency_links.txt0000664000175000017500000000000113072512425022475 0ustar ionelionel00000000000000 tblib-1.3.2/src/tblib.egg-info/SOURCES.txt0000664000175000017500000000156513072512425020322 0ustar ionelionel00000000000000.bumpversion.cfg .cookiecutterrc .coveragerc .editorconfig .travis.yml AUTHORS.rst CHANGELOG.rst CONTRIBUTING.rst LICENSE MANIFEST.in README.rst appveyor.yml setup.cfg setup.py tox.ini ci/appveyor-bootstrap.py ci/appveyor-download.py ci/appveyor-with-compiler.cmd ci/bootstrap.py ci/templates/.travis.yml ci/templates/appveyor.yml docs/authors.rst docs/changelog.rst docs/conf.py docs/contributing.rst docs/index.rst docs/installation.rst docs/readme.rst docs/requirements.txt docs/spelling_wordlist.txt docs/usage.rst docs/reference/index.rst docs/reference/tblib.rst src/tblib/__init__.py src/tblib/cpython.py src/tblib/decorators.py src/tblib/pickling_support.py src/tblib.egg-info/PKG-INFO src/tblib.egg-info/SOURCES.txt src/tblib.egg-info/dependency_links.txt src/tblib.egg-info/not-zip-safe src/tblib.egg-info/top_level.txt tests/badmodule.py tests/badsyntax.py tests/examples.pytblib-1.3.2/src/tblib.egg-info/PKG-INFO0000664000175000017500000006114713072512425017535 0ustar ionelionel00000000000000Metadata-Version: 1.1 Name: tblib Version: 1.3.2 Summary: Traceback serialization library. Home-page: https://github.com/ionelmc/python-tblib Author: Ionel Cristian Mărieș Author-email: contact@ionelmc.ro License: BSD Description: ======== Overview ======== Traceback serialization library. * Free software: BSD license It allows you to: * `Pickle `_ tracebacks and raise exceptions with pickled tracebacks in different processes. This allows better error handling when running code over multiple processes (imagine multiprocessing, billiard, futures, celery etc). * Create traceback objects from strings (the ``from_string`` method). *No pickling is used*. * Serialize tracebacks to/from plain dicts (the ``from_dict`` and ``to_dict`` methods). *No pickling is used*. * Raise the tracebacks created from the aforementioned sources. **Again, note that using the pickle support is completely optional. You are solely responsible for security problems should you decide to use the pickle support.** Installation ============ :: pip install tblib Documentation ============= .. contents:: :local: Pickling tracebacks ~~~~~~~~~~~~~~~~~~~ **Note**: The traceback objects that come out are stripped of some attributes (like variables). But you'll be able to raise exceptions with those tracebacks or print them - that should cover 99% of the usecases. :: >>> from tblib import pickling_support >>> pickling_support.install() >>> import pickle, sys >>> def inner_0(): ... raise Exception('fail') ... >>> def inner_1(): ... inner_0() ... >>> def inner_2(): ... inner_1() ... >>> try: ... inner_2() ... except: ... s1 = pickle.dumps(sys.exc_info()) ... >>> len(s1) > 1 True >>> try: ... inner_2() ... except: ... s2 = pickle.dumps(sys.exc_info(), protocol=pickle.HIGHEST_PROTOCOL) ... >>> len(s2) > 1 True >>> try: ... import cPickle ... except ImportError: ... import pickle as cPickle >>> try: ... inner_2() ... except: ... s3 = cPickle.dumps(sys.exc_info(), protocol=pickle.HIGHEST_PROTOCOL) ... >>> len(s3) > 1 True Unpickling ~~~~~~~~~~ :: >>> pickle.loads(s1) (<...Exception'>, Exception('fail',), ) >>> pickle.loads(s2) (<...Exception'>, Exception('fail',), ) >>> pickle.loads(s3) (<...Exception'>, Exception('fail',), ) Raising ~~~~~~~ :: >>> from six import reraise >>> reraise(*pickle.loads(s1)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail >>> reraise(*pickle.loads(s2)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail >>> reraise(*pickle.loads(s3)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail What if we have a local stack, does it show correctly ? ------------------------------------------------------- Yes it does:: >>> exc_info = pickle.loads(s3) >>> def local_0(): ... reraise(*exc_info) ... >>> def local_1(): ... local_0() ... >>> def local_2(): ... local_1() ... >>> local_2() Traceback (most recent call last): File "...doctest.py", line ..., in __run compileflags, 1) in test.globs File "", line 1, in local_2() File "", line 2, in local_2 local_1() File "", line 2, in local_1 local_0() File "", line 2, in local_0 reraise(*exc_info) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail It also supports more contrived scenarios ----------------------------------------- Like tracebacks with syntax errors:: >>> from tblib import Traceback >>> from examples import bad_syntax >>> try: ... bad_syntax() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in reraise(et, ev, tb.as_traceback()) File "", line 2, in bad_syntax() File "...tests...examples.py", line 18, in bad_syntax import badsyntax File "...tests...badsyntax.py", line 5 is very bad ^ SyntaxError: invalid syntax Or other import failures:: >>> from examples import bad_module >>> try: ... bad_module() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in reraise(et, ev, tb.as_traceback()) File "", line 2, in bad_module() File "...tests...examples.py", line 23, in bad_module import badmodule File "...tests...badmodule.py", line 3, in raise Exception("boom!") Exception: boom! Or a traceback that's caused by exceeding the recursion limit (here we're forcing the type and value to have consistency across platforms):: >>> def f(): f() >>> try: ... f() ... except RuntimeError: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(RuntimeError, RuntimeError("maximum recursion depth exceeded"), tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in f def f(): f() File "", line 1, in f def f(): f() File "", line 1, in f def f(): f() ... RuntimeError: maximum recursion depth exceeded Reference ~~~~~~~~~ tblib.Traceback --------------- It is used by the ``pickling_support``. You can use it too if you want more flexibility:: >>> from tblib import Traceback >>> try: ... inner_2() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail tblib.Traceback.to_dict ``````````````````````` You can use the ``to_dict`` method and the ``from_dict`` classmethod to convert a Traceback into and from a dictionary serializable by the stdlib json.JSONDecoder:: >>> import json >>> from pprint import pprint >>> try: ... inner_2() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... tb_dict = tb.to_dict() ... pprint(tb_dict) {'tb_frame': {'f_code': {'co_filename': '', 'co_name': ''}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_2'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_1'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_0'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': None}}}} tblib.Traceback.from_dict ````````````````````````` Building on the previous example:: >>> tb_json = json.dumps(tb_dict) >>> tb = Traceback.from_dict(json.loads(tb_json)) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail tblib.Traceback.from_string ``````````````````````````` :: >>> tb = Traceback.from_string(""" ... File "skipped.py", line 123, in func_123 ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: fail If you use the ``strict=False`` option then parsing is a bit more lax:: >>> tb = Traceback.from_string(""" ... File "bogus.py", line 123, in bogus ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """, strict=False) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "bogus.py", line 123, in bogus File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: fail tblib.decorators.return_error ----------------------------- :: >>> from tblib.decorators import return_error >>> inner_2r = return_error(inner_2) >>> e = inner_2r() >>> e >>> e.reraise() Traceback (most recent call last): ... File "", line 1, in e.reraise() File "...tblib...decorators.py", line 19, in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line 25, in return_exceptions_wrapper return func(*args, **kwargs) File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail How's this useful ? Imagine you're using multiprocessing like this:: >>> import traceback >>> from multiprocessing import Pool >>> from examples import func_a >>> if sys.version_info[:2] >= (3, 4): ... import multiprocessing.pool ... # Undo the fix for http://bugs.python.org/issue13831 so that we can see the effects of our change. ... # because Python 3.4 will show the remote traceback (but as a string sadly) ... multiprocessing.pool.ExceptionWithTraceback = lambda e, t: e >>> pool = Pool() >>> try: ... for i in pool.map(func_a, range(5)): ... print(i) ... except: ... print(traceback.format_exc()) ... Traceback (most recent call last): File "", line 2, in for i in pool.map(func_a, range(5)): File "...multiprocessing...pool.py", line ..., in map ... File "...multiprocessing...pool.py", line ..., in get ... Exception: Guessing time ! >>> pool.terminate() Not very useful is it? Let's sort this out:: >>> from tblib.decorators import apply_with_return_error, Error >>> from itertools import repeat >>> pool = Pool() >>> try: ... for i in pool.map(apply_with_return_error, zip(repeat(func_a), range(5))): ... if isinstance(i, Error): ... i.reraise() ... else: ... print(i) ... except: ... print(traceback.format_exc()) ... Traceback (most recent call last): File "", line 4, in i.reraise() File "...tblib...decorators.py", line ..., in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line ..., in return_exceptions_wrapper return func(*args, **kwargs) File "...tblib...decorators.py", line ..., in apply_with_return_error return args[0](*args[1:]) File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: Guessing time ! >>> pool.terminate() Much better ! What if we have a local call stack ? ```````````````````````````````````` :: >>> def local_0(): ... pool = Pool() ... for i in pool.map(apply_with_return_error, zip(repeat(func_a), range(5))): ... if isinstance(i, Error): ... i.reraise() ... else: ... print(i) ... >>> def local_1(): ... local_0() ... >>> def local_2(): ... local_1() ... >>> try: ... local_2() ... except: ... print(traceback.format_exc()) Traceback (most recent call last): File "", line 2, in local_2() File "", line 2, in local_2 local_1() File "", line 2, in local_1 local_0() File "", line 5, in local_0 i.reraise() File "...tblib...decorators.py", line 20, in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line 27, in return_exceptions_wrapper return func(*args, **kwargs) File "...tblib...decorators.py", line 47, in apply_with_return_error return args[0](*args[1:]) File "...tests...examples.py", line 2, in func_a func_b() File "...tests...examples.py", line 6, in func_b func_c() File "...tests...examples.py", line 10, in func_c func_d() File "...tests...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: Guessing time ! Other weird stuff ````````````````` Clearing traceback works (Python 3.4 and up):: >>> tb = Traceback.from_string(""" ... File "skipped.py", line 123, in func_123 ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """) >>> import traceback, sys >>> if sys.version_info > (3, 4): ... traceback.clear_frames(tb) Credits ======= * `mitsuhiko/jinja2 `_ for figuring a way to create traceback objects. Changelog ========= 1.3.2 (2017-04-09) ~~~~~~~~~~~~~~~~~~ * Add support for PyPy3.5-5.7.1-beta. Previously ``AttributeError: 'Frame' object has no attribute 'clear'`` could be raised. See PyPy issue `#2532 `_. 1.3.1 (2017-03-27) ~~~~~~~~~~~~~~~~~~ * Fixed handling for tracebacks due to exceeding the recursion limit. Fixes `#15 `_. 1.3.0 (2016-03-08) ~~~~~~~~~~~~~~~~~~ * Added ``Traceback.from_string``. 1.2.0 (2015-12-18) ~~~~~~~~~~~~~~~~~~ * Fixed handling for tracebacks from generators and other internal improvements and optimizations. Contributed by DRayX in `#10 `_ and `#11 `_. 1.1.0 (2015-07-27) ~~~~~~~~~~~~~~~~~~ * Added support for Python 2.6. Contributed by Arcadiy Ivanov in `#8 `_. 1.0.0 (2015-03-30) ~~~~~~~~~~~~~~~~~~ * Added ``to_dict`` method and ``from_dict`` classmethod on Tracebacks. Contributed by beckjake in `#5 `_. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: Unix Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Utilities tblib-1.3.2/src/tblib/0000775000175000017500000000000013072512425014735 5ustar ionelionel00000000000000tblib-1.3.2/src/tblib/__init__.py0000664000175000017500000001475113072431554017061 0ustar ionelionel00000000000000import re import sys from types import CodeType from types import TracebackType try: from __pypy__ import tproxy except ImportError: tproxy = None try: from .cpython import tb_set_next except ImportError: tb_set_next = None if not tb_set_next and not tproxy: raise ImportError("Cannot use tblib. Runtime not supported.") __version__ = '1.3.2' __all__ = 'Traceback', PY3 = sys.version_info[0] == 3 FRAME_RE = re.compile(r'^\s*File "(?P.+)", line (?P\d+)(, in (?P.+))?$') class _AttrDict(dict): __slots__ = () __getattr__ = dict.__getitem__ # noinspection PyPep8Naming class __traceback_maker(Exception): pass class TracebackParseError(Exception): pass class Code(object): def __init__(self, code): self.co_filename = code.co_filename self.co_name = code.co_name class Frame(object): def __init__(self, frame): self.f_globals = dict([ (k, v) for k, v in frame.f_globals.items() if k in ("__file__", "__name__") ]) self.f_code = Code(frame.f_code) def clear(self): # For compatibility with PyPy 3.5; # clear() was added to frame in Python 3.4 # and is called by traceback.clear_frames(), which # in turn is called by unittest.TestCase.assertRaises pass class Traceback(object): tb_next = None def __init__(self, tb): self.tb_frame = Frame(tb.tb_frame) # noinspection SpellCheckingInspection self.tb_lineno = int(tb.tb_lineno) # Build in place to avoid exceeding the recursion limit tb = tb.tb_next prev_traceback = self cls = type(self) while tb is not None: traceback = object.__new__(cls) traceback.tb_frame = Frame(tb.tb_frame) traceback.tb_lineno = int(tb.tb_lineno) prev_traceback.tb_next = traceback prev_traceback = traceback tb = tb.tb_next def as_traceback(self): if tproxy: return tproxy(TracebackType, self.__tproxy_handler) if not tb_set_next: raise RuntimeError("Unsupported Python interpreter!") current = self top_tb = None tb = None while current: f_code = current.tb_frame.f_code code = compile('\n' * (current.tb_lineno - 1) + 'raise __traceback_maker', current.tb_frame.f_code.co_filename, 'exec') if PY3: code = CodeType( 0, code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, f_code.co_filename, f_code.co_name, code.co_firstlineno, code.co_lnotab, (), () ) else: code = CodeType( 0, code.co_nlocals, code.co_stacksize, code.co_flags, code.co_code, code.co_consts, code.co_names, code.co_varnames, f_code.co_filename.encode(), f_code.co_name.encode(), code.co_firstlineno, code.co_lnotab, (), () ) # noinspection PyBroadException try: exec(code, current.tb_frame.f_globals, {}) except: next_tb = sys.exc_info()[2].tb_next if top_tb is None: top_tb = next_tb if tb is not None: tb_set_next(tb, next_tb) tb = next_tb del next_tb current = current.tb_next try: return top_tb finally: del top_tb del tb # noinspection SpellCheckingInspection def __tproxy_handler(self, operation, *args, **kwargs): if operation in ('__getattribute__', '__getattr__'): if args[0] == 'tb_next': return self.tb_next and self.tb_next.as_traceback() else: return getattr(self, args[0]) else: return getattr(self, operation)(*args, **kwargs) def to_dict(self): """Convert a Traceback into a dictionary representation""" if self.tb_next is None: tb_next = None else: tb_next = self.tb_next.to_dict() code = { 'co_filename': self.tb_frame.f_code.co_filename, 'co_name': self.tb_frame.f_code.co_name, } frame = { 'f_globals': self.tb_frame.f_globals, 'f_code': code, } return { 'tb_frame': frame, 'tb_lineno': self.tb_lineno, 'tb_next': tb_next, } @classmethod def from_dict(cls, dct): if dct['tb_next']: tb_next = cls.from_dict(dct['tb_next']) else: tb_next = None code = _AttrDict( co_filename=dct['tb_frame']['f_code']['co_filename'], co_name=dct['tb_frame']['f_code']['co_name'], ) frame = _AttrDict( f_globals=dct['tb_frame']['f_globals'], f_code=code, ) tb = _AttrDict( tb_frame=frame, tb_lineno=dct['tb_lineno'], tb_next=tb_next, ) return cls(tb) @classmethod def from_string(cls, string, strict=True): frames = [] header = strict for line in string.splitlines(): line = line.rstrip() if header: if line == 'Traceback (most recent call last):': header = False continue frame_match = FRAME_RE.match(line) if frame_match: frames.append(frame_match.groupdict()) elif line.startswith(' '): pass elif strict: break # traceback ended if frames: previous = None for frame in reversed(frames): previous = _AttrDict( frame, tb_frame=_AttrDict( frame, f_globals=_AttrDict( __file__=frame['co_filename'], __name__='?', ), f_code=_AttrDict(frame), ), tb_next=previous, ) return cls(previous) else: raise TracebackParseError("Could not find any frames in %r." % string) tblib-1.3.2/src/tblib/cpython.py0000664000175000017500000000451513071773003017000 0ustar ionelionel00000000000000""" Taken verbatim from Jinja2. https://github.com/mitsuhiko/jinja2/blob/master/jinja2/debug.py#L267 """ import platform import sys def _init_ugly_crap(): """This function implements a few ugly things so that we can patch the traceback objects. The function returned allows resetting `tb_next` on any python traceback object. Do not attempt to use this on non cpython interpreters """ import ctypes from types import TracebackType # figure out side of _Py_ssize_t if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): _Py_ssize_t = ctypes.c_int64 else: _Py_ssize_t = ctypes.c_int # regular python class _PyObject(ctypes.Structure): pass _PyObject._fields_ = [ ('ob_refcnt', _Py_ssize_t), ('ob_type', ctypes.POINTER(_PyObject)) ] # python with trace if hasattr(sys, 'getobjects'): class _PyObject(ctypes.Structure): pass _PyObject._fields_ = [ ('_ob_next', ctypes.POINTER(_PyObject)), ('_ob_prev', ctypes.POINTER(_PyObject)), ('ob_refcnt', _Py_ssize_t), ('ob_type', ctypes.POINTER(_PyObject)) ] class _Traceback(_PyObject): pass _Traceback._fields_ = [ ('tb_next', ctypes.POINTER(_Traceback)), ('tb_frame', ctypes.POINTER(_PyObject)), ('tb_lasti', ctypes.c_int), ('tb_lineno', ctypes.c_int) ] def tb_set_next(tb, next): """Set the tb_next attribute of a traceback object.""" if not (isinstance(tb, TracebackType) and (next is None or isinstance(next, TracebackType))): raise TypeError('tb_set_next arguments must be traceback objects') obj = _Traceback.from_address(id(tb)) if tb.tb_next is not None: old = _Traceback.from_address(id(tb.tb_next)) old.ob_refcnt -= 1 if next is None: obj.tb_next = ctypes.POINTER(_Traceback)() else: next = _Traceback.from_address(id(next)) next.ob_refcnt += 1 obj.tb_next = ctypes.pointer(next) return tb_set_next tb_set_next = None try: if platform.python_implementation() == 'CPython': tb_set_next = _init_ugly_crap() except Exception as exc: sys.stderr.write("Failed to initialize cpython support: {!r}".format(exc)) del _init_ugly_crap tblib-1.3.2/src/tblib/pickling_support.py0000664000175000017500000000106113071773003020701 0ustar ionelionel00000000000000try: import copy_reg except ImportError: import copyreg as copy_reg from types import TracebackType from . import Frame from . import Traceback def unpickle_traceback(tb_frame, tb_lineno, tb_next): ret = object.__new__(Traceback) ret.tb_frame = tb_frame ret.tb_lineno = tb_lineno ret.tb_next = tb_next return ret.as_traceback() def pickle_traceback(tb): return unpickle_traceback, (Frame(tb.tb_frame), tb.tb_lineno, tb.tb_next and Traceback(tb.tb_next)) def install(): copy_reg.pickle(TracebackType, pickle_traceback) tblib-1.3.2/src/tblib/decorators.py0000664000175000017500000000206413071773003017456 0ustar ionelionel00000000000000import sys from functools import wraps from six import reraise from . import Traceback class Error(object): def __init__(self, exc_type, exc_value, traceback): self.exc_type = exc_type self.exc_value = exc_value self.__traceback = Traceback(traceback) @property def traceback(self): return self.__traceback.as_traceback() def reraise(self): reraise(self.exc_type, self.exc_value, self.traceback) def return_error(func, exc_type=Exception): @wraps(func) def return_exceptions_wrapper(*args, **kwargs): try: return func(*args, **kwargs) except exc_type: return Error(*sys.exc_info()) return return_exceptions_wrapper returns_error = return_errors = returns_errors = return_error # cause I make too many typos @return_error def apply_with_return_error(args): """ args is a tuple where the first argument is a callable. eg:: apply_with_return_error((func, 1, 2, 3)) - this will call func(1, 2, 3) """ return args[0](*args[1:]) tblib-1.3.2/ci/0000775000175000017500000000000013072512425013445 5ustar ionelionel00000000000000tblib-1.3.2/ci/bootstrap.py0000775000175000017500000000400513071773003016036 0ustar ionelionel00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals import os import sys from os.path import abspath from os.path import dirname from os.path import exists from os.path import join if __name__ == "__main__": base_path = dirname(dirname(abspath(__file__))) print("Project path: {0}".format(base_path)) env_path = join(base_path, ".tox", "bootstrap") if sys.platform == "win32": bin_path = join(env_path, "Scripts") else: bin_path = join(env_path, "bin") if not exists(env_path): import subprocess print("Making bootstrap env in: {0} ...".format(env_path)) try: subprocess.check_call(["virtualenv", env_path]) except subprocess.CalledProcessError: subprocess.check_call([sys.executable, "-m", "virtualenv", env_path]) print("Installing `jinja2` into bootstrap environment...") subprocess.check_call([join(bin_path, "pip"), "install", "jinja2"]) activate = join(bin_path, "activate_this.py") # noinspection PyCompatibility exec(compile(open(activate, "rb").read(), activate, "exec"), dict(__file__=activate)) import jinja2 import subprocess jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True ) tox_environments = [ line.strip() # WARNING: 'tox' must be installed globally or in the project's virtualenv for line in subprocess.check_output(['tox', '--listenvs'], universal_newlines=True).splitlines() ] tox_environments = [line for line in tox_environments if line not in ['clean', 'report', 'docs', 'check']] for name in os.listdir(join("ci", "templates")): with open(join(base_path, name), "w") as fh: fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) print("Wrote {}".format(name)) print("DONE.") tblib-1.3.2/ci/appveyor-with-compiler.cmd0000664000175000017500000000307613071773003020566 0ustar ionelionel00000000000000:: To build extensions for 64 bit Python 3, we need to configure environment :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) :: :: To build extensions for 64 bit Python 2, we need to configure environment :: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) :: :: 32 bit builds do not require specific environment configurations. :: :: Note: this script needs to be run with the /E:ON and /V:ON flags for the :: cmd interpreter, at least for (SDK v7.0) :: :: More details at: :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows :: http://stackoverflow.com/a/13751649/163740 :: :: Author: Olivier Grisel :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows SET WIN_WDK="c:\Program Files (x86)\Windows Kits\10\Include\wdf" ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% IF "%PYTHON_VERSION%"=="3.5" ( IF EXIST %WIN_WDK% ( REM See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ REN %WIN_WDK% 0wdf ) GOTO main ) IF "%PYTHON_ARCH%"=="32" ( GOTO main ) SET DISTUTILS_USE_SDK=1 SET MSSdk=1 "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% CALL "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release :main ECHO Executing: %COMMAND_TO_RUN% CALL %COMMAND_TO_RUN% || EXIT 1 tblib-1.3.2/ci/appveyor-bootstrap.py0000664000175000017500000001034613071773003017703 0ustar ionelionel00000000000000""" AppVeyor will at least have few Pythons around so there's no point of implementing a bootstrapper in PowerShell. This is a port of https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 with various fixes and improvements that just weren't feasible to implement in PowerShell. """ from __future__ import print_function from os import environ from os.path import exists from subprocess import check_call try: from urllib.request import urlretrieve except ImportError: from urllib import urlretrieve BASE_URL = "https://www.python.org/ftp/python/" GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" GET_PIP_PATH = "C:\get-pip.py" URLS = { ("2.7", "64"): BASE_URL + "2.7.10/python-2.7.13.amd64.msi", ("2.7", "32"): BASE_URL + "2.7.10/python-2.7.13.msi", # NOTE: no .msi installer for 3.3.6 ("3.3", "64"): BASE_URL + "3.3.3/python-3.3.5.amd64.msi", ("3.3", "32"): BASE_URL + "3.3.3/python-3.3.5.msi", ("3.4", "64"): BASE_URL + "3.4.3/python-3.4.6.amd64.msi", ("3.4", "32"): BASE_URL + "3.4.3/python-3.4.6.msi", ("3.5", "64"): BASE_URL + "3.5.0/python-3.5.3-amd64.exe", ("3.5", "32"): BASE_URL + "3.5.0/python-3.5.3.exe", ("3.6", "64"): BASE_URL + "3.6.0/python-3.6.0-amd64.exe", ("3.6", "32"): BASE_URL + "3.6.0/python-3.6.0.exe", } INSTALL_CMD = { # Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. "2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], "3.3": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], "3.4": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], "3.5": [["{path}", "/quiet", "TargetDir={home}"]], "3.6": [["{path}", "/quiet", "TargetDir={home}"]], } def download_file(url, path): print("Downloading: {} (into {})".format(url, path)) progress = [0, 0] def report(count, size, total): progress[0] = count * size if progress[0] - progress[1] > 1000000: progress[1] = progress[0] print("Downloaded {:,}/{:,} ...".format(progress[1], total)) dest, _ = urlretrieve(url, path, reporthook=report) return dest def install_python(version, arch, home): print("Installing Python", version, "for", arch, "bit architecture to", home) if exists(home): return path = download_python(version, arch) print("Installing", path, "to", home) success = False for cmd in INSTALL_CMD[version]: cmd = [part.format(home=home, path=path) for part in cmd] print("Running:", " ".join(cmd)) try: check_call(cmd) except Exception as exc: print("Failed command", cmd, "with:", exc) if exists("install.log"): with open("install.log") as fh: print(fh.read()) else: success = True if success: print("Installation complete!") else: print("Installation failed") def download_python(version, arch): for _ in range(3): try: return download_file(URLS[version, arch], "installer.exe") except Exception as exc: print("Failed to download:", exc) print("Retrying ...") def install_pip(home): pip_path = home + "/Scripts/pip.exe" python_path = home + "/python.exe" if exists(pip_path): print("pip already installed.") else: print("Installing pip...") download_file(GET_PIP_URL, GET_PIP_PATH) print("Executing:", python_path, GET_PIP_PATH) check_call([python_path, GET_PIP_PATH]) def install_packages(home, *packages): cmd = [home + "/Scripts/pip.exe", "install"] cmd.extend(packages) check_call(cmd) if __name__ == "__main__": install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], environ['PYTHON_HOME']) install_pip(environ['PYTHON_HOME']) install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", "tox", "virtualenv>=13.1.0") tblib-1.3.2/ci/templates/0000775000175000017500000000000013072512425015443 5ustar ionelionel00000000000000tblib-1.3.2/ci/templates/.travis.yml0000664000175000017500000000356513072261404017563 0ustar ionelionel00000000000000language: python sudo: false cache: pip env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all matrix: - TOXENV=check - TOXENV=docs matrix: include: {% for env in tox_environments %} - python: {{ env.split('-')[0] if env.startswith('pypy') else '{0[2]}.{0[3]}'.format(env) }} env: - TOXENV={{ env }},report,coveralls,codecov {% endfor %} before_install: - python --version - uname -a - lsb_release -a install: - pip install -U tox virtualenv - virtualenv --version - easy_install --version - pip --version - tox --version - | set -ex if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then (cd $HOME wget https://bitbucket.org/squeaky/portable-pypy/downloads/pypy-5.7.1-linux_x86_64-portable.tar.bz2 tar xf pypy-5.7.1-linux_x86_64-portable.tar.bz2 pypy-5.7.1-linux_x86_64-portable/bin/pypy -m ensurepip pypy-5.7.1-linux_x86_64-portable/bin/pypy -m pip install -U virtualenv) export PATH=$HOME/pypy-5.7.1-linux_x86_64-portable/bin/:$PATH export TOXPYTHON=$HOME/pypy-5.7.1-linux_x86_64-portable/bin/pypy fi if [[ $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then (cd $HOME wget https://bitbucket.org/squeaky/portable-pypy/downloads/pypy3.5-5.7.1-beta-linux_x86_64-portable.tar.bz2 tar xf pypy3.5-5.7.1-beta-linux_x86_64-portable.tar.bz2 pypy3.5-5.7.1-beta-linux_x86_64-portable/bin/pypy3 -m ensurepip pypy3.5-5.7.1-beta-linux_x86_64-portable/bin/pypy3 -m pip install -U virtualenv) export PATH=$HOME/pypy3.5-5.7.1-beta-linux_x86_64-portable/bin/:$PATH export TOXPYTHON=$HOME/pypy3.5-5.7.1-beta-linux_x86_64-portable/bin/pypy3 fi set +x script: - tox -v after_failure: - more .tox/log/* | cat - more .tox/*/log/* | cat notifications: email: on_success: never on_failure: always tblib-1.3.2/ci/templates/appveyor.yml0000664000175000017500000000321313071773003020032 0ustar ionelionel00000000000000version: '{branch}-{build}' build: off cache: - '%LOCALAPPDATA%\pip\Cache' environment: global: WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' matrix: - TOXENV: check PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' {% for env in tox_environments %}{% if env.startswith(('py2', 'py3')) %} - TOXENV: '{{ env }},codecov' TOXPYTHON: C:\Python{{ env[2:4] }}\python.exe PYTHON_HOME: C:\Python{{ env[2:4] }} PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' PYTHON_ARCH: '32' - TOXENV: '{{ env }},codecov' TOXPYTHON: C:\Python{{ env[2:4] }}-x64\python.exe {%- if env.startswith(('py2', 'py33', 'py34')) %} WINDOWS_SDK_VERSION: v7.{{ '1' if env.startswith('py3') else '0' }} {%- endif %} PYTHON_HOME: C:\Python{{ env[2:4] }}-x64 PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' PYTHON_ARCH: '64' {% endif %}{% endfor %} init: - ps: echo $env:TOXENV - ps: ls C:\Python* install: - python -u ci\appveyor-bootstrap.py - '%PYTHON_HOME%\Scripts\virtualenv --version' - '%PYTHON_HOME%\Scripts\easy_install --version' - '%PYTHON_HOME%\Scripts\pip --version' - '%PYTHON_HOME%\Scripts\tox --version' test_script: - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' on_failure: - ps: dir "env:" - ps: get-content .tox\*\log\* artifacts: - path: dist\* ### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): # on_finish: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) tblib-1.3.2/ci/appveyor-download.py0000775000175000017500000000735113071773003017502 0ustar ionelionel00000000000000#!/usr/bin/env python """ Use the AppVeyor API to download Windows artifacts. Taken from: https://bitbucket.org/ned/coveragepy/src/tip/ci/download_appveyor.py # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """ from __future__ import unicode_literals import argparse import os import zipfile import requests def make_auth_headers(): """Make the authentication headers needed to use the Appveyor API.""" path = os.path.expanduser("~/.appveyor.token") if not os.path.exists(path): raise RuntimeError( "Please create a file named `.appveyor.token` in your home directory. " "You can get the token from https://ci.appveyor.com/api-token" ) with open(path) as f: token = f.read().strip() headers = { 'Authorization': 'Bearer {}'.format(token), } return headers def download_latest_artifacts(account_project, build_id): """Download all the artifacts from the latest build.""" if build_id is None: url = "https://ci.appveyor.com/api/projects/{}".format(account_project) else: url = "https://ci.appveyor.com/api/projects/{}/build/{}".format(account_project, build_id) build = requests.get(url, headers=make_auth_headers()).json() jobs = build['build']['jobs'] print(u"Build {0[build][version]}, {1} jobs: {0[build][message]}".format(build, len(jobs))) for job in jobs: name = job['name'] print(u" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, job)) url = "https://ci.appveyor.com/api/buildjobs/{}/artifacts".format(job['jobId']) response = requests.get(url, headers=make_auth_headers()) artifacts = response.json() for artifact in artifacts: is_zip = artifact['type'] == "Zip" filename = artifact['fileName'] print(u" {0}, {1} bytes".format(filename, artifact['size'])) url = "https://ci.appveyor.com/api/buildjobs/{}/artifacts/{}".format(job['jobId'], filename) download_url(url, filename, make_auth_headers()) if is_zip: unpack_zipfile(filename) os.remove(filename) def ensure_dirs(filename): """Make sure the directories exist for `filename`.""" dirname = os.path.dirname(filename) if dirname and not os.path.exists(dirname): os.makedirs(dirname) def download_url(url, filename, headers): """Download a file from `url` to `filename`.""" ensure_dirs(filename) response = requests.get(url, headers=headers, stream=True) if response.status_code == 200: with open(filename, 'wb') as f: for chunk in response.iter_content(16 * 1024): f.write(chunk) else: print(u" Error downloading {}: {}".format(url, response)) def unpack_zipfile(filename): """Unpack a zipfile, using the names in the zip.""" with open(filename, 'rb') as fzip: z = zipfile.ZipFile(fzip) for name in z.namelist(): print(u" extracting {}".format(name)) ensure_dirs(name) z.extract(name) parser = argparse.ArgumentParser(description='Download artifacts from AppVeyor.') parser.add_argument('--id', metavar='PROJECT_ID', default='ionelmc/python-tblib', help='Project ID in AppVeyor.') parser.add_argument('build', nargs='?', metavar='BUILD_ID', help='Build ID in AppVeyor. Eg: master-123') if __name__ == "__main__": # import logging # logging.basicConfig(level="DEBUG") args = parser.parse_args() download_latest_artifacts(args.id, args.build) tblib-1.3.2/setup.cfg0000664000175000017500000000116713072512425014700 0ustar ionelionel00000000000000[bdist_wheel] universal = 1 [flake8] max-line-length = 140 exclude = tests/*,*/migrations/*,*/south_migrations/* [tool:pytest] norecursedirs = .git .tox .env dist build south_migrations migrations python_files = test_*.py *_test.py tests.py addopts = -rxEfsw --strict --ignore=tests/badmodule.py --ignore=tests/badsyntax.py --doctest-modules --doctest-glob=\*.rst --tb=short [isort] force_single_line = True line_length = 120 known_first_party = tblib default_section = THIRDPARTY forced_separate = test_tblib not_skip = __init__.py skip = migrations, south_migrations [egg_info] tag_build = tag_date = 0 tblib-1.3.2/AUTHORS.rst0000664000175000017500000000036213071773003014732 0ustar ionelionel00000000000000 Authors ======= * Ionel Cristian Mărieș - https://blog.ionelmc.ro * Arcadiy Ivanov - https://github.com/arcivanov * Beckjake - https://github.com/beckjake * DRayX - https://github.com/DRayX * Jason Madden - https://github.com/jamadden tblib-1.3.2/LICENSE0000664000175000017500000000243313071773003014061 0ustar ionelionel00000000000000Copyright (c) 2013-2017, Ionel Cristian Mărieș All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. tblib-1.3.2/MANIFEST.in0000664000175000017500000000055413071773003014614 0ustar ionelionel00000000000000graft docs graft examples graft src graft ci graft tests include .bumpversion.cfg include .coveragerc include .cookiecutterrc include .editorconfig include .isort.cfg include AUTHORS.rst include CHANGELOG.rst include CONTRIBUTING.rst include LICENSE include README.rst include tox.ini .travis.yml appveyor.yml global-exclude *.py[cod] __pycache__ *.so *.dylib tblib-1.3.2/.bumpversion.cfg0000664000175000017500000000030713072431554016165 0ustar ionelionel00000000000000[bumpversion] current_version = 1.3.2 commit = True tag = True [bumpversion:file:setup.py] [bumpversion:file:README.rst] [bumpversion:file:docs/conf.py] [bumpversion:file:src/tblib/__init__.py] tblib-1.3.2/tests/0000775000175000017500000000000013072512425014214 5ustar ionelionel00000000000000tblib-1.3.2/tests/badsyntax.py0000664000175000017500000000004013071773003016555 0ustar ionelionel00000000000000""" bad bad bad """ is very bad tblib-1.3.2/tests/badmodule.py0000664000175000017500000000004513071773003016521 0ustar ionelionel00000000000000a = 1 b = 2 raise Exception("boom!") tblib-1.3.2/tests/examples.py0000664000175000017500000000037313071773003016407 0ustar ionelionel00000000000000def func_a(_): func_b() def func_b(): func_c() def func_c(): func_d() def func_d(): raise Exception("Guessing time !") def bad_syntax(): import badsyntax badsyntax def bad_module(): import badmodule badmodule tblib-1.3.2/.cookiecutterrc0000664000175000017500000000174713071773003016111 0ustar ionelionel00000000000000# Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher) cookiecutter: appveyor: 'yes' c_extension_cython: 'no' c_extension_optional: 'no' c_extension_support: 'no' codacy: 'yes' codeclimate: 'yes' codecov: 'yes' command_line_interface: 'no' command_line_interface_bin_name: '-' coveralls: 'yes' distribution_name: tblib email: contact@ionelmc.ro full_name: Ionel Cristian Mărieș github_username: ionelmc landscape: 'yes' package_name: tblib project_name: tblib project_short_description: Traceback serialization library. release_date: '2016-03-08' repo_name: python-tblib requiresio: 'yes' scrutinizer: 'yes' sphinx_doctest: 'no' sphinx_theme: sphinx-py3doc-enhanced-theme test_matrix_configurator: 'no' test_matrix_separate_coverage: 'no' test_runner: pytest travis: 'yes' version: 1.3.0 website: http://blog.ionelmc.ro year: 2013-2017 tblib-1.3.2/README.rst0000664000175000017500000005133013072431554014546 0ustar ionelionel00000000000000======== Overview ======== .. start-badges .. list-table:: :stub-columns: 1 * - docs - |docs| * - tests - | |travis| |appveyor| |requires| | |coveralls| |codecov| | |landscape| |scrutinizer| |codacy| |codeclimate| * - package - | |version| |wheel| |supported-versions| |supported-implementations| | |commits-since| .. |docs| image:: https://readthedocs.org/projects/python-tblib/badge/?style=flat :target: https://readthedocs.org/projects/python-tblib :alt: Documentation Status .. |travis| image:: https://travis-ci.org/ionelmc/python-tblib.svg?branch=master :alt: Travis-CI Build Status :target: https://travis-ci.org/ionelmc/python-tblib .. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/ionelmc/python-tblib?branch=master&svg=true :alt: AppVeyor Build Status :target: https://ci.appveyor.com/project/ionelmc/python-tblib .. |requires| image:: https://requires.io/github/ionelmc/python-tblib/requirements.svg?branch=master :alt: Requirements Status :target: https://requires.io/github/ionelmc/python-tblib/requirements/?branch=master .. |coveralls| image:: https://coveralls.io/repos/ionelmc/python-tblib/badge.svg?branch=master&service=github :alt: Coverage Status :target: https://coveralls.io/r/ionelmc/python-tblib .. |codecov| image:: https://codecov.io/github/ionelmc/python-tblib/coverage.svg?branch=master :alt: Coverage Status :target: https://codecov.io/github/ionelmc/python-tblib .. |landscape| image:: https://landscape.io/github/ionelmc/python-tblib/master/landscape.svg?style=flat :target: https://landscape.io/github/ionelmc/python-tblib/master :alt: Code Quality Status .. |codacy| image:: https://img.shields.io/codacy/a8f7891727784601931d715097b703ee.svg?style=flat :target: https://www.codacy.com/app/ionelmc/python-tblib :alt: Codacy Code Quality Status .. |codeclimate| image:: https://codeclimate.com/github/ionelmc/python-tblib/badges/gpa.svg :target: https://codeclimate.com/github/ionelmc/python-tblib :alt: CodeClimate Quality Status .. |version| image:: https://img.shields.io/pypi/v/tblib.svg :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/tblib .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-tblib/v1.3.2.svg :alt: Commits since latest release :target: https://github.com/ionelmc/python-tblib/compare/v1.3.2...master .. |wheel| image:: https://img.shields.io/pypi/wheel/tblib.svg :alt: PyPI Wheel :target: https://pypi.python.org/pypi/tblib .. |supported-versions| image:: https://img.shields.io/pypi/pyversions/tblib.svg :alt: Supported versions :target: https://pypi.python.org/pypi/tblib .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/tblib.svg :alt: Supported implementations :target: https://pypi.python.org/pypi/tblib .. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-tblib/master.svg :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/ionelmc/python-tblib/ .. end-badges Traceback serialization library. * Free software: BSD license It allows you to: * `Pickle `_ tracebacks and raise exceptions with pickled tracebacks in different processes. This allows better error handling when running code over multiple processes (imagine multiprocessing, billiard, futures, celery etc). * Create traceback objects from strings (the ``from_string`` method). *No pickling is used*. * Serialize tracebacks to/from plain dicts (the ``from_dict`` and ``to_dict`` methods). *No pickling is used*. * Raise the tracebacks created from the aforementioned sources. **Again, note that using the pickle support is completely optional. You are solely responsible for security problems should you decide to use the pickle support.** Installation ============ :: pip install tblib Documentation ============= .. contents:: :local: Pickling tracebacks ~~~~~~~~~~~~~~~~~~~ **Note**: The traceback objects that come out are stripped of some attributes (like variables). But you'll be able to raise exceptions with those tracebacks or print them - that should cover 99% of the usecases. :: >>> from tblib import pickling_support >>> pickling_support.install() >>> import pickle, sys >>> def inner_0(): ... raise Exception('fail') ... >>> def inner_1(): ... inner_0() ... >>> def inner_2(): ... inner_1() ... >>> try: ... inner_2() ... except: ... s1 = pickle.dumps(sys.exc_info()) ... >>> len(s1) > 1 True >>> try: ... inner_2() ... except: ... s2 = pickle.dumps(sys.exc_info(), protocol=pickle.HIGHEST_PROTOCOL) ... >>> len(s2) > 1 True >>> try: ... import cPickle ... except ImportError: ... import pickle as cPickle >>> try: ... inner_2() ... except: ... s3 = cPickle.dumps(sys.exc_info(), protocol=pickle.HIGHEST_PROTOCOL) ... >>> len(s3) > 1 True Unpickling ~~~~~~~~~~ :: >>> pickle.loads(s1) (<...Exception'>, Exception('fail',), ) >>> pickle.loads(s2) (<...Exception'>, Exception('fail',), ) >>> pickle.loads(s3) (<...Exception'>, Exception('fail',), ) Raising ~~~~~~~ :: >>> from six import reraise >>> reraise(*pickle.loads(s1)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail >>> reraise(*pickle.loads(s2)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail >>> reraise(*pickle.loads(s3)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail What if we have a local stack, does it show correctly ? ------------------------------------------------------- Yes it does:: >>> exc_info = pickle.loads(s3) >>> def local_0(): ... reraise(*exc_info) ... >>> def local_1(): ... local_0() ... >>> def local_2(): ... local_1() ... >>> local_2() Traceback (most recent call last): File "...doctest.py", line ..., in __run compileflags, 1) in test.globs File "", line 1, in local_2() File "", line 2, in local_2 local_1() File "", line 2, in local_1 local_0() File "", line 2, in local_0 reraise(*exc_info) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail It also supports more contrived scenarios ----------------------------------------- Like tracebacks with syntax errors:: >>> from tblib import Traceback >>> from examples import bad_syntax >>> try: ... bad_syntax() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in reraise(et, ev, tb.as_traceback()) File "", line 2, in bad_syntax() File "...tests...examples.py", line 18, in bad_syntax import badsyntax File "...tests...badsyntax.py", line 5 is very bad ^ SyntaxError: invalid syntax Or other import failures:: >>> from examples import bad_module >>> try: ... bad_module() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in reraise(et, ev, tb.as_traceback()) File "", line 2, in bad_module() File "...tests...examples.py", line 23, in bad_module import badmodule File "...tests...badmodule.py", line 3, in raise Exception("boom!") Exception: boom! Or a traceback that's caused by exceeding the recursion limit (here we're forcing the type and value to have consistency across platforms):: >>> def f(): f() >>> try: ... f() ... except RuntimeError: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(RuntimeError, RuntimeError("maximum recursion depth exceeded"), tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in f def f(): f() File "", line 1, in f def f(): f() File "", line 1, in f def f(): f() ... RuntimeError: maximum recursion depth exceeded Reference ~~~~~~~~~ tblib.Traceback --------------- It is used by the ``pickling_support``. You can use it too if you want more flexibility:: >>> from tblib import Traceback >>> try: ... inner_2() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail tblib.Traceback.to_dict ``````````````````````` You can use the ``to_dict`` method and the ``from_dict`` classmethod to convert a Traceback into and from a dictionary serializable by the stdlib json.JSONDecoder:: >>> import json >>> from pprint import pprint >>> try: ... inner_2() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... tb_dict = tb.to_dict() ... pprint(tb_dict) {'tb_frame': {'f_code': {'co_filename': '', 'co_name': ''}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_2'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_1'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_0'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': None}}}} tblib.Traceback.from_dict ````````````````````````` Building on the previous example:: >>> tb_json = json.dumps(tb_dict) >>> tb = Traceback.from_dict(json.loads(tb_json)) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail tblib.Traceback.from_string ``````````````````````````` :: >>> tb = Traceback.from_string(""" ... File "skipped.py", line 123, in func_123 ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: fail If you use the ``strict=False`` option then parsing is a bit more lax:: >>> tb = Traceback.from_string(""" ... File "bogus.py", line 123, in bogus ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """, strict=False) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "bogus.py", line 123, in bogus File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: fail tblib.decorators.return_error ----------------------------- :: >>> from tblib.decorators import return_error >>> inner_2r = return_error(inner_2) >>> e = inner_2r() >>> e >>> e.reraise() Traceback (most recent call last): ... File "", line 1, in e.reraise() File "...tblib...decorators.py", line 19, in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line 25, in return_exceptions_wrapper return func(*args, **kwargs) File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail How's this useful ? Imagine you're using multiprocessing like this:: >>> import traceback >>> from multiprocessing import Pool >>> from examples import func_a >>> if sys.version_info[:2] >= (3, 4): ... import multiprocessing.pool ... # Undo the fix for http://bugs.python.org/issue13831 so that we can see the effects of our change. ... # because Python 3.4 will show the remote traceback (but as a string sadly) ... multiprocessing.pool.ExceptionWithTraceback = lambda e, t: e >>> pool = Pool() >>> try: ... for i in pool.map(func_a, range(5)): ... print(i) ... except: ... print(traceback.format_exc()) ... Traceback (most recent call last): File "", line 2, in for i in pool.map(func_a, range(5)): File "...multiprocessing...pool.py", line ..., in map ... File "...multiprocessing...pool.py", line ..., in get ... Exception: Guessing time ! >>> pool.terminate() Not very useful is it? Let's sort this out:: >>> from tblib.decorators import apply_with_return_error, Error >>> from itertools import repeat >>> pool = Pool() >>> try: ... for i in pool.map(apply_with_return_error, zip(repeat(func_a), range(5))): ... if isinstance(i, Error): ... i.reraise() ... else: ... print(i) ... except: ... print(traceback.format_exc()) ... Traceback (most recent call last): File "", line 4, in i.reraise() File "...tblib...decorators.py", line ..., in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line ..., in return_exceptions_wrapper return func(*args, **kwargs) File "...tblib...decorators.py", line ..., in apply_with_return_error return args[0](*args[1:]) File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: Guessing time ! >>> pool.terminate() Much better ! What if we have a local call stack ? ```````````````````````````````````` :: >>> def local_0(): ... pool = Pool() ... for i in pool.map(apply_with_return_error, zip(repeat(func_a), range(5))): ... if isinstance(i, Error): ... i.reraise() ... else: ... print(i) ... >>> def local_1(): ... local_0() ... >>> def local_2(): ... local_1() ... >>> try: ... local_2() ... except: ... print(traceback.format_exc()) Traceback (most recent call last): File "", line 2, in local_2() File "", line 2, in local_2 local_1() File "", line 2, in local_1 local_0() File "", line 5, in local_0 i.reraise() File "...tblib...decorators.py", line 20, in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line 27, in return_exceptions_wrapper return func(*args, **kwargs) File "...tblib...decorators.py", line 47, in apply_with_return_error return args[0](*args[1:]) File "...tests...examples.py", line 2, in func_a func_b() File "...tests...examples.py", line 6, in func_b func_c() File "...tests...examples.py", line 10, in func_c func_d() File "...tests...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: Guessing time ! Other weird stuff ````````````````` Clearing traceback works (Python 3.4 and up):: >>> tb = Traceback.from_string(""" ... File "skipped.py", line 123, in func_123 ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """) >>> import traceback, sys >>> if sys.version_info > (3, 4): ... traceback.clear_frames(tb) Credits ======= * `mitsuhiko/jinja2 `_ for figuring a way to create traceback objects. tblib-1.3.2/tox.ini0000664000175000017500000000403413072261404014364 0ustar ionelionel00000000000000; a generative tox configuration, see: https://testrun.org/tox/latest/config.html#generative-envlist [tox] envlist = clean, check, {py27,py33,py34,py35,py36,pypy}, report, docs [testenv] basepython = pypy: {env:TOXPYTHON:pypy} pypy3: {env:TOXPYTHON:pypy3} {py27,docs,spell}: {env:TOXPYTHON:python2.7} py33: {env:TOXPYTHON:python3.3} py34: {env:TOXPYTHON:python3.4} py35: {env:TOXPYTHON:python3.5} py36: {env:TOXPYTHON:python3.6} {bootstrap,clean,check,report,coveralls,codecov}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes passenv = * usedevelop = false deps = pytest pytest-travis-fold pytest-cov six commands = {posargs:py.test --cov=tblib --cov-report=term-missing -vv tests README.rst} [testenv:bootstrap] deps = jinja2 matrix skip_install = true commands = python ci/bootstrap.py passenv = * [testenv:spell] setenv = SPELLCHECK=1 commands = sphinx-build -b spelling docs dist/docs skip_install = true deps = -r{toxinidir}/docs/requirements.txt sphinxcontrib-spelling pyenchant [testenv:docs] deps = -r{toxinidir}/docs/requirements.txt commands = sphinx-build {posargs:-E} -b html docs dist/docs sphinx-build -b linkcheck docs dist/docs [testenv:check] deps = docutils check-manifest flake8 readme-renderer pygments isort skip_install = true commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} flake8 src tests setup.py isort --verbose --check-only --diff --recursive src tests setup.py [testenv:coveralls] deps = coveralls skip_install = true commands = coveralls [] [testenv:codecov] deps = codecov skip_install = true commands = coverage xml --ignore-errors codecov [] [testenv:report] deps = coverage skip_install = true commands = coverage combine --append coverage report coverage html [testenv:clean] commands = coverage erase skip_install = true deps = coverage tblib-1.3.2/setup.py0000664000175000017500000000441613072431554014574 0ustar ionelionel00000000000000#!/usr/bin/env python # -*- encoding: utf-8 -*- from __future__ import absolute_import from __future__ import print_function import io import re from glob import glob from os.path import basename from os.path import dirname from os.path import join from os.path import splitext from setuptools import find_packages from setuptools import setup def read(*names, **kwargs): return io.open( join(dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8') ).read() setup( name='tblib', version='1.3.2', license='BSD', description='Traceback serialization library.', long_description='%s\n%s' % ( re.compile('^.. start-badges.*^.. end-badges', re.M | re.S).sub('', read('README.rst')), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst')) ), author='Ionel Cristian Mărieș', author_email='contact@ionelmc.ro', url='https://github.com/ionelmc/python-tblib', packages=find_packages('src'), package_dir={'': 'src'}, py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], include_package_data=True, zip_safe=False, classifiers=[ # complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: Unix', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Utilities', ], keywords=[ # eg: 'keyword1', 'keyword2', 'keyword3', ], install_requires=[ # eg: 'aspectlib==1.1.1', 'six>=1.7', ], extras_require={ # eg: # 'rst': ['docutils>=0.11'], # ':python_version=="2.6"': ['argparse'], }, ) tblib-1.3.2/CHANGELOG.rst0000664000175000017500000000226513072431330015073 0ustar ionelionel00000000000000 Changelog ========= 1.3.2 (2017-04-09) ~~~~~~~~~~~~~~~~~~ * Add support for PyPy3.5-5.7.1-beta. Previously ``AttributeError: 'Frame' object has no attribute 'clear'`` could be raised. See PyPy issue `#2532 `_. 1.3.1 (2017-03-27) ~~~~~~~~~~~~~~~~~~ * Fixed handling for tracebacks due to exceeding the recursion limit. Fixes `#15 `_. 1.3.0 (2016-03-08) ~~~~~~~~~~~~~~~~~~ * Added ``Traceback.from_string``. 1.2.0 (2015-12-18) ~~~~~~~~~~~~~~~~~~ * Fixed handling for tracebacks from generators and other internal improvements and optimizations. Contributed by DRayX in `#10 `_ and `#11 `_. 1.1.0 (2015-07-27) ~~~~~~~~~~~~~~~~~~ * Added support for Python 2.6. Contributed by Arcadiy Ivanov in `#8 `_. 1.0.0 (2015-03-30) ~~~~~~~~~~~~~~~~~~ * Added ``to_dict`` method and ``from_dict`` classmethod on Tracebacks. Contributed by beckjake in `#5 `_. tblib-1.3.2/appveyor.yml0000664000175000017500000000443313072261400015440 0ustar ionelionel00000000000000version: '{branch}-{build}' build: off cache: - '%LOCALAPPDATA%\pip\Cache' environment: global: WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' matrix: - TOXENV: check PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - TOXENV: 'py27,codecov' TOXPYTHON: C:\Python27\python.exe PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - TOXENV: 'py27,codecov' TOXPYTHON: C:\Python27-x64\python.exe WINDOWS_SDK_VERSION: v7.0 PYTHON_HOME: C:\Python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - TOXENV: 'py34,codecov' TOXPYTHON: C:\Python34\python.exe PYTHON_HOME: C:\Python34 PYTHON_VERSION: '3.4' PYTHON_ARCH: '32' - TOXENV: 'py34,codecov' TOXPYTHON: C:\Python34-x64\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\Python34-x64 PYTHON_VERSION: '3.4' PYTHON_ARCH: '64' - TOXENV: 'py35,codecov' TOXPYTHON: C:\Python35\python.exe PYTHON_HOME: C:\Python35 PYTHON_VERSION: '3.5' PYTHON_ARCH: '32' - TOXENV: 'py35,codecov' TOXPYTHON: C:\Python35-x64\python.exe PYTHON_HOME: C:\Python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' - TOXENV: 'py36,codecov' TOXPYTHON: C:\Python36\python.exe PYTHON_HOME: C:\Python36 PYTHON_VERSION: '3.6' PYTHON_ARCH: '32' - TOXENV: 'py36,codecov' TOXPYTHON: C:\Python36-x64\python.exe PYTHON_HOME: C:\Python36-x64 PYTHON_VERSION: '3.6' PYTHON_ARCH: '64' init: - ps: echo $env:TOXENV - ps: ls C:\Python* install: - python -u ci\appveyor-bootstrap.py - '%PYTHON_HOME%\Scripts\virtualenv --version' - '%PYTHON_HOME%\Scripts\easy_install --version' - '%PYTHON_HOME%\Scripts\pip --version' - '%PYTHON_HOME%\Scripts\tox --version' test_script: - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' on_failure: - ps: dir "env:" - ps: get-content .tox\*\log\* artifacts: - path: dist\* ### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): # on_finish: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) tblib-1.3.2/PKG-INFO0000664000175000017500000006114713072512425014160 0ustar ionelionel00000000000000Metadata-Version: 1.1 Name: tblib Version: 1.3.2 Summary: Traceback serialization library. Home-page: https://github.com/ionelmc/python-tblib Author: Ionel Cristian Mărieș Author-email: contact@ionelmc.ro License: BSD Description: ======== Overview ======== Traceback serialization library. * Free software: BSD license It allows you to: * `Pickle `_ tracebacks and raise exceptions with pickled tracebacks in different processes. This allows better error handling when running code over multiple processes (imagine multiprocessing, billiard, futures, celery etc). * Create traceback objects from strings (the ``from_string`` method). *No pickling is used*. * Serialize tracebacks to/from plain dicts (the ``from_dict`` and ``to_dict`` methods). *No pickling is used*. * Raise the tracebacks created from the aforementioned sources. **Again, note that using the pickle support is completely optional. You are solely responsible for security problems should you decide to use the pickle support.** Installation ============ :: pip install tblib Documentation ============= .. contents:: :local: Pickling tracebacks ~~~~~~~~~~~~~~~~~~~ **Note**: The traceback objects that come out are stripped of some attributes (like variables). But you'll be able to raise exceptions with those tracebacks or print them - that should cover 99% of the usecases. :: >>> from tblib import pickling_support >>> pickling_support.install() >>> import pickle, sys >>> def inner_0(): ... raise Exception('fail') ... >>> def inner_1(): ... inner_0() ... >>> def inner_2(): ... inner_1() ... >>> try: ... inner_2() ... except: ... s1 = pickle.dumps(sys.exc_info()) ... >>> len(s1) > 1 True >>> try: ... inner_2() ... except: ... s2 = pickle.dumps(sys.exc_info(), protocol=pickle.HIGHEST_PROTOCOL) ... >>> len(s2) > 1 True >>> try: ... import cPickle ... except ImportError: ... import pickle as cPickle >>> try: ... inner_2() ... except: ... s3 = cPickle.dumps(sys.exc_info(), protocol=pickle.HIGHEST_PROTOCOL) ... >>> len(s3) > 1 True Unpickling ~~~~~~~~~~ :: >>> pickle.loads(s1) (<...Exception'>, Exception('fail',), ) >>> pickle.loads(s2) (<...Exception'>, Exception('fail',), ) >>> pickle.loads(s3) (<...Exception'>, Exception('fail',), ) Raising ~~~~~~~ :: >>> from six import reraise >>> reraise(*pickle.loads(s1)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail >>> reraise(*pickle.loads(s2)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail >>> reraise(*pickle.loads(s3)) Traceback (most recent call last): ... File "", line 1, in reraise(*pickle.loads(s2)) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail What if we have a local stack, does it show correctly ? ------------------------------------------------------- Yes it does:: >>> exc_info = pickle.loads(s3) >>> def local_0(): ... reraise(*exc_info) ... >>> def local_1(): ... local_0() ... >>> def local_2(): ... local_1() ... >>> local_2() Traceback (most recent call last): File "...doctest.py", line ..., in __run compileflags, 1) in test.globs File "", line 1, in local_2() File "", line 2, in local_2 local_1() File "", line 2, in local_1 local_0() File "", line 2, in local_0 reraise(*exc_info) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail It also supports more contrived scenarios ----------------------------------------- Like tracebacks with syntax errors:: >>> from tblib import Traceback >>> from examples import bad_syntax >>> try: ... bad_syntax() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in reraise(et, ev, tb.as_traceback()) File "", line 2, in bad_syntax() File "...tests...examples.py", line 18, in bad_syntax import badsyntax File "...tests...badsyntax.py", line 5 is very bad ^ SyntaxError: invalid syntax Or other import failures:: >>> from examples import bad_module >>> try: ... bad_module() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in reraise(et, ev, tb.as_traceback()) File "", line 2, in bad_module() File "...tests...examples.py", line 23, in bad_module import badmodule File "...tests...badmodule.py", line 3, in raise Exception("boom!") Exception: boom! Or a traceback that's caused by exceeding the recursion limit (here we're forcing the type and value to have consistency across platforms):: >>> def f(): f() >>> try: ... f() ... except RuntimeError: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(RuntimeError, RuntimeError("maximum recursion depth exceeded"), tb.as_traceback()) Traceback (most recent call last): ... File "", line 1, in f def f(): f() File "", line 1, in f def f(): f() File "", line 1, in f def f(): f() ... RuntimeError: maximum recursion depth exceeded Reference ~~~~~~~~~ tblib.Traceback --------------- It is used by the ``pickling_support``. You can use it too if you want more flexibility:: >>> from tblib import Traceback >>> try: ... inner_2() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail tblib.Traceback.to_dict ``````````````````````` You can use the ``to_dict`` method and the ``from_dict`` classmethod to convert a Traceback into and from a dictionary serializable by the stdlib json.JSONDecoder:: >>> import json >>> from pprint import pprint >>> try: ... inner_2() ... except: ... et, ev, tb = sys.exc_info() ... tb = Traceback(tb) ... tb_dict = tb.to_dict() ... pprint(tb_dict) {'tb_frame': {'f_code': {'co_filename': '', 'co_name': ''}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_2'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_1'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': {'tb_frame': {'f_code': {'co_filename': ... 'co_name': 'inner_0'}, 'f_globals': {'__name__': '__main__'}}, 'tb_lineno': 2, 'tb_next': None}}}} tblib.Traceback.from_dict ````````````````````````` Building on the previous example:: >>> tb_json = json.dumps(tb_dict) >>> tb = Traceback.from_dict(json.loads(tb_json)) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "", line 2, in inner_2() File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail tblib.Traceback.from_string ``````````````````````````` :: >>> tb = Traceback.from_string(""" ... File "skipped.py", line 123, in func_123 ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: fail If you use the ``strict=False`` option then parsing is a bit more lax:: >>> tb = Traceback.from_string(""" ... File "bogus.py", line 123, in bogus ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """, strict=False) >>> reraise(et, ev, tb.as_traceback()) Traceback (most recent call last): ... File "", line 6, in reraise(et, ev, tb.as_traceback()) File "bogus.py", line 123, in bogus File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: fail tblib.decorators.return_error ----------------------------- :: >>> from tblib.decorators import return_error >>> inner_2r = return_error(inner_2) >>> e = inner_2r() >>> e >>> e.reraise() Traceback (most recent call last): ... File "", line 1, in e.reraise() File "...tblib...decorators.py", line 19, in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line 25, in return_exceptions_wrapper return func(*args, **kwargs) File "", line 2, in inner_2 inner_1() File "", line 2, in inner_1 inner_0() File "", line 2, in inner_0 raise Exception('fail') Exception: fail How's this useful ? Imagine you're using multiprocessing like this:: >>> import traceback >>> from multiprocessing import Pool >>> from examples import func_a >>> if sys.version_info[:2] >= (3, 4): ... import multiprocessing.pool ... # Undo the fix for http://bugs.python.org/issue13831 so that we can see the effects of our change. ... # because Python 3.4 will show the remote traceback (but as a string sadly) ... multiprocessing.pool.ExceptionWithTraceback = lambda e, t: e >>> pool = Pool() >>> try: ... for i in pool.map(func_a, range(5)): ... print(i) ... except: ... print(traceback.format_exc()) ... Traceback (most recent call last): File "", line 2, in for i in pool.map(func_a, range(5)): File "...multiprocessing...pool.py", line ..., in map ... File "...multiprocessing...pool.py", line ..., in get ... Exception: Guessing time ! >>> pool.terminate() Not very useful is it? Let's sort this out:: >>> from tblib.decorators import apply_with_return_error, Error >>> from itertools import repeat >>> pool = Pool() >>> try: ... for i in pool.map(apply_with_return_error, zip(repeat(func_a), range(5))): ... if isinstance(i, Error): ... i.reraise() ... else: ... print(i) ... except: ... print(traceback.format_exc()) ... Traceback (most recent call last): File "", line 4, in i.reraise() File "...tblib...decorators.py", line ..., in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line ..., in return_exceptions_wrapper return func(*args, **kwargs) File "...tblib...decorators.py", line ..., in apply_with_return_error return args[0](*args[1:]) File "...examples.py", line 2, in func_a func_b() File "...examples.py", line 6, in func_b func_c() File "...examples.py", line 10, in func_c func_d() File "...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: Guessing time ! >>> pool.terminate() Much better ! What if we have a local call stack ? ```````````````````````````````````` :: >>> def local_0(): ... pool = Pool() ... for i in pool.map(apply_with_return_error, zip(repeat(func_a), range(5))): ... if isinstance(i, Error): ... i.reraise() ... else: ... print(i) ... >>> def local_1(): ... local_0() ... >>> def local_2(): ... local_1() ... >>> try: ... local_2() ... except: ... print(traceback.format_exc()) Traceback (most recent call last): File "", line 2, in local_2() File "", line 2, in local_2 local_1() File "", line 2, in local_1 local_0() File "", line 5, in local_0 i.reraise() File "...tblib...decorators.py", line 20, in reraise reraise(self.exc_type, self.exc_value, self.traceback) File "...tblib...decorators.py", line 27, in return_exceptions_wrapper return func(*args, **kwargs) File "...tblib...decorators.py", line 47, in apply_with_return_error return args[0](*args[1:]) File "...tests...examples.py", line 2, in func_a func_b() File "...tests...examples.py", line 6, in func_b func_c() File "...tests...examples.py", line 10, in func_c func_d() File "...tests...examples.py", line 14, in func_d raise Exception("Guessing time !") Exception: Guessing time ! Other weird stuff ````````````````` Clearing traceback works (Python 3.4 and up):: >>> tb = Traceback.from_string(""" ... File "skipped.py", line 123, in func_123 ... Traceback (most recent call last): ... File "tests/examples.py", line 2, in func_a ... func_b() ... File "tests/examples.py", line 6, in func_b ... func_c() ... File "tests/examples.py", line 10, in func_c ... func_d() ... File "tests/examples.py", line 14, in func_d ... Doesn't: matter ... """) >>> import traceback, sys >>> if sys.version_info > (3, 4): ... traceback.clear_frames(tb) Credits ======= * `mitsuhiko/jinja2 `_ for figuring a way to create traceback objects. Changelog ========= 1.3.2 (2017-04-09) ~~~~~~~~~~~~~~~~~~ * Add support for PyPy3.5-5.7.1-beta. Previously ``AttributeError: 'Frame' object has no attribute 'clear'`` could be raised. See PyPy issue `#2532 `_. 1.3.1 (2017-03-27) ~~~~~~~~~~~~~~~~~~ * Fixed handling for tracebacks due to exceeding the recursion limit. Fixes `#15 `_. 1.3.0 (2016-03-08) ~~~~~~~~~~~~~~~~~~ * Added ``Traceback.from_string``. 1.2.0 (2015-12-18) ~~~~~~~~~~~~~~~~~~ * Fixed handling for tracebacks from generators and other internal improvements and optimizations. Contributed by DRayX in `#10 `_ and `#11 `_. 1.1.0 (2015-07-27) ~~~~~~~~~~~~~~~~~~ * Added support for Python 2.6. Contributed by Arcadiy Ivanov in `#8 `_. 1.0.0 (2015-03-30) ~~~~~~~~~~~~~~~~~~ * Added ``to_dict`` method and ``from_dict`` classmethod on Tracebacks. Contributed by beckjake in `#5 `_. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: Unix Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Utilities tblib-1.3.2/.coveragerc0000664000175000017500000000025313071773003015173 0ustar ionelionel00000000000000[paths] source = src/tblib */site-packages/tblib [run] branch = True source = tblib parallel = true [report] show_missing = true precision = 2 omit = *migrations*