pax_global_header00006660000000000000000000000064137110157540014516gustar00rootroot0000000000000052 comment=db4bbb778669fd11c709919a99f04b09cfe2ff2e crashtest-0.3.1/000077500000000000000000000000001371101575400135175ustar00rootroot00000000000000crashtest-0.3.1/.coveragerc000066400000000000000000000000671371101575400156430ustar00rootroot00000000000000[report] exclude_lines = raise NotImplementedError crashtest-0.3.1/.flake8000066400000000000000000000003711371101575400146730ustar00rootroot00000000000000[flake8] max-line-length = 88 ignore = E501, E203, W503 per-file-ignores = __init__.py:F401 exclude = .git __pycache__ setup.py build dist releases .venv .tox .mypy_cache .pytest_cache .vscode .github crashtest-0.3.1/.github/000077500000000000000000000000001371101575400150575ustar00rootroot00000000000000crashtest-0.3.1/.github/workflows/000077500000000000000000000000001371101575400171145ustar00rootroot00000000000000crashtest-0.3.1/.github/workflows/main.yml000066400000000000000000000044131371101575400205650ustar00rootroot00000000000000name: Main on: push: branches: - master tags: - '**' pull_request: branches: - '**' jobs: flake8: runs-on: ubuntu-latest steps: - name: Setup Python uses: actions/setup-python@v1 with: python-version: 3.7 architecture: x64 - uses: actions/checkout@master - name: Install flake8 run: pip install flake8 - name: Run flake8 uses: suo/flake8-github-action@releases/v1 with: checkName: 'flake8' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} black: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Run Black uses: lgeiger/black-action@master with: args: --check crashtest/ tests/ tests: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: python-version: [3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Get full Python version id: full-python-version shell: bash run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") - name: Install poetry shell: bash run: | curl -fsS -o get-poetry.py https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py python get-poetry.py -y echo "::set-env name=PATH::$HOME/.poetry/bin:$PATH" - name: Configure poetry shell: bash run: poetry config virtualenvs.in-project true - name: Set up cache uses: actions/cache@v2 id: cache with: path: .venv key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/poetry.lock') }} - name: Ensure cache is healthy if: steps.cache.outputs.cache-hit == 'true' shell: bash run: poetry run pip --version >/dev/null 2>&1 || rm -rf .venv - name: Install dependencies shell: bash run: poetry install - name: Run pytest shell: bash run: poetry run pytest -v tests crashtest-0.3.1/.github/workflows/release.yml000066400000000000000000000016301371101575400212570ustar00rootroot00000000000000name: Release on: push: tags: - '**' jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Get tag id: tag run: | echo ::set-output name=tag::${GITHUB_REF#refs/tags/} - name: Set up Python 3.7 uses: actions/setup-python@v1 with: python-version: 3.7 - name: Install dependencies run: python -m pip install --upgrade pip poetry --pre - name: Publish release to PyPI env: POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} run: | poetry build poetry publish - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} with: tag_name: ${{ steps.tag.outputs.tag }} release_name: ${{ steps.tag.outputs.tag }} draft: false prerelease: false crashtest-0.3.1/.gitignore000066400000000000000000000003661371101575400155140ustar00rootroot00000000000000*.pyc # Packages *.egg *.egg-info dist build .cache # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml .DS_Store .idea/* .vscode/* .python-version test.py /test .pytest_cache pip-wheel-metadata setup.py crashtest-0.3.1/.pre-commit-config.yaml000066400000000000000000000011371371101575400200020ustar00rootroot00000000000000repos: - repo: https://github.com/psf/black rev: stable hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 rev: 3.7.8 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-isort rev: v4.3.21 hooks: - id: isort additional_dependencies: [toml] exclude: ^.*/?setup\.py$ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.3.0 hooks: - id: trailing-whitespace exclude: ^tests/.*/fixtures/.* - id: end-of-file-fixer exclude: ^tests/.*/fixtures/.* - id: debug-statements crashtest-0.3.1/LICENSE000066400000000000000000000020461371101575400145260ustar00rootroot00000000000000Copyright (c) 2020 Sébastien Eustace Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. crashtest-0.3.1/README.md000066400000000000000000000001411371101575400147720ustar00rootroot00000000000000# Crashtest Crashtest is a Python library that makes exceptions handling and inspection easier. crashtest-0.3.1/crashtest/000077500000000000000000000000001371101575400155175ustar00rootroot00000000000000crashtest-0.3.1/crashtest/__init__.py000066400000000000000000000000261371101575400176260ustar00rootroot00000000000000__version__ = "0.3.1" crashtest-0.3.1/crashtest/contracts/000077500000000000000000000000001371101575400175175ustar00rootroot00000000000000crashtest-0.3.1/crashtest/contracts/__init__.py000066400000000000000000000000001371101575400216160ustar00rootroot00000000000000crashtest-0.3.1/crashtest/contracts/base_solution.py000066400000000000000000000010051371101575400227330ustar00rootroot00000000000000from typing import List from .solution import Solution class BaseSolution(Solution): def __init__(self, title: str = None, description: str = None) -> None: self._title = title self._description = description self._links = [] @property def solution_title(self) -> str: return self._title @property def solution_description(self) -> str: return self._description @property def documentation_links(self) -> List[str]: return self._links crashtest-0.3.1/crashtest/contracts/has_solutions_for_exception.py000066400000000000000000000004371371101575400257130ustar00rootroot00000000000000from typing import List from .solution import Solution class HasSolutionsForException: def can_solve(self, exception: Exception) -> bool: raise NotImplementedError() def get_solutions(self, exception: Exception) -> List[Solution]: raise NotImplementedError() crashtest-0.3.1/crashtest/contracts/provides_solution.py000066400000000000000000000002171371101575400236600ustar00rootroot00000000000000from .solution import Solution class ProvidesSolution: @property def solution(self) -> Solution: raise NotImplementedError() crashtest-0.3.1/crashtest/contracts/solution.py000066400000000000000000000005021371101575400217420ustar00rootroot00000000000000from typing import List class Solution: @property def solution_title(self) -> str: raise NotImplementedError() @property def solution_description(self) -> str: raise NotImplementedError() @property def documentation_links(self) -> List[str]: raise NotImplementedError() crashtest-0.3.1/crashtest/contracts/solution_provider_repository.py000066400000000000000000000010541371101575400261560ustar00rootroot00000000000000from typing import List from typing import Type from .solution import Solution class SolutionProviderRepository: def register_solution_provider( self, solution_provider_class: Type ) -> "SolutionProviderRepository": raise NotImplementedError() def register_solution_providers( self, solution_provider_classes: List[Type] ) -> "SolutionProviderRepository": raise NotImplementedError() def get_solutions_for_exception(self, exception: Exception) -> List[Solution]: raise NotImplementedError() crashtest-0.3.1/crashtest/frame.py000066400000000000000000000040021371101575400171570ustar00rootroot00000000000000import inspect from types import FrameType from typing import Dict class Frame: _content_cache: Dict[str, str] = {} def __init__(self, frame_info: inspect.FrameInfo) -> None: self._frame = frame_info.frame self._frame_info = frame_info self._lineno = frame_info.lineno self._filename = frame_info.filename self._function = frame_info.function self._lines = None self._file_content = None @property def frame(self) -> FrameType: return self._frame @property def lineno(self) -> int: return self._lineno @property def filename(self) -> str: return self._filename @property def function(self) -> str: return self._function @property def line(self) -> str: if not self._frame_info.code_context: return "" return self._frame_info.code_context[0] @property def file_content(self) -> str: if self._file_content is None: if not self._filename: file_content = "" else: if self._filename not in self.__class__._content_cache: try: with open(self._filename) as f: file_content = f.read() except OSError: file_content = "" self.__class__._content_cache[self._filename] = file_content file_content = self.__class__._content_cache[self._filename] self._file_content = file_content return self._file_content def __hash__(self) -> int: return hash(self._filename) ^ hash(self._function) ^ hash(self._lineno) def __eq__(self, other: "Frame") -> bool: return ( self._filename == other.filename and self._function == other.function and self._lineno == other.lineno ) def __repr__(self) -> str: return f"" crashtest-0.3.1/crashtest/frame_collection.py000066400000000000000000000041011371101575400213720ustar00rootroot00000000000000from typing import List from typing import Optional from .frame import Frame class FrameCollection(list): def __init__(self, frames: Optional[List[Frame]] = None, count: int = 0) -> None: if frames is None: frames = [] super().__init__(frames) self._count = count @property def repetitions(self) -> int: return self._count - 1 def is_repeated(self) -> bool: return self._count > 1 def increment_count(self, increment: int = 1) -> "FrameCollection": self._count += increment return self def compact(self) -> List["FrameCollection"]: """ Compacts the frames to deduplicate recursive calls. """ collections = [] current_collection = FrameCollection() i = 0 while i < len(self) - 1: frame = self[i] if frame in self[i + 1 :]: duplicate_indices = [] for sub_index, sub_frame in enumerate(self[i + 1 :]): if frame == sub_frame: duplicate_indices.append(sub_index + i + 1) found_duplicate = False for duplicate_index in duplicate_indices: collection = FrameCollection(self[i:duplicate_index]) if collection == current_collection: current_collection.increment_count() i = duplicate_index found_duplicate = True break if found_duplicate: continue collections.append(current_collection) current_collection = FrameCollection(self[i : duplicate_indices[0]]) i = duplicate_indices[0] continue if current_collection.is_repeated(): collections.append(current_collection) current_collection = FrameCollection() current_collection.append(frame) i += 1 collections.append(current_collection) return collections crashtest-0.3.1/crashtest/inspector.py000066400000000000000000000023771371101575400201100ustar00rootroot00000000000000import inspect from typing import Optional from .frame import Frame from .frame_collection import FrameCollection class Inspector: def __init__(self, exception: Exception): self._exception = exception self._frames = None self._outer_frames = None self._inner_frames = None self._previous_exception = exception.__context__ @property def exception(self) -> Exception: return self._exception @property def exception_name(self) -> str: return self._exception.__class__.__name__ @property def exception_message(self) -> str: return str(self._exception) @property def frames(self) -> FrameCollection: if self._frames is not None: return self._frames self._frames = FrameCollection() tb = self._exception.__traceback__ while tb: frame_info = inspect.getframeinfo(tb) self._frames.append(Frame(inspect.FrameInfo(tb.tb_frame, *frame_info))) tb = tb.tb_next return self._frames @property def previous_exception(self) -> Optional[Exception]: return self._previous_exception def has_previous_exception(self) -> bool: return self._previous_exception is not None crashtest-0.3.1/crashtest/solution_providers/000077500000000000000000000000001371101575400214705ustar00rootroot00000000000000crashtest-0.3.1/crashtest/solution_providers/__init__.py000066400000000000000000000000001371101575400235670ustar00rootroot00000000000000crashtest-0.3.1/crashtest/solution_providers/solution_provider_repository.py000066400000000000000000000041651371101575400301350ustar00rootroot00000000000000from typing import List from typing import Optional from typing import Type from crashtest.contracts.has_solutions_for_exception import HasSolutionsForException from crashtest.contracts.provides_solution import ProvidesSolution from crashtest.contracts.solution import Solution from crashtest.contracts.solution_provider_repository import ( SolutionProviderRepository as BaseSolutionProviderRepository, ) class SolutionProviderRepository(BaseSolutionProviderRepository): def __init__(self, solution_providers: Optional[List[Type]] = None) -> None: self._solution_providers = [] if solution_providers is None: solution_providers = [] self.register_solution_providers(solution_providers) def register_solution_provider( self, solution_provider_class: Type ) -> "SolutionProviderRepository": self._solution_providers.append(solution_provider_class) return self def register_solution_providers( self, solution_provider_classes: List[Type] ) -> "SolutionProviderRepository": for solution_provider_class in solution_provider_classes: self.register_solution_provider(solution_provider_class) return self def get_solutions_for_exception(self, exception: Exception) -> List[Solution]: solutions = [] if isinstance(exception, Solution): solutions.append(exception) if isinstance(exception, ProvidesSolution): solutions.append(exception.solution) for solution_provider_class in self._solution_providers: if not issubclass(solution_provider_class, HasSolutionsForException): continue solution_provider: HasSolutionsForException = solution_provider_class() try: if not solution_provider.can_solve(exception): continue except Exception: continue try: for solution in solution_provider.get_solutions(exception): solutions.append(solution) except Exception: continue return solutions crashtest-0.3.1/poetry.lock000066400000000000000000000676741371101575400157370ustar00rootroot00000000000000[[package]] category = "dev" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." name = "appdirs" optional = false python-versions = "*" version = "1.4.3" [[package]] category = "dev" description = "A few extensions to pyyaml." name = "aspy.yaml" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.3.0" [package.dependencies] pyyaml = "*" [[package]] category = "dev" description = "Atomic file writes." marker = "sys_platform == \"win32\"" name = "atomicwrites" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.3.0" [[package]] category = "dev" description = "Classes Without Boilerplate" name = "attrs" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "19.3.0" [package.extras] azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] docs = ["sphinx", "zope.interface"] tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] [[package]] category = "dev" description = "The uncompromising code formatter." name = "black" optional = false python-versions = ">=3.6" version = "19.10b0" [package.dependencies] appdirs = "*" attrs = ">=18.1.0" click = ">=6.5" pathspec = ">=0.6,<1" regex = "*" toml = ">=0.9.4" typed-ast = ">=1.4.0" [package.extras] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] category = "dev" description = "Validate configuration and produce human readable error messages." name = "cfgv" optional = false python-versions = ">=3.6" version = "3.0.0" [[package]] category = "dev" description = "Composable command line interface toolkit" name = "click" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "7.1.1" [[package]] category = "dev" description = "Cross-platform colored terminal text." marker = "sys_platform == \"win32\"" name = "colorama" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.4.3" [[package]] category = "dev" description = "Code coverage measurement for Python" name = "coverage" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" version = "5.0.4" [package.extras] toml = ["toml"] [[package]] category = "dev" description = "Distribution utilities" name = "distlib" optional = false python-versions = "*" version = "0.3.0" [[package]] category = "dev" description = "A platform independent file lock." name = "filelock" optional = false python-versions = "*" version = "3.0.12" [[package]] category = "dev" description = "File identification library for Python" name = "identify" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" version = "1.4.13" [package.extras] license = ["editdistance"] [[package]] category = "dev" description = "Read metadata from Python packages" marker = "python_version < \"3.8\"" name = "importlib-metadata" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" version = "1.6.0" [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["sphinx", "rst.linker"] testing = ["packaging", "importlib-resources"] [[package]] category = "dev" description = "Read resources from Python packages" marker = "python_version < \"3.7\"" name = "importlib-resources" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" version = "1.4.0" [package.dependencies] [package.dependencies.importlib-metadata] python = "<3.8" version = "*" [package.dependencies.zipp] python = "<3.8" version = ">=0.4" [package.extras] docs = ["sphinx", "rst.linker", "jaraco.packaging"] [[package]] category = "dev" description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" optional = false python-versions = ">=3.5" version = "8.2.0" [[package]] category = "dev" description = "Node.js virtual environment builder" name = "nodeenv" optional = false python-versions = "*" version = "1.3.5" [[package]] category = "dev" description = "Core utilities for Python packages" name = "packaging" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "20.3" [package.dependencies] pyparsing = ">=2.0.2" six = "*" [[package]] category = "dev" description = "Utility library for gitignore style pattern matching of file paths." name = "pathspec" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.7.0" [[package]] category = "dev" description = "plugin and hook calling mechanisms for python" name = "pluggy" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.13.1" [package.dependencies] [package.dependencies.importlib-metadata] python = "<3.8" version = ">=0.12" [package.extras] dev = ["pre-commit", "tox"] [[package]] category = "dev" description = "A framework for managing and maintaining multi-language pre-commit hooks." name = "pre-commit" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" version = "1.21.0" [package.dependencies] "aspy.yaml" = "*" cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = "*" six = "*" toml = "*" virtualenv = ">=15.2" [package.dependencies.importlib-metadata] python = "<3.8" version = "*" [package.dependencies.importlib-resources] python = "<3.7" version = "*" [[package]] category = "dev" description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.8.1" [[package]] category = "dev" description = "Python parsing module" name = "pyparsing" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" version = "2.4.6" [[package]] category = "dev" description = "pytest: simple powerful testing with Python" name = "pytest" optional = false python-versions = ">=3.5" version = "5.4.1" [package.dependencies] atomicwrites = ">=1.0" attrs = ">=17.4.0" colorama = "*" more-itertools = ">=4.0.0" packaging = "*" pluggy = ">=0.12,<1.0" py = ">=1.5.0" wcwidth = "*" [package.dependencies.importlib-metadata] python = "<3.8" version = ">=0.12" [package.extras] checkqa-mypy = ["mypy (v0.761)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] category = "dev" description = "Pytest plugin for measuring coverage." name = "pytest-cov" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.8.1" [package.dependencies] coverage = ">=4.4" pytest = ">=3.6" [package.extras] testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "virtualenv"] [[package]] category = "dev" description = "YAML parser and emitter for Python" name = "pyyaml" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "5.3.1" [[package]] category = "dev" description = "Alternative regular expression module, to replace re." name = "regex" optional = false python-versions = "*" version = "2020.2.20" [[package]] category = "dev" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" version = "1.14.0" [[package]] category = "dev" description = "Python Library for Tom's Obvious, Minimal Language" name = "toml" optional = false python-versions = "*" version = "0.10.0" [[package]] category = "dev" description = "a fork of Python 2 and 3 ast modules with type comment support" name = "typed-ast" optional = false python-versions = "*" version = "1.4.1" [[package]] category = "dev" description = "Virtual Python Environment builder" name = "virtualenv" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" version = "20.0.15" [package.dependencies] appdirs = ">=1.4.3,<2" distlib = ">=0.3.0,<1" filelock = ">=3.0.0,<4" six = ">=1.9.0,<2" [package.dependencies.importlib-metadata] python = "<3.8" version = ">=0.12,<2" [package.dependencies.importlib-resources] python = "<3.7" version = ">=1.0,<2" [package.extras] docs = ["sphinx (>=2.0.0,<3)", "sphinx-argparse (>=0.2.5,<1)", "sphinx-rtd-theme (>=0.4.3,<1)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2,<1)"] testing = ["pytest (>=4.0.0,<6)", "coverage (>=4.5.1,<6)", "pytest-mock (>=2.0.0,<3)", "pytest-env (>=0.6.2,<1)", "pytest-timeout (>=1.3.4,<2)", "packaging (>=20.0)", "xonsh (>=0.9.13,<1)"] [[package]] category = "dev" description = "Measures number of Terminal column cells of wide-character codes" name = "wcwidth" optional = false python-versions = "*" version = "0.1.9" [[package]] category = "dev" description = "Backport of pathlib-compatible object wrapper for zip files" marker = "python_version < \"3.8\"" name = "zipp" optional = false python-versions = ">=3.6" version = "3.1.0" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] content-hash = "f308842e9da34e25902342e2613bf67e30f96591bda6d639f88808f7a0e6880e" python-versions = "^3.6" [metadata.files] appdirs = [ {file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"}, {file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"}, ] "aspy.yaml" = [ {file = "aspy.yaml-1.3.0-py2.py3-none-any.whl", hash = "sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc"}, {file = "aspy.yaml-1.3.0.tar.gz", hash = "sha256:e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45"}, ] atomicwrites = [ {file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"}, {file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"}, ] attrs = [ {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, ] black = [ {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, ] cfgv = [ {file = "cfgv-3.0.0-py2.py3-none-any.whl", hash = "sha256:f22b426ed59cd2ab2b54ff96608d846c33dfb8766a67f0b4a6ce130ce244414f"}, {file = "cfgv-3.0.0.tar.gz", hash = "sha256:04b093b14ddf9fd4d17c53ebfd55582d27b76ed30050193c14e560770c5360eb"}, ] click = [ {file = "click-7.1.1-py2.py3-none-any.whl", hash = "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"}, {file = "click-7.1.1.tar.gz", hash = "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc"}, ] colorama = [ {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, ] coverage = [ {file = "coverage-5.0.4-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307"}, {file = "coverage-5.0.4-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8"}, {file = "coverage-5.0.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31"}, {file = "coverage-5.0.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441"}, {file = "coverage-5.0.4-cp27-cp27m-win32.whl", hash = "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac"}, {file = "coverage-5.0.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435"}, {file = "coverage-5.0.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037"}, {file = "coverage-5.0.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a"}, {file = "coverage-5.0.4-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5"}, {file = "coverage-5.0.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30"}, {file = "coverage-5.0.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7"}, {file = "coverage-5.0.4-cp35-cp35m-win32.whl", hash = "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de"}, {file = "coverage-5.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1"}, {file = "coverage-5.0.4-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1"}, {file = "coverage-5.0.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0"}, {file = "coverage-5.0.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd"}, {file = "coverage-5.0.4-cp36-cp36m-win32.whl", hash = "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0"}, {file = "coverage-5.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b"}, {file = "coverage-5.0.4-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78"}, {file = "coverage-5.0.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6"}, {file = "coverage-5.0.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014"}, {file = "coverage-5.0.4-cp37-cp37m-win32.whl", hash = "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732"}, {file = "coverage-5.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006"}, {file = "coverage-5.0.4-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2"}, {file = "coverage-5.0.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe"}, {file = "coverage-5.0.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9"}, {file = "coverage-5.0.4-cp38-cp38-win32.whl", hash = "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1"}, {file = "coverage-5.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0"}, {file = "coverage-5.0.4-cp39-cp39-win32.whl", hash = "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7"}, {file = "coverage-5.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892"}, {file = "coverage-5.0.4.tar.gz", hash = "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823"}, ] distlib = [ {file = "distlib-0.3.0.zip", hash = "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"}, ] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, ] identify = [ {file = "identify-1.4.13-py2.py3-none-any.whl", hash = "sha256:a7577a1f55cee1d21953a5cf11a3c839ab87f5ef909a4cba6cf52ed72b4c6059"}, {file = "identify-1.4.13.tar.gz", hash = "sha256:ab246293e6585a1c6361a505b68d5b501a0409310932b7de2c2ead667b564d89"}, ] importlib-metadata = [ {file = "importlib_metadata-1.6.0-py2.py3-none-any.whl", hash = "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f"}, {file = "importlib_metadata-1.6.0.tar.gz", hash = "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"}, ] importlib-resources = [ {file = "importlib_resources-1.4.0-py2.py3-none-any.whl", hash = "sha256:dd98ceeef3f5ad2ef4cc287b8586da4ebad15877f351e9688987ad663a0a29b8"}, {file = "importlib_resources-1.4.0.tar.gz", hash = "sha256:4019b6a9082d8ada9def02bece4a76b131518866790d58fdda0b5f8c603b36c2"}, ] more-itertools = [ {file = "more-itertools-8.2.0.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"}, {file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"}, ] nodeenv = [ {file = "nodeenv-1.3.5-py2.py3-none-any.whl", hash = "sha256:5b2438f2e42af54ca968dd1b374d14a1194848955187b0e5e4be1f73813a5212"}, ] packaging = [ {file = "packaging-20.3-py2.py3-none-any.whl", hash = "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"}, {file = "packaging-20.3.tar.gz", hash = "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3"}, ] pathspec = [ {file = "pathspec-0.7.0-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"}, {file = "pathspec-0.7.0.tar.gz", hash = "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"}, ] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] pre-commit = [ {file = "pre_commit-1.21.0-py2.py3-none-any.whl", hash = "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029"}, {file = "pre_commit-1.21.0.tar.gz", hash = "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850"}, ] py = [ {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, ] pyparsing = [ {file = "pyparsing-2.4.6-py2.py3-none-any.whl", hash = "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"}, {file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"}, ] pytest = [ {file = "pytest-5.4.1-py3-none-any.whl", hash = "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172"}, {file = "pytest-5.4.1.tar.gz", hash = "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970"}, ] pytest-cov = [ {file = "pytest-cov-2.8.1.tar.gz", hash = "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b"}, {file = "pytest_cov-2.8.1-py2.py3-none-any.whl", hash = "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"}, ] pyyaml = [ {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ {file = "regex-2020.2.20-cp27-cp27m-win32.whl", hash = "sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb"}, {file = "regex-2020.2.20-cp27-cp27m-win_amd64.whl", hash = "sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74"}, {file = "regex-2020.2.20-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400"}, {file = "regex-2020.2.20-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0"}, {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc"}, {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"}, {file = "regex-2020.2.20-cp36-cp36m-win32.whl", hash = "sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69"}, {file = "regex-2020.2.20-cp36-cp36m-win_amd64.whl", hash = "sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b"}, {file = "regex-2020.2.20-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e"}, {file = "regex-2020.2.20-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242"}, {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce"}, {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab"}, {file = "regex-2020.2.20-cp37-cp37m-win32.whl", hash = "sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431"}, {file = "regex-2020.2.20-cp37-cp37m-win_amd64.whl", hash = "sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1"}, {file = "regex-2020.2.20-cp38-cp38-manylinux1_i686.whl", hash = "sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045"}, {file = "regex-2020.2.20-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26"}, {file = "regex-2020.2.20-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2"}, {file = "regex-2020.2.20-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70"}, {file = "regex-2020.2.20-cp38-cp38-win32.whl", hash = "sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d"}, {file = "regex-2020.2.20-cp38-cp38-win_amd64.whl", hash = "sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa"}, {file = "regex-2020.2.20.tar.gz", hash = "sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5"}, ] six = [ {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, ] toml = [ {file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"}, {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"}, {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, ] typed-ast = [ {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] virtualenv = [ {file = "virtualenv-20.0.15-py2.py3-none-any.whl", hash = "sha256:4e399f48c6b71228bf79f5febd27e3bbb753d9d5905776a86667bc61ab628a25"}, {file = "virtualenv-20.0.15.tar.gz", hash = "sha256:9e81279f4a9d16d1c0654a127c2c86e5bca2073585341691882c1e66e31ef8a5"}, ] wcwidth = [ {file = "wcwidth-0.1.9-py2.py3-none-any.whl", hash = "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1"}, {file = "wcwidth-0.1.9.tar.gz", hash = "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"}, ] zipp = [ {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, ] crashtest-0.3.1/pyproject.toml000066400000000000000000000015561371101575400164420ustar00rootroot00000000000000[tool.poetry] name = "crashtest" version = "0.3.1" description = "Manage Python errors with ease" authors = ["Sébastien Eustace "] license = "MIT" readme = "README.md" homepage = "https://github.com/sdispater/crashtest" repository = "https://github.com/sdispater/crashtest" [tool.poetry.dependencies] python = "^3.6" [tool.poetry.dev-dependencies] pytest = "^5.4.1" black = "^19.10b0" pytest-cov = "^2.8.1" pre-commit = "^1.20.0" [tool.isort] line_length = 88 force_single_line = true atomic = true include_trailing_comma = true lines_after_imports = 2 lines_between_types = 1 multi_line_output = 3 use_parentheses = true not_skip = "__init__.py" skip_glob = ["*/setup.py"] filter_files = true known_first_party = "crashtest" known_third_party = ["pytest"] [build-system] requires = ["poetry-core>=1.0.0a5"] build-backend = "poetry.core.masonry.api" crashtest-0.3.1/tests/000077500000000000000000000000001371101575400146615ustar00rootroot00000000000000crashtest-0.3.1/tests/__init__.py000066400000000000000000000000001371101575400167600ustar00rootroot00000000000000crashtest-0.3.1/tests/helpers.py000066400000000000000000000004621371101575400166770ustar00rootroot00000000000000def simple_exception(): raise ValueError("Simple Exception") def nested_exception(): try: simple_exception() except ValueError: raise RuntimeError("Nested Exception") def recursive_exception(): def inner(): outer() def outer(): inner() inner() crashtest-0.3.1/tests/solution_providers/000077500000000000000000000000001371101575400206325ustar00rootroot00000000000000crashtest-0.3.1/tests/solution_providers/__init__.py000066400000000000000000000000001371101575400227310ustar00rootroot00000000000000crashtest-0.3.1/tests/solution_providers/test_solution_provider_repository.py000066400000000000000000000052601371101575400303330ustar00rootroot00000000000000from typing import List from crashtest.contracts.base_solution import BaseSolution from crashtest.contracts.has_solutions_for_exception import HasSolutionsForException from crashtest.contracts.provides_solution import ProvidesSolution from crashtest.contracts.solution import Solution from crashtest.solution_providers.solution_provider_repository import ( SolutionProviderRepository, ) class ExceptionSolutionProvider(HasSolutionsForException): def can_solve(self, exception: Exception) -> bool: return isinstance(exception, ExceptionProvidingException) def get_solutions(self, exception: Exception) -> List[Solution]: return [ BaseSolution("An exception solution", "An exception solution description") ] class ExceptionProvidingException(Exception, ProvidesSolution): @property def solution(self) -> Solution: solution = BaseSolution("A simple solution", "A simple solution description") solution.documentation_links.append("https://example.com") return solution class ExceptionWhichIsSolution(Exception, Solution): @property def solution_title(self) -> str: return "A solution" @property def solution_description(self) -> str: return "A solution description" @property def documentation_links(self) -> List[str]: return ["https://foo.bar"] def test_it_has_no_provider_by_default(): repository = SolutionProviderRepository() assert 0 == len(repository._solution_providers) def test_providers_can_be_passed_to_constructor(): repository = SolutionProviderRepository() assert 0 == len(repository._solution_providers) def test_it_can_find_solutions(): repository = SolutionProviderRepository() repository.register_solution_provider(ExceptionSolutionProvider) solutions = repository.get_solutions_for_exception(ExceptionProvidingException()) assert 2 == len(solutions) solution1 = solutions[0] solution2 = solutions[1] assert "A simple solution" == solution1.solution_title assert "An exception solution" == solution2.solution_title assert "A simple solution description" == solution1.solution_description assert "An exception solution description" == solution2.solution_description assert ["https://example.com"] == solution1.documentation_links assert 0 == len(solution2.documentation_links) solutions = repository.get_solutions_for_exception(ExceptionWhichIsSolution()) assert 1 == len(solutions) solution1 = solutions[0] assert "A solution" == solution1.solution_title assert "A solution description" == solution1.solution_description assert ["https://foo.bar"] == solution1.documentation_links crashtest-0.3.1/tests/test_frame.py000066400000000000000000000022551371101575400173700ustar00rootroot00000000000000import inspect from crashtest.frame import Frame from .helpers import nested_exception from .helpers import simple_exception def test_frame(): try: simple_exception() except ValueError as e: frame_info = inspect.getinnerframes(e.__traceback__)[0] frame = Frame(frame_info) same_frame = Frame(frame_info) assert frame_info.frame == frame.frame assert 11 == frame.lineno assert __file__ == frame.filename assert "test_frame" == frame.function assert " simple_exception()\n" == frame.line with open(__file__) as f: assert f.read() == frame.file_content assert f"" == repr(frame) try: nested_exception() except Exception as e: frame_info = inspect.getinnerframes(e.__traceback__)[0] other_frame = Frame(frame_info) assert same_frame == frame assert other_frame != frame assert hash(same_frame) == hash(frame) assert hash(other_frame) != hash(frame) def test_frame_with_no_context_should_return_empty_line(): frame = Frame(inspect.FrameInfo(None, "filename.py", 123, "function", None, 3)) assert "" == frame.line crashtest-0.3.1/tests/test_inspector.py000066400000000000000000000032071371101575400203020ustar00rootroot00000000000000from crashtest.inspector import Inspector from .helpers import nested_exception from .helpers import recursive_exception from .helpers import simple_exception def test_inspector_with_simple_exception(): try: simple_exception() except ValueError as e: inspector = Inspector(e) assert e == inspector.exception assert not inspector.has_previous_exception() assert inspector.previous_exception is None assert "ValueError" == inspector.exception_name assert "Simple Exception" == inspector.exception_message assert len(inspector.frames) > 0 def test_inspector_with_nested_exception(): try: nested_exception() except RuntimeError as e: inspector = Inspector(e) assert e == inspector.exception assert inspector.has_previous_exception() assert inspector.previous_exception is not None assert "RuntimeError" == inspector.exception_name assert "Nested Exception" == inspector.exception_message assert len(inspector.frames) > 0 assert 1 == len(inspector.frames.compact()) def test_inspector_with_recursive_exception(): try: recursive_exception() except RuntimeError as e: inspector = Inspector(e) assert e == inspector.exception assert not inspector.has_previous_exception() assert inspector.previous_exception is None assert "RecursionError" == inspector.exception_name assert "maximum recursion depth exceeded" == inspector.exception_message assert len(inspector.frames) > 0 assert len(inspector.frames) > len(inspector.frames.compact())