tblib-1.2.0/0000775000175000017500000000000012634623345013056 5ustar ionelionel00000000000000tblib-1.2.0/README.rst0000664000175000017500000003635512634620015014550 0ustar ionelionel00000000000000===== tblib ===== .. list-table:: :stub-columns: 1 * - docs - |docs| * - tests - | |travis| |appveyor| |requires| | |coveralls| |codecov| | |landscape| |scrutinizer| |codacy| |codeclimate| * - package - |version| |downloads| |wheel| |supported-versions| |supported-implementations| .. |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/REPLACE_WITH_PROJECT_ID.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?style=flat :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/tblib .. |downloads| image:: https://img.shields.io/pypi/dm/tblib.svg?style=flat :alt: PyPI Package monthly downloads :target: https://pypi.python.org/pypi/tblib .. |wheel| image:: https://img.shields.io/pypi/wheel/tblib.svg?style=flat :alt: PyPI Wheel :target: https://pypi.python.org/pypi/tblib .. |supported-versions| image:: https://img.shields.io/pypi/pyversions/tblib.svg?style=flat :alt: Supported versions :target: https://pypi.python.org/pypi/tblib .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/tblib.svg?style=flat :alt: Supported implementations :target: https://pypi.python.org/pypi/tblib .. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-tblib/master.svg?style=flat :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/ionelmc/python-tblib/ Traceback fiddling library. For now 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). * Free software: BSD license Installation ============ :: pip install tblib Documentation ============= 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 The tblib.Traceback object -------------------------- 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 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 tblib import Traceback >>> 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}}}} >>> 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 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 5, in func_b func_c() File "...examples.py", line 8, in func_c func_d() File "...examples.py", line 11, 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 5, in func_b func_c() File "...tests...examples.py", line 8, in func_c func_d() File "...tests...examples.py", line 11, in func_d raise Exception("Guessing time !") Exception: Guessing time ! Credits ======= * `mitsuhiko/jinja2 `_ for figuring a way to create traceback objects. tblib-1.2.0/tox.ini0000664000175000017500000000436712625714526014405 0ustar ionelionel00000000000000; a generative tox configuration, see: https://testrun.org/tox/latest/config.html#generative-envlist [tox] envlist = clean, check, {2.6,2.7,3.3,3.4,3.5,pypy}-{cover,nocov}, report, docs [testenv] basepython = pypy: {env:TOXPYTHON:pypy} 2.6: {env:TOXPYTHON:python2.6} {2.7,docs,spell}: {env:TOXPYTHON:python2.7} 3.3: {env:TOXPYTHON:python3.3} 3.4: {env:TOXPYTHON:python3.4} 3.5: {env:TOXPYTHON:python3.5} {clean,check,report,extension-coveralls,coveralls,codecov}: python3.4 setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes passenv = * deps = six cover: coverage commands = cover: {posargs:coverage run {toxinidir}/tests/test_tblib.py} nocov: {posargs:{envpython} {toxinidir}/tests/test_tblib.py} usedevelop = true [testenv:spell] setenv = SPELLCHECK=1 commands = sphinx-build -b spelling docs dist/docs skip_install = true usedevelop = 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] basepython = python3.4 deps = docutils check-manifest flake8 readme pygments skip_install = true usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} flake8 src tests [testenv:coveralls] deps = coveralls skip_install = true usedevelop = false commands = coverage combine coverage report coveralls [] [testenv:codecov] deps = codecov skip_install = true usedevelop = false commands = coverage combine coverage report coverage xml --ignore-errors codecov [] [testenv:report] basepython = python3.4 deps = coverage skip_install = true usedevelop = false commands = coverage combine coverage report [testenv:clean] commands = coverage erase skip_install = true usedevelop = false deps = coverage [testenv:2.6-nocov] usedevelop = false [testenv:2.7-nocov] usedevelop = false [testenv:3.3-nocov] usedevelop = false [testenv:3.4-nocov] usedevelop = false [testenv:3.5-nocov] usedevelop = false [testenv:pypy-nocov] usedevelop = false tblib-1.2.0/setup.cfg0000664000175000017500000000122512634623345014677 0ustar ionelionel00000000000000[bdist_wheel] universal = 1 [aliases] release = register clean --all sdist bdist_wheel [flake8] max-line-length = 140 exclude = tests/*,*/migrations/*,*/south_migrations/* [pytest] norecursedirs = .git .tox .env dist build south_migrations migrations python_files = test_*.py *_test.py tests.py addopts = -rxEfsw --strict --ignore=docs/conf.py --ignore=setup.py --ignore=ci --ignore=.eggs --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 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 tblib-1.2.0/AUTHORS.rst0000664000175000017500000000030412634621147014730 0ustar ionelionel00000000000000 Authors ======= * Ionel Cristian Mărieș - http://blog.ionelmc.ro * Arcadiy Ivanov - https://github.com/arcivanov * Beckjake - https://github.com/beckjake * DRayX - https://github.com/DRayX tblib-1.2.0/.bumpversion.cfg0000664000175000017500000000020712634621336016163 0ustar ionelionel00000000000000[bumpversion] current_version = 1.2.0 commit = True tag = True [bumpversion:file:setup.py] [bumpversion:file:src/tblib/__init__.py] tblib-1.2.0/.travis.yml0000664000175000017500000000155412625714526015176 0ustar ionelionel00000000000000language: python python: '3.5' sudo: false env: global: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - TOXENV=2.6-cover,coveralls,codecov - TOXENV=2.6-nocov - TOXENV=2.7-cover,coveralls,codecov - TOXENV=2.7-nocov - TOXENV=3.3-cover,coveralls,codecov - TOXENV=3.3-nocov - TOXENV=3.4-cover,coveralls,codecov - TOXENV=3.4-nocov - TOXENV=3.5-cover,coveralls,codecov - TOXENV=3.5-nocov - TOXENV=pypy-cover,coveralls,codecov - TOXENV=pypy-nocov before_install: - python --version - uname -a - lsb_release -a install: - pip install tox - virtualenv --version - easy_install --version - pip --version - tox --version script: - tox -v after_failure: - more .tox/log/* | cat - more .tox/*/log/* | cat notifications: email: on_success: never on_failure: always tblib-1.2.0/ci/0000775000175000017500000000000012634623345013451 5ustar ionelionel00000000000000tblib-1.2.0/ci/appveyor-bootstrap.py0000664000175000017500000001053412625714526017710 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.6", "64"): BASE_URL + "2.6.6/python-2.6.6.amd64.msi", ("2.6", "32"): BASE_URL + "2.6.6/python-2.6.6.msi", ("2.7", "64"): BASE_URL + "2.7.10/python-2.7.10.amd64.msi", ("2.7", "32"): BASE_URL + "2.7.10/python-2.7.10.msi", # NOTE: no .msi installer for 3.3.6 ("3.3", "64"): BASE_URL + "3.3.3/python-3.3.3.amd64.msi", ("3.3", "32"): BASE_URL + "3.3.3/python-3.3.3.msi", ("3.4", "64"): BASE_URL + "3.4.3/python-3.4.3.amd64.msi", ("3.4", "32"): BASE_URL + "3.4.3/python-3.4.3.msi", ("3.5", "64"): BASE_URL + "3.5.0/python-3.5.0-amd64.exe", ("3.5", "32"): BASE_URL + "3.5.0/python-3.5.0.exe", } INSTALL_CMD = { # Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. "2.6": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], "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}"]], } 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.2.0/ci/appveyor-with-compiler.cmd0000664000175000017500000000307612625714526020574 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.2.0/ci/appveyor-download.py0000664000175000017500000000716112625714526017504 0ustar ionelionel00000000000000""" 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 """ import argparse import os import zipfile import requests def make_auth_headers(): """Make the authentication headers needed to use the Appveyor API.""" if not os.path.exists(".appveyor.token"): raise RuntimeError( "Please create a file named `.appveyor.token` in the current directory. " "You can get the token from https://ci.appveyor.com/api-token" ) with open(".appveyor.token") as f: token = f.read().strip() headers = { 'Authorization': 'Bearer {}'.format(token), } return headers def make_url(url, **kwargs): """Build an Appveyor API url.""" return "https://ci.appveyor.com/api" + url.format(**kwargs) def get_project_build(account_project): """Get the details of the latest Appveyor build.""" url = make_url("/projects/{account_project}", account_project=account_project) response = requests.get(url, headers=make_auth_headers()) return response.json() def download_latest_artifacts(account_project): """Download all the artifacts from the latest build.""" build = get_project_build(account_project) jobs = build['build']['jobs'] print("Build {0[build][version]}, {1} jobs: {0[build][message]}".format(build, len(jobs))) for job in jobs: name = job['name'].partition(':')[2].split(',')[0].strip() print(" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, job)) url = make_url("/buildjobs/{jobid}/artifacts", jobid=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(" {0}, {1} bytes".format(filename, artifact['size'])) url = make_url( "/buildjobs/{jobid}/artifacts/{filename}", jobid=job['jobId'], filename=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.split(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) 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(" extracting {}".format(name)) ensure_dirs(name) z.extract(name) parser = argparse.ArgumentParser(description='Download artifacts from AppVeyor.') parser.add_argument('name', metavar='ID', help='Project ID in AppVeyor. Example: ionelmc/python-nameless') if __name__ == "__main__": # import logging # logging.basicConfig(level="DEBUG") args = parser.parse_args() download_latest_artifacts(args.name) tblib-1.2.0/src/0000775000175000017500000000000012634623345013645 5ustar ionelionel00000000000000tblib-1.2.0/src/tblib.egg-info/0000775000175000017500000000000012634623345016433 5ustar ionelionel00000000000000tblib-1.2.0/src/tblib.egg-info/SOURCES.txt0000664000175000017500000000104412634623345020316 0ustar ionelionel00000000000000.bumpversion.cfg .cookiecutterrc .coveragerc .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 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/examples.py tests/test_tblib.pytblib-1.2.0/src/tblib.egg-info/top_level.txt0000664000175000017500000000000612634623345021161 0ustar ionelionel00000000000000tblib tblib-1.2.0/src/tblib.egg-info/dependency_links.txt0000664000175000017500000000000112634623345022501 0ustar ionelionel00000000000000 tblib-1.2.0/src/tblib.egg-info/not-zip-safe0000664000175000017500000000000112634623345020661 0ustar ionelionel00000000000000 tblib-1.2.0/src/tblib.egg-info/PKG-INFO0000664000175000017500000005165512634623345017544 0ustar ionelionel00000000000000Metadata-Version: 1.1 Name: tblib Version: 1.2.0 Summary: Traceback fiddling library. For now 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). Home-page: https://github.com/ionelmc/python-tblib Author: Ionel Cristian Mărieș Author-email: contact@ionelmc.ro License: BSD Description: ===== tblib ===== .. list-table:: :stub-columns: 1 * - docs - |docs| * - tests - | |travis| |appveyor| |requires| | |coveralls| |codecov| | |landscape| |scrutinizer| |codacy| |codeclimate| * - package - |version| |downloads| |wheel| |supported-versions| |supported-implementations| .. |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/REPLACE_WITH_PROJECT_ID.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?style=flat :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/tblib .. |downloads| image:: https://img.shields.io/pypi/dm/tblib.svg?style=flat :alt: PyPI Package monthly downloads :target: https://pypi.python.org/pypi/tblib .. |wheel| image:: https://img.shields.io/pypi/wheel/tblib.svg?style=flat :alt: PyPI Wheel :target: https://pypi.python.org/pypi/tblib .. |supported-versions| image:: https://img.shields.io/pypi/pyversions/tblib.svg?style=flat :alt: Supported versions :target: https://pypi.python.org/pypi/tblib .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/tblib.svg?style=flat :alt: Supported implementations :target: https://pypi.python.org/pypi/tblib .. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-tblib/master.svg?style=flat :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/ionelmc/python-tblib/ Traceback fiddling library. For now 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). * Free software: BSD license Installation ============ :: pip install tblib Documentation ============= 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 The tblib.Traceback object -------------------------- 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 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 tblib import Traceback >>> 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}}}} >>> 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 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 5, in func_b func_c() File "...examples.py", line 8, in func_c func_d() File "...examples.py", line 11, 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 5, in func_b func_c() File "...tests...examples.py", line 8, in func_c func_d() File "...tests...examples.py", line 11, in func_d raise Exception("Guessing time !") Exception: Guessing time ! Credits ======= * `mitsuhiko/jinja2 `_ for figuring a way to create traceback objects. Changelog ========= 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.6 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 :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Utilities tblib-1.2.0/src/tblib/0000775000175000017500000000000012634623345014741 5ustar ionelionel00000000000000tblib-1.2.0/src/tblib/cpython.py0000664000175000017500000000451512625714526017006 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.2.0/src/tblib/decorators.py0000664000175000017500000000206412625714526017464 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.2.0/src/tblib/__init__.py0000664000175000017500000001012412634621336017046 0ustar ionelionel00000000000000try: 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.") import sys from types import CodeType from types import TracebackType __version__ = '1.2.0' PY3 = sys.version_info[0] == 3 class _AttrDict(dict): __slots__ = () def __getattr__(self, attr): return self[attr] class __traceback_maker(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) class Traceback(object): def __init__(self, tb): self.tb_frame = Frame(tb.tb_frame) self.tb_lineno = tb.tb_lineno if tb.tb_next is None: self.tb_next = None else: self.tb_next = Traceback(tb.tb_next) def as_traceback(self): if tproxy: return tproxy(TracebackType, self.__tproxy_handler) elif tb_set_next: f_code = self.tb_frame.f_code code = compile('\n' * (self.tb_lineno - 1) + 'raise __traceback_maker', self.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, (), () ) try: exec(code, self.tb_frame.f_globals, {}) except: tb = sys.exc_info()[2].tb_next tb_set_next(tb, self.tb_next and self.tb_next.as_traceback()) return tb else: raise RuntimeError("Cannot re-create traceback !") 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) tblib-1.2.0/src/tblib/pickling_support.py0000664000175000017500000000104412625714526020710 0ustar ionelionel00000000000000try: import copy_reg except ImportError: import copyreg as copy_reg from types import TracebackType from . import Frame, 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.2.0/appveyor.yml0000664000175000017500000000570112625714526015453 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: '2.7-cover,codecov' TOXPYTHON: C:\Python27\python.exe PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - TOXENV: '2.7-nocov' TOXPYTHON: C:\Python27\python.exe PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - TOXENV: '2.7-cover,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: '2.7-nocov' TOXPYTHON: C:\Python27-x64\python.exe WINDOWS_SDK_VERSION: v7.0 PYTHON_HOME: C:\Python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - TOXENV: '3.4-cover,codecov' TOXPYTHON: C:\Python34\python.exe PYTHON_HOME: C:\Python34 PYTHON_VERSION: '3.4' PYTHON_ARCH: '32' - TOXENV: '3.4-nocov' TOXPYTHON: C:\Python34\python.exe PYTHON_HOME: C:\Python34 PYTHON_VERSION: '3.4' PYTHON_ARCH: '32' - TOXENV: '3.4-cover,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: '3.4-nocov' TOXPYTHON: C:\Python34-x64\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\Python34-x64 PYTHON_VERSION: '3.4' PYTHON_ARCH: '64' - TOXENV: '3.5-cover,codecov' TOXPYTHON: C:\Python35\python.exe PYTHON_HOME: C:\Python35 PYTHON_VERSION: '3.5' PYTHON_ARCH: '32' - TOXENV: '3.5-nocov' TOXPYTHON: C:\Python35\python.exe PYTHON_HOME: C:\Python35 PYTHON_VERSION: '3.5' PYTHON_ARCH: '32' - TOXENV: '3.5-cover,codecov' TOXPYTHON: C:\Python35-x64\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\Python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' - TOXENV: '3.5-nocov' TOXPYTHON: C:\Python35-x64\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\Python35-x64 PYTHON_VERSION: '3.5' 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: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) tblib-1.2.0/MANIFEST.in0000664000175000017500000000055012625714526014616 0ustar ionelionel00000000000000graft docs graft examples graft src graft ci graft tests include .bumpversion.cfg include .coveragerc include .cookiecutterrc include .isort.cfg include .pylintrc 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.2.0/.coveragerc0000664000175000017500000000020712625714526015200 0ustar ionelionel00000000000000[paths] source = src [run] branch = True source = src parallel = true [report] show_missing = true precision = 2 omit = *migrations* tblib-1.2.0/setup.py0000664000175000017500000000460112634621336014567 0ustar ionelionel00000000000000#!/usr/bin/env python # -*- encoding: utf-8 -*- from __future__ import absolute_import, print_function import io import os 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 relpath 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.2.0', license='BSD', description='Traceback fiddling library. For now 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).', long_description='%s\n%s' % (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.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', '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.2.0/CONTRIBUTING.rst0000664000175000017500000000523012625714526015521 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 contributions are welcome :) Development =========== To set up `python-tblib` for local development: 1. `Fork python-tblib on GitHub `_. 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``):: detoxtblib-1.2.0/LICENSE0000664000175000017500000000243312625714526014067 0ustar ionelionel00000000000000Copyright (c) 2013-2015, 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.2.0/PKG-INFO0000664000175000017500000005165512634623345014167 0ustar ionelionel00000000000000Metadata-Version: 1.1 Name: tblib Version: 1.2.0 Summary: Traceback fiddling library. For now 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). Home-page: https://github.com/ionelmc/python-tblib Author: Ionel Cristian Mărieș Author-email: contact@ionelmc.ro License: BSD Description: ===== tblib ===== .. list-table:: :stub-columns: 1 * - docs - |docs| * - tests - | |travis| |appveyor| |requires| | |coveralls| |codecov| | |landscape| |scrutinizer| |codacy| |codeclimate| * - package - |version| |downloads| |wheel| |supported-versions| |supported-implementations| .. |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/REPLACE_WITH_PROJECT_ID.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?style=flat :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/tblib .. |downloads| image:: https://img.shields.io/pypi/dm/tblib.svg?style=flat :alt: PyPI Package monthly downloads :target: https://pypi.python.org/pypi/tblib .. |wheel| image:: https://img.shields.io/pypi/wheel/tblib.svg?style=flat :alt: PyPI Wheel :target: https://pypi.python.org/pypi/tblib .. |supported-versions| image:: https://img.shields.io/pypi/pyversions/tblib.svg?style=flat :alt: Supported versions :target: https://pypi.python.org/pypi/tblib .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/tblib.svg?style=flat :alt: Supported implementations :target: https://pypi.python.org/pypi/tblib .. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-tblib/master.svg?style=flat :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/ionelmc/python-tblib/ Traceback fiddling library. For now 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). * Free software: BSD license Installation ============ :: pip install tblib Documentation ============= 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 The tblib.Traceback object -------------------------- 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 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 tblib import Traceback >>> 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}}}} >>> 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 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 5, in func_b func_c() File "...examples.py", line 8, in func_c func_d() File "...examples.py", line 11, 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 5, in func_b func_c() File "...tests...examples.py", line 8, in func_c func_d() File "...tests...examples.py", line 11, in func_d raise Exception("Guessing time !") Exception: Guessing time ! Credits ======= * `mitsuhiko/jinja2 `_ for figuring a way to create traceback objects. Changelog ========= 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.6 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 :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Utilities tblib-1.2.0/.cookiecutterrc0000664000175000017500000000303512625714526016107 0ustar ionelionel00000000000000# This file exists so you can easily regenerate your project. # # Unfortunately cookiecutter can't use this right away so # you have to copy this file to ~/.cookiecutterrc default_context: appveyor: 'yes' c_extension_optional: 'no' c_extension_support: 'no' codacy: 'yes' codeclimate: 'yes' codecov: 'yes' command_line_interface: 'no' 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 fiddling library. For now 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).' release_date: '2015-07-27' repo_name: 'python-tblib' requiresio: 'yes' scrutinizer: 'yes' sphinx_theme: 'sphinx-py3doc-enhanced-theme' test_matrix_configurator: 'no' test_runner: 'no' travis: 'yes' version: '1.1.0' website: 'http://blog.ionelmc.ro' year: '2013-2015' tblib-1.2.0/tests/0000775000175000017500000000000012634623345014220 5ustar ionelionel00000000000000tblib-1.2.0/tests/test_tblib.py0000664000175000017500000000030212567711157016724 0ustar ionelionel00000000000000import sys import doctest if __name__ == '__main__': results = doctest.testfile('../README.rst', optionflags=doctest.ELLIPSIS) print(results) if results.failed: sys.exit(1) tblib-1.2.0/tests/examples.py0000664000175000017500000000021312567711157016410 0ustar ionelionel00000000000000def func_a(_): func_b() def func_b(): func_c() def func_c(): func_d() def func_d(): raise Exception("Guessing time !") tblib-1.2.0/CHANGELOG.rst0000664000175000017500000000123712634621117015075 0ustar ionelionel00000000000000 Changelog ========= 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 `_.