summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2020-07-25 00:58:33 +0100
committerColin Watson <cjwatson@debian.org>2020-07-25 00:58:33 +0100
commitbe15a5ec8a45c072c2262e3a7ce598004f8739cf (patch)
tree91242dabb2cc05de83b49a3cc41c629509ce4387
parent1ff0be431951ff9b61e39d2bb2ca9a243cb90d78 (diff)
New upstream version 1.7.0
-rw-r--r--.bumpversion.cfg2
-rw-r--r--.cookiecutterrc75
-rw-r--r--.readthedocs.yml10
-rw-r--r--AUTHORS.rst1
-rw-r--r--CHANGELOG.rst8
-rw-r--r--LICENSE3
-rw-r--r--MANIFEST.in4
-rw-r--r--PKG-INFO28
-rw-r--r--README.rst25
-rw-r--r--ci/requirements.txt1
-rw-r--r--docs/conf.py10
-rw-r--r--docs/index.rst1
-rw-r--r--docs/reference/index.rst7
-rw-r--r--docs/reference/tblib.rst9
-rw-r--r--docs/requirements.txt1
-rw-r--r--setup.cfg3
-rw-r--r--setup.py2
-rw-r--r--src/tblib.egg-info/PKG-INFO28
-rw-r--r--src/tblib.egg-info/SOURCES.txt6
-rw-r--r--src/tblib/__init__.py99
-rw-r--r--tests/test_tblib.py144
-rw-r--r--tox.ini5
22 files changed, 356 insertions, 116 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 828f46b..1d8622c 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 1.6.0
+current_version = 1.7.0
commit = True
tag = True
diff --git a/.cookiecutterrc b/.cookiecutterrc
index 5e0c749..1836a3d 100644
--- a/.cookiecutterrc
+++ b/.cookiecutterrc
@@ -1,55 +1,52 @@
# Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher)
cookiecutter:
- _extensions:
- - jinja2_time.TimeExtension
- _template: /home/ionel/open-source/cookiecutter-pylibrary
- allow_tests_inside_package: no
- appveyor: yes
- c_extension_function: '-'
- c_extension_module: '-'
- c_extension_optional: no
+ full_name: Ionel Cristian Mărieș
+ email: contact@ionelmc.ro
+ website: https://blog.ionelmc.ro/
+ project_name: tblib
+ repo_name: python-tblib
+ repo_hosting: github.com
+ repo_hosting_domain: github.com
+ repo_username: ionelmc
+ package_name: tblib
+ distribution_name: tblib
+ project_short_description: Traceback serialization library.
+ release_date: '2020-03-07'
+ year_from: '2013'
+ year_to: '2'
+ version: 1.6.0
+ license: BSD 2-Clause License
c_extension_support: no
+ c_extension_optional: no
+ c_extension_module: '-'
+ c_extension_function: '-'
c_extension_test_pypi: no
c_extension_test_pypi_username: '-'
- codacy: no
- codacy_projectid: '-'
- codeclimate: no
- codecov: yes
+ test_matrix_configurator: no
+ test_matrix_separate_coverage: no
+ test_runner: pytest
+ setup_py_uses_test_runner: no
+ setup_py_uses_setuptools_scm: no
+ pypi_badge: yes
+ pypi_disable_upload: no
+ allow_tests_inside_package: no
+ linter: flake8
command_line_interface: no
command_line_interface_bin_name: '-'
coveralls: no
coveralls_token: '-'
- distribution_name: tblib
- email: contact@ionelmc.ro
- full_name: Ionel Cristian Mărieș
+ codecov: yes
landscape: no
- license: BSD 2-Clause License
- linter: flake8
- package_name: tblib
- project_name: tblib
- project_short_description: Traceback serialization library.
- pypi_badge: yes
- pypi_disable_upload: no
- release_date: '2019-05-02'
- repo_hosting: github.com
- repo_hosting_domain: github.com
- repo_name: python-tblib
- repo_username: ionelmc
- requiresio: yes
scrutinizer: no
- setup_py_uses_setuptools_scm: no
- setup_py_uses_test_runner: no
+ codacy: no
+ codacy_projectid: '-'
+ codeclimate: no
sphinx_docs: yes
- sphinx_docs_hosting: https://python-tblib.readthedocs.io/
- sphinx_doctest: no
sphinx_theme: sphinx-py3doc-enhanced-theme
- test_matrix_configurator: no
- test_matrix_separate_coverage: no
- test_runner: pytest
+ sphinx_doctest: no
+ sphinx_docs_hosting: https://python-tblib.readthedocs.io/
travis: yes
travis_osx: no
- version: 1.4.0
- website: https://blog.ionelmc.ro/
- year_from: '2013'
- year_to: '2019'
+ appveyor: yes
+ requiresio: yes
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 0000000..59ff5c0
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,10 @@
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+version: 2
+sphinx:
+ configuration: docs/conf.py
+formats: all
+python:
+ install:
+ - requirements: docs/requirements.txt
+ - method: pip
+ path: .
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 3800ae0..5f6547d 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -10,3 +10,4 @@ Authors
* Elliott Sales de Andrade - https://github.com/QuLogic
* Victor Stinner - https://github.com/vstinner
* Guido Imperiale - https://github.com/crusaderky
+* Ivanq - https://github.com/imachug
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5b08ce8..c3c5aeb 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,6 +2,12 @@
Changelog
=========
+1.7.0 (2020-07-24)
+~~~~~~~~~~~~~~~~~~
+
+* Add more attributes to ``Frame`` and ``Code`` objects for pytest compatibility. Contributed by Ivanq in
+ `#58 <https://github.com/ionelmc/python-tblib/pull/58>`_.
+
1.6.0 (2019-12-07)
~~~~~~~~~~~~~~~~~~
@@ -13,7 +19,7 @@ Changelog
~~~~~~~~~~~~~~~~~~
* Added support for Python 3.8. Contributed by Victor Stinner in
- `#42 <HTTPS://GITHUB.COM/IONELMC/PYTHON-TBLIB/ISSUES/42>`_.
+ `#42 <https://github.com/ionelmc/python-tblib/issues/42>`_.
* Removed support for end of life Python 3.4.
* Few CI improvements and fixes.
diff --git a/LICENSE b/LICENSE
index 863f780..7dac254 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,6 @@
BSD 2-Clause License
-Copyright (c) 2013-2019, Ionel Cristian Mărieș
-All rights reserved.
+Copyright (c) 2013-2020, 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:
diff --git a/MANIFEST.in b/MANIFEST.in
index 81df70c..8b9e93d 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -14,6 +14,6 @@ include CONTRIBUTING.rst
include LICENSE
include README.rst
-include tox.ini .travis.yml .appveyor.yml
+include tox.ini .travis.yml .appveyor.yml .readthedocs.yml
-global-exclude *.py[cod] __pycache__ *.so *.dylib
+global-exclude *.py[cod] __pycache__/* *.so *.dylib
diff --git a/PKG-INFO b/PKG-INFO
index 7f8efc3..122805d 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: tblib
-Version: 1.6.0
+Version: 1.7.0
Summary: Traceback serialization library.
Home-page: https://github.com/ionelmc/python-tblib
Author: Ionel Cristian Mărieș
@@ -398,19 +398,23 @@ Description: ========
... pprint(tb_dict)
{'tb_frame': {'f_code': {'co_filename': '<doctest README.rst[...]>',
'co_name': '<module>'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 5},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_2'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_1'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_0'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
'tb_next': None}}}}
@@ -669,6 +673,12 @@ Description: ========
Changelog
=========
+ 1.7.0 (2020-07-24)
+ ~~~~~~~~~~~~~~~~~~
+
+ * Add more attributes to ``Frame`` and ``Code`` objects for pytest compatibility. Contributed by Ivanq in
+ `#58 <https://github.com/ionelmc/python-tblib/pull/58>`_.
+
1.6.0 (2019-12-07)
~~~~~~~~~~~~~~~~~~
@@ -680,7 +690,7 @@ Description: ========
~~~~~~~~~~~~~~~~~~
* Added support for Python 3.8. Contributed by Victor Stinner in
- `#42 <HTTPS://GITHUB.COM/IONELMC/PYTHON-TBLIB/ISSUES/42>`_.
+ `#42 <https://github.com/ionelmc/python-tblib/issues/42>`_.
* Removed support for end of life Python 3.4.
* Few CI improvements and fixes.
diff --git a/README.rst b/README.rst
index 2bfbc6d..3d003da 100644
--- a/README.rst
+++ b/README.rst
@@ -15,7 +15,8 @@ Overview
* - package
- | |version| |wheel| |supported-versions| |supported-implementations|
| |commits-since|
-.. |docs| image:: https://readthedocs.org/projects/python-tblib/badge/?style=flat
+
+.. |docs| image:: https://codecov.io/gh/ionelmc/python-tblib/branch/master/graphs/badge.svg?branch=master
:target: https://readthedocs.org/projects/python-tblib
:alt: Documentation Status
@@ -51,9 +52,9 @@ Overview
:alt: Supported implementations
:target: https://pypi.org/project/tblib
-.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-tblib/v1.6.0.svg
+.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-tblib/v1.7.0.svg
:alt: Commits since latest release
- :target: https://github.com/ionelmc/python-tblib/compare/v1.6.0...master
+ :target: https://github.com/ionelmc/python-tblib/compare/v1.7.0...master
.. end-badges
@@ -440,19 +441,23 @@ json.JSONDecoder::
... pprint(tb_dict)
{'tb_frame': {'f_code': {'co_filename': '<doctest README.rst[...]>',
'co_name': '<module>'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 5},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_2'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_1'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_0'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
'tb_next': None}}}}
diff --git a/ci/requirements.txt b/ci/requirements.txt
index 1c8d385..b2a21e5 100644
--- a/ci/requirements.txt
+++ b/ci/requirements.txt
@@ -1,3 +1,4 @@
virtualenv>=16.6.0
pip>=19.1.1
setuptools>=18.0.1
+six>=1.12.0
diff --git a/docs/conf.py b/docs/conf.py
index f6a4e14..af0256c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -4,8 +4,7 @@ from __future__ import unicode_literals
import os
extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.autosummary',
+ 'autoapi.extension',
'sphinx.ext.coverage',
'sphinx.ext.doctest',
'sphinx.ext.extlinks',
@@ -14,13 +13,16 @@ extensions = [
'sphinx.ext.todo',
'sphinx.ext.viewcode',
]
+autoapi_type = 'python'
+autoapi_dirs = ['../src']
+
source_suffix = '.rst'
master_doc = 'index'
project = 'tblib'
-year = '2013-2019'
+year = '2013-2020'
author = 'Ionel Cristian Mărieș'
copyright = '{0}, {1}'.format(year, author)
-version = release = '1.6.0'
+version = release = '1.7.0'
pygments_style = 'trac'
templates_path = ['.']
diff --git a/docs/index.rst b/docs/index.rst
index 40f35b5..e55d633 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -8,7 +8,6 @@ Contents
readme
installation
usage
- reference/index
contributing
authors
changelog
diff --git a/docs/reference/index.rst b/docs/reference/index.rst
deleted file mode 100644
index 42fbe62..0000000
--- a/docs/reference/index.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-Reference
-=========
-
-.. toctree::
- :glob:
-
- tblib*
diff --git a/docs/reference/tblib.rst b/docs/reference/tblib.rst
deleted file mode 100644
index 26354ee..0000000
--- a/docs/reference/tblib.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-tblib
-=====
-
-.. testsetup::
-
- from tblib import *
-
-.. automodule:: tblib
- :members:
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 62bc14e..deb6219 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,2 +1,3 @@
sphinx>=1.3
sphinx-py3doc-enhanced-theme
+sphinx-autoapi
diff --git a/setup.cfg b/setup.cfg
index 9780078..597f8af 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,7 +6,6 @@ max-line-length = 140
exclude = */migrations/*
[tool:pytest]
-testpaths = tests
norecursedirs =
migrations
python_files =
@@ -22,6 +21,8 @@ addopts =
--doctest-continue-on-failure
--doctest-glob=\*.rst
--tb=short
+testpaths =
+ tests
[tool:isort]
force_single_line = True
diff --git a/setup.py b/setup.py
index fe7230c..9193b68 100644
--- a/setup.py
+++ b/setup.py
@@ -25,7 +25,7 @@ def read(*names, **kwargs):
setup(
name='tblib',
- version='1.6.0',
+ version='1.7.0',
license='BSD-2-Clause',
description='Traceback serialization library.',
long_description='%s\n%s' % (
diff --git a/src/tblib.egg-info/PKG-INFO b/src/tblib.egg-info/PKG-INFO
index 7f8efc3..122805d 100644
--- a/src/tblib.egg-info/PKG-INFO
+++ b/src/tblib.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: tblib
-Version: 1.6.0
+Version: 1.7.0
Summary: Traceback serialization library.
Home-page: https://github.com/ionelmc/python-tblib
Author: Ionel Cristian Mărieș
@@ -398,19 +398,23 @@ Description: ========
... pprint(tb_dict)
{'tb_frame': {'f_code': {'co_filename': '<doctest README.rst[...]>',
'co_name': '<module>'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 5},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_2'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_1'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
- 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...
+ 'tb_next': {'tb_frame': {'f_code': {'co_filename': ...,
'co_name': 'inner_0'},
- 'f_globals': {'__name__': '__main__'}},
+ 'f_globals': {'__name__': '__main__'},
+ 'f_lineno': 2},
'tb_lineno': 2,
'tb_next': None}}}}
@@ -669,6 +673,12 @@ Description: ========
Changelog
=========
+ 1.7.0 (2020-07-24)
+ ~~~~~~~~~~~~~~~~~~
+
+ * Add more attributes to ``Frame`` and ``Code`` objects for pytest compatibility. Contributed by Ivanq in
+ `#58 <https://github.com/ionelmc/python-tblib/pull/58>`_.
+
1.6.0 (2019-12-07)
~~~~~~~~~~~~~~~~~~
@@ -680,7 +690,7 @@ Description: ========
~~~~~~~~~~~~~~~~~~
* Added support for Python 3.8. Contributed by Victor Stinner in
- `#42 <HTTPS://GITHUB.COM/IONELMC/PYTHON-TBLIB/ISSUES/42>`_.
+ `#42 <https://github.com/ionelmc/python-tblib/issues/42>`_.
* Removed support for end of life Python 3.4.
* Few CI improvements and fixes.
diff --git a/src/tblib.egg-info/SOURCES.txt b/src/tblib.egg-info/SOURCES.txt
index a913451..f5cb383 100644
--- a/src/tblib.egg-info/SOURCES.txt
+++ b/src/tblib.egg-info/SOURCES.txt
@@ -4,6 +4,7 @@
.coveragerc
.editorconfig
.gitignore
+.readthedocs.yml
.travis.yml
AUTHORS.rst
CHANGELOG.rst
@@ -30,8 +31,6 @@ 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
@@ -45,4 +44,5 @@ tests/badmodule.py
tests/badsyntax.py
tests/examples.py
tests/test_issue30.py
-tests/test_pickle_exception.py \ No newline at end of file
+tests/test_pickle_exception.py
+tests/test_tblib.py \ No newline at end of file
diff --git a/src/tblib/__init__.py b/src/tblib/__init__.py
index 163fc62..7e717b4 100644
--- a/src/tblib/__init__.py
+++ b/src/tblib/__init__.py
@@ -1,6 +1,7 @@
import re
import sys
from types import CodeType
+from types import FrameType
from types import TracebackType
try:
@@ -15,8 +16,8 @@ except ImportError:
if not tb_set_next and not tproxy:
raise ImportError("Cannot use tblib. Runtime not supported.")
-__version__ = '1.6.0'
-__all__ = 'Traceback',
+__version__ = '1.7.0'
+__all__ = 'Traceback', 'TracebackParseError', 'Frame', 'Code'
PY3 = sys.version_info[0] == 3
FRAME_RE = re.compile(r'^\s*File "(?P<co_filename>.+)", line (?P<tb_lineno>\d+)(, in (?P<co_name>.+))?$')
@@ -24,7 +25,12 @@ FRAME_RE = re.compile(r'^\s*File "(?P<co_filename>.+)", line (?P<tb_lineno>\d+)(
class _AttrDict(dict):
__slots__ = ()
- __getattr__ = dict.__getitem__
+
+ def __getattr__(self, name):
+ try:
+ return self[name]
+ except KeyError:
+ raise AttributeError(name)
# noinspection PyPep8Naming
@@ -37,33 +43,73 @@ class TracebackParseError(Exception):
class Code(object):
-
+ """
+ Class that replicates just enough of the builtin Code object to enable serialization and traceback rendering.
+ """
co_code = None
def __init__(self, code):
self.co_filename = code.co_filename
self.co_name = code.co_name
+ self.co_argcount = 0
+ self.co_kwonlyargcount = 0
+ self.co_varnames = ()
+ self.co_nlocals = 0
+ self.co_stacksize = 0
+ self.co_flags = 64
+ self.co_firstlineno = 0
+
+ # noinspection SpellCheckingInspection
+ def __tproxy__(self, operation, *args, **kwargs):
+ """
+ Necessary for PyPy's tproxy.
+ """
+ if operation in ('__getattribute__', '__getattr__'):
+ return getattr(self, args[0])
+ else:
+ return getattr(self, operation)(*args, **kwargs)
class Frame(object):
+ """
+ Class that replicates just enough of the builtin Frame object to enable serialization and traceback rendering.
+ """
def __init__(self, frame):
+ self.f_locals = {}
self.f_globals = {
k: v
for k, v in frame.f_globals.items()
if k in ("__file__", "__name__")
}
self.f_code = Code(frame.f_code)
+ self.f_lineno = frame.f_lineno
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
+ """
+ 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
+ """
+ # noinspection SpellCheckingInspection
+ def __tproxy__(self, operation, *args, **kwargs):
+ """
+ Necessary for PyPy's tproxy.
+ """
+ if operation in ('__getattribute__', '__getattr__'):
+ if args[0] == 'f_code':
+ return tproxy(CodeType, self.f_code.__tproxy__)
+ else:
+ return getattr(self, args[0])
+ else:
+ return getattr(self, operation)(*args, **kwargs)
-class Traceback(object):
+class Traceback(object):
+ """
+ Class that wraps builtin Traceback objects.
+ """
tb_next = None
def __init__(self, tb):
@@ -84,8 +130,11 @@ class Traceback(object):
tb = tb.tb_next
def as_traceback(self):
+ """
+ Convert to a builtin Traceback object that is usable for raising or rendering a stacktrace.
+ """
if tproxy:
- return tproxy(TracebackType, self.__tproxy_handler)
+ return tproxy(TracebackType, self.__tproxy__)
if not tb_set_next:
raise RuntimeError("Unsupported Python interpreter!")
@@ -119,7 +168,7 @@ class Traceback(object):
# noinspection PyBroadException
try:
- exec(code, current.tb_frame.f_globals, {})
+ exec(code, dict(current.tb_frame.f_globals), {})
except Exception:
next_tb = sys.exc_info()[2].tb_next
if top_tb is None:
@@ -135,19 +184,28 @@ class Traceback(object):
finally:
del top_tb
del tb
+ to_traceback = as_traceback
# noinspection SpellCheckingInspection
- def __tproxy_handler(self, operation, *args, **kwargs):
+ def __tproxy__(self, operation, *args, **kwargs):
+ """
+ Necessary for PyPy's tproxy.
+ """
if operation in ('__getattribute__', '__getattr__'):
if args[0] == 'tb_next':
return self.tb_next and self.tb_next.as_traceback()
+ elif args[0] == 'tb_frame':
+ return tproxy(FrameType, self.tb_frame.__tproxy__)
else:
return getattr(self, args[0])
else:
return getattr(self, operation)(*args, **kwargs)
- def to_dict(self):
- """Convert a Traceback into a dictionary representation"""
+ def as_dict(self):
+ """
+ Converts to a dictionary representation. You can serialize the result to JSON as it only has
+ builtin objects like dicts, lists, ints or strings.
+ """
if self.tb_next is None:
tb_next = None
else:
@@ -160,15 +218,20 @@ class Traceback(object):
frame = {
'f_globals': self.tb_frame.f_globals,
'f_code': code,
+ 'f_lineno': self.tb_frame.f_lineno,
}
return {
'tb_frame': frame,
'tb_lineno': self.tb_lineno,
'tb_next': tb_next,
}
+ to_dict = as_dict
@classmethod
def from_dict(cls, dct):
+ """
+ Creates an instance from a dictionary with the same structure as ``.as_dict()`` returns.
+ """
if dct['tb_next']:
tb_next = cls.from_dict(dct['tb_next'])
else:
@@ -181,6 +244,7 @@ class Traceback(object):
frame = _AttrDict(
f_globals=dct['tb_frame']['f_globals'],
f_code=code,
+ f_lineno=dct['tb_frame']['f_lineno'],
)
tb = _AttrDict(
tb_frame=frame,
@@ -191,6 +255,10 @@ class Traceback(object):
@classmethod
def from_string(cls, string, strict=True):
+ """
+ Creates an instance by parsing a stacktrace. Strict means that parsing stops when lines are not indented by at least two spaces
+ anymore.
+ """
frames = []
header = strict
@@ -220,6 +288,7 @@ class Traceback(object):
__name__='?',
),
f_code=_AttrDict(frame),
+ f_lineno=int(frame['tb_lineno']),
),
tb_next=previous,
)
diff --git a/tests/test_tblib.py b/tests/test_tblib.py
new file mode 100644
index 0000000..bade6d4
--- /dev/null
+++ b/tests/test_tblib.py
@@ -0,0 +1,144 @@
+import pickle
+import traceback
+
+from tblib import Traceback
+from tblib import pickling_support
+
+pickling_support.install()
+
+pytest_plugins = 'pytester',
+
+
+def test_parse_traceback():
+ tb1 = Traceback.from_string(
+ """
+Traceback (most recent call last):
+ File "file1", line 123, in <module>
+ code1
+ File "file2", line 234, in ???
+ code2
+ File "file3", line 345, in function3
+ File "file4", line 456, in
+ code4
+KeyboardInterrupt"""
+ )
+ pytb = tb1.as_traceback()
+ assert traceback.format_tb(pytb) == [
+ ' File "file1", line 123, in <module>\n',
+ ' File "file2", line 234, in ???\n',
+ ' File "file3", line 345, in function3\n',
+ ]
+ tb2 = Traceback(pytb)
+
+ expected_dict = {
+ "tb_frame": {
+ "f_code": {"co_filename": "file1", "co_name": "<module>"},
+ "f_globals": {"__file__": "file1", "__name__": "?"},
+ "f_lineno": 123,
+ },
+ "tb_lineno": 123,
+ "tb_next": {
+ "tb_frame": {
+ "f_code": {"co_filename": "file2", "co_name": "???"},
+ "f_globals": {"__file__": "file2", "__name__": "?"},
+ "f_lineno": 234,
+ },
+ "tb_lineno": 234,
+ "tb_next": {
+ "tb_frame": {
+ "f_code": {"co_filename": "file3", "co_name": "function3"},
+ "f_globals": {"__file__": "file3", "__name__": "?"},
+ "f_lineno": 345,
+ },
+ "tb_lineno": 345,
+ "tb_next": None,
+ },
+ },
+ }
+ tb3 = Traceback.from_dict(expected_dict)
+ tb4 = pickle.loads(pickle.dumps(tb3))
+ assert tb4.as_dict() == tb3.as_dict() == tb2.as_dict() == tb1.as_dict() == expected_dict
+
+
+def test_pytest_integration(testdir):
+ test = testdir.makepyfile("""
+import six
+
+from tblib import Traceback
+
+def test_raise():
+ tb1 = Traceback.from_string('''
+Traceback (most recent call last):
+ File "file1", line 123, in <module>
+ code1
+ File "file2", line 234, in ???
+ code2
+ File "file3", line 345, in function3
+ File "file4", line 456, in ""
+''')
+ pytb = tb1.as_traceback()
+ six.reraise(RuntimeError, RuntimeError(), pytb)
+""")
+
+ # mode(auto / long / short / line / native / no).
+
+ result = testdir.runpytest_subprocess('--tb=long', '-vv', test)
+ result.stdout.fnmatch_lines([
+ "_ _ _ _ _ _ _ _ *",
+ "",
+ "> [?][?][?]",
+ "",
+ "file1:123:*",
+ "_ _ _ _ _ _ _ _ *",
+ "",
+ "> [?][?][?]",
+ "",
+ "file2:234:*",
+ "_ _ _ _ _ _ _ _ *",
+ "",
+ "> [?][?][?]",
+ "",
+ "file3:345:*",
+ "_ _ _ _ _ _ _ _ *",
+ "",
+ "> [?][?][?]",
+ "E RuntimeError",
+ "",
+ "file4:456: RuntimeError",
+ "===*=== 1 failed in * ===*===",
+ ])
+
+ result = testdir.runpytest_subprocess('--tb=short', '-vv', test)
+ result.stdout.fnmatch_lines([
+ 'test_pytest_integration.py:*: in test_raise',
+ ' six.reraise(RuntimeError, RuntimeError(), pytb)',
+ 'file1:123: in <module>',
+ ' ???',
+ 'file2:234: in ???',
+ ' ???',
+ 'file3:345: in function3',
+ ' ???',
+ 'file4:456: in ""',
+ ' ???',
+ 'E RuntimeError',
+ ])
+
+ result = testdir.runpytest_subprocess('--tb=line', '-vv', test)
+ result.stdout.fnmatch_lines([
+ "===*=== FAILURES ===*===",
+ "file4:456: RuntimeError",
+ "===*=== 1 failed in * ===*===",
+ ])
+
+ result = testdir.runpytest_subprocess('--tb=native', '-vv', test)
+ result.stdout.fnmatch_lines([
+ 'Traceback (most recent call last):',
+ ' File "*test_pytest_integration.py", line *, in test_raise',
+ ' six.reraise(RuntimeError, RuntimeError(), pytb)',
+ ' File "file1", line 123, in <module>',
+ ' File "file2", line 234, in ???',
+ ' File "file3", line 345, in function3',
+ ' File "file4", line 456, in ""',
+ 'RuntimeError',
+
+ ])
diff --git a/tox.ini b/tox.ini
index 95b3075..e052f92 100644
--- a/tox.ini
+++ b/tox.ini
@@ -25,10 +25,10 @@ basepython =
pypy3: {env:TOXPYTHON:pypy3}
py27: {env:TOXPYTHON:python2.7}
py35: {env:TOXPYTHON:python3.5}
- {py36,docs}: {env:TOXPYTHON:python3.6}
+ py36: {env:TOXPYTHON:python3.6}
py37: {env:TOXPYTHON:python3.7}
py38: {env:TOXPYTHON:python3.8}
- {bootstrap,clean,check,report,codecov}: {env:TOXPYTHON:python3}
+ {bootstrap,clean,check,report,codecov,docs}: {env:TOXPYTHON:python3}
setenv =
PYTHONPATH={toxinidir}/tests
PYTHONUNBUFFERED=yes
@@ -39,6 +39,7 @@ deps =
pytest
pytest-travis-fold
pytest-cov
+ pytest-clarity
six
py{27,35,36,37,38,py,py3}: twisted
commands =