Design by contract for Python. Write bug-free code. Add a few decorators, get static analysis and tests for free.

Overview

Deal

Build Status PyPI version Development Status

A Python library for design by contract (DbC) and checking values, exceptions, and side-effects. In a nutshell, deal empowers you to write bug-free code. By adding a few decorators to your code, you get for free tests, static analysis, formal verification, and much more. Read intro to get started.

Features

Deal in 30 seconds

# the result is always non-negative
@deal.post(lambda result: result >= 0)
# the function has no side-effects
@deal.pure
def count(items: List[str], item: str) -> int:
    return items.count(item)

# generate test function
test_count = deal.cases(count)

Now we can:

  • Run python3 -m deal lint or flake8 to statically check errors.
  • Run python3 -m deal test or pytest to generate and run tests.
  • Just use the function in the project and check errors in runtime.

Read more in the documentation.

Installation

python3 -m pip install --user deal

Contributing

Contributions are welcome! A few ideas what you can contribute:

  • Add new checks for the linter.
  • Improve documentation.
  • Add more tests.
  • Improve performance.
  • Found a bug? Fix it!
  • Made an article about deal? Great! Let's add it into the README.md.
  • Don't have time to code? No worries! Just tell your friends and subscribers about the project. More users -> more contributors -> more cool features.

Thank you ❤️

Comments
  • Check for _.result in 'ensure' contract linting

    Check for _.result in 'ensure' contract linting

    Previously, using _ in a @deal.ensure contract validator would lead to this error:

    $ python3 -m deal lint
    dumbpw/pwgen.py
      32:4 DEAL002 ensure contract must have `result` arg
        lambda _: len(_.result) == _.length,
        ^
    

    This change fixes that false positive by detecting when the arg name is _ and passing the lint check if _.result is anywhere in the validator body.

    opened by rpdelaney 10
  • Add special handling for missing deal_solver

    Add special handling for missing deal_solver

    Before this change, the prover attempts to handle the case where deal_solver cannot be imported by setting deal_solver = None. However, instantiation of the DealTheorem class a few lines later depends on deal_solver. This results in an unhandled exception because None does not have a "Theorem" attribute:

    $ python3 -m deal lint
    Traceback (most recent call last):
      File "/Users/ryan/.local/share/asdf/installs/python/3.10.2/lib/python3.10/runpy.py", line 196, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "/Users/ryan/.local/share/asdf/installs/python/3.10.2/lib/python3.10/runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "/Users/ryan/src/me/extinfo/.venv/lib/python3.10/site-packages/deal/__main__.py", line 6, in <module>
        sys.exit(main(sys.argv[1:]))
      File "/Users/ryan/src/me/extinfo/.venv/lib/python3.10/site-packages/deal/_cli/_main.py", line 37, in main
        commands = get_commands()
      File "/Users/ryan/src/me/extinfo/.venv/lib/python3.10/site-packages/deal/_cli/_main.py", line 16, in get_commands
        from ._prove import ProveCommand
      File "/Users/ryan/src/me/extinfo/.venv/lib/python3.10/site-packages/deal/_cli/_prove.py", line 26, in <module>
        class DealTheorem(deal_solver.Theorem):
    AttributeError: 'NoneType' object has no attribute 'Theorem'
    

    After this change, a special exception is raised to get_commands() so that when deal_solver import fails, the ProveCommand can be set to an empty Command (because the prover cannot run at all without the solver).

    opened by rpdelaney 6
  • Fix incompatible type in raises(SystemExit)

    Fix incompatible type in raises(SystemExit)

    The deal linter decorates functions that call sys.exit() with @deal.raises(SystemExit). SystemExit inherits from BaseException, which makes it incompatible with Exception:

    dumbpw/cli.py:14:14: error: Argument 1 to "raises" has incompatible type
    "Type[SystemExit]"; expected "Type[Exception]"  [arg-type]
        @deal.raises(SystemExit)
                     ^
    Found 1 error in 1 file (checked 14 source files)
    

    This change annotates the raises() decorator to expect BaseException so that SystemExit can be included without an incompatible type error from the type checker.

    opened by rpdelaney 5
  • Update stubs.md

    Update stubs.md

    It would be helpful for python3 -m deal stub /path/to/a/file.py to detect whether file.py already has deal annotations and generate itself based on them. This could be a neat way to separate deal contracts into their own file rather than intrude on the code, sort of like it's possible to do with type hints.

    opened by Ayenem 3
  • Make vaa optional

    Make vaa optional

    Most of vaa usage is for short signature. I doubt anyone really uses schemas. So, let's reimplement simple signature logic on deal side and make vaa optional.

    opened by orsinium 1
  • Don't call typeguard if it's not available

    Don't call typeguard if it's not available

    Attempting to run the "deal in 30s" example:

    # the result is always non-negative
    @deal.post(lambda result: result >= 0)
    # the function has no side-effects
    @deal.pure
    def count(items: List[str], item: str) -> int:
        return items.count(item)
    
    # generate test function
    test_count = deal.cases(count)
    

    fails with the following error run under pytest:

    [nix-shell:~/code/nixpkgs]$ pytest deal_test.py
    ======================================================== test session starts ========================================================
    platform darwin -- Python 3.9.11, pytest-7.0.1, pluggy-1.0.0
    rootdir: /Users/panashe/code/nixpkgs
    plugins: hypothesis-6.38.0
    collected 1 item
    
    deal_test.py F                                                                                                                [100%]
    
    ============================================================= FAILURES ==============================================================
    ____________________________________________________________ test_count _____________________________________________________________
    
    >   ???
    
    /nix/store/3mnlyndr9s9r0v49ynahldczkawqa076-python3.9-deal-4.21.1/lib/python3.9/site-packages/deal/_testing.py:329:
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    /nix/store/3mnlyndr9s9r0v49ynahldczkawqa076-python3.9-deal-4.21.1/lib/python3.9/site-packages/deal/_testing.py:328: in <lambda>
        return self._wrap(lambda case: case())
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    
    self = TestCase(args=(), kwargs={'items': [], 'item': ''}, func=<function count at 0x10fe74af0>, exceptions=(), check_types=True)
    result = 0
    
        def _check_result(self, result: typing.Any) -> None:
            if not self.check_types:
                return
    >       memo = typeguard._CallMemo(
                func=self.func,
                args=self.args,
                kwargs=self.kwargs,
            )
    E       AttributeError: 'NoneType' object has no attribute '_CallMemo'
    
    /nix/store/3mnlyndr9s9r0v49ynahldczkawqa076-python3.9-deal-4.21.1/lib/python3.9/site-packages/deal/_testing.py:68: AttributeError
    ------------------------------------------------------- Captured stdout call --------------------------------------------------------
    
    You can reproduce this example by temporarily adding @reproduce_failure('6.38.0', b'AAAA') as a decorator on your test case
    ====================================================== short test summary info ======================================================
    FAILED deal_test.py::test_count - AttributeError: 'NoneType' object has no attribute '_CallMemo'
    ========================================================= 1 failed in 0.81s =========================================================
    

    This does not fail if you just call deal.cases(count)(). Looking into the failure, this is attempting to call typeguard when the dependency is not available. In the constructor, self.check_types defaults to True when not passed, but there is no explicit check that typeguard exists before using it, even though it should optional.

    opened by munyari 1
  • Create py.typed

    Create py.typed

    Related #44

    That's the first step to get the typing support. The next step is to run mypy on the source code.

    And probably fix some problems, annotate some missing functions, etc.

    opened by sobolevn 1
Releases(4.23.4)
  • 4.23.4(Sep 1, 2022)

    What's Changed

    • Integration test for flake8 by @orsinium in https://github.com/life4/deal/pull/120
    • fix(flake8): Flake8 does not support 4 letter codes anymore. by @ruler501 in https://github.com/life4/deal/pull/119
    • Detect noqa comments by @orsinium in https://github.com/life4/deal/pull/122

    New Contributors

    • @ruler501 made their first contribution in https://github.com/life4/deal/pull/119

    Full Changelog: https://github.com/life4/deal/compare/4.23.3...4.23.4

    Source code(tar.gz)
    Source code(zip)
  • 4.23.3(May 2, 2022)

    What's Changed

    • linter: detect self even when it is a posonlyarg by @orsinium in https://github.com/life4/deal/pull/116
    • Make vaa optional by @orsinium in https://github.com/life4/deal/pull/117

    Full Changelog: https://github.com/life4/deal/compare/4.23.2...4.23.3

    Source code(tar.gz)
    Source code(zip)
  • 4.23.2(Apr 21, 2022)

    What's Changed

    • Add explicit docs for @deal.safe by @rpdelaney in https://github.com/life4/deal/pull/113
    • linter: detect keyword validator by @orsinium in https://github.com/life4/deal/pull/115
    • Fix incompatible type in raises(SystemExit) by @rpdelaney in https://github.com/life4/deal/pull/114

    Full Changelog: https://github.com/life4/deal/compare/4.23.1...4.23.2

    Source code(tar.gz)
    Source code(zip)
  • 4.23.1(Apr 12, 2022)

    What's Changed

    • Lazy annotations by @orsinium in https://github.com/life4/deal/pull/110
    • Enable Python 3.10 pytest on CI by @orsinium in https://github.com/life4/deal/pull/111
    • Improve import time by @orsinium in https://github.com/life4/deal/pull/112

    Full Changelog: https://github.com/life4/deal/compare/4.23.0...4.23.1

    Source code(tar.gz)
    Source code(zip)
  • 4.23.0(Apr 12, 2022)

    What's Changed

    • Support deal.pure in code generator by @orsinium in https://github.com/life4/deal/pull/109

    Full Changelog: https://github.com/life4/deal/compare/4.22.0...4.23.0

    Source code(tar.gz)
    Source code(zip)
  • 4.22.0(Apr 9, 2022)

    I accidentally released it as 4.21.2 but then realized that it has a feature included, not only a bug fix. So, now you have two releases with the same changes inside.

    What's Changed

    • Don't call typeguard if it's not available by @munyari in https://github.com/life4/deal/pull/108
    • Allow permanently disabling contracts by @orsinium in https://github.com/life4/deal/pull/107

    New Contributors

    • @munyari made their first contribution in https://github.com/life4/deal/pull/108

    Full Changelog: https://github.com/life4/deal/compare/4.21.1...4.21.2

    Source code(tar.gz)
    Source code(zip)
  • 4.21.1(Mar 18, 2022)

    What's Changed

    • Add special handling for missing deal_solver by @rpdelaney in https://github.com/life4/deal/pull/106

    Full Changelog: https://github.com/life4/deal/compare/4.21.0...4.21.1

    Source code(tar.gz)
    Source code(zip)
  • 4.21.0(Mar 18, 2022)

    What's Changed

    • Correct variable reference in code sample by @jgberry in https://github.com/life4/deal/pull/104
    • Linter: extract exceptions from docstrings by @orsinium in https://github.com/life4/deal/pull/105

    New Contributors

    • @jgberry made their first contribution in https://github.com/life4/deal/pull/104

    Full Changelog: https://github.com/life4/deal/compare/4.20.0...4.21.0

    Source code(tar.gz)
    Source code(zip)
  • 4.20.0(Mar 18, 2022)

    What's Changed

    • Make some dependencies optional by @orsinium in https://github.com/life4/deal/pull/103

    Full Changelog: https://github.com/life4/deal/compare/4.19.2...4.20.0

    Source code(tar.gz)
    Source code(zip)
  • 4.19.2(Mar 18, 2022)

    What's Changed

    • Improve linter behavior for assert by @orsinium in https://github.com/life4/deal/pull/102

    Full Changelog: https://github.com/life4/deal/compare/4.19.1...4.19.2

    Source code(tar.gz)
    Source code(zip)
  • 4.19.1(Dec 30, 2021)

    What's Changed

    • Add some more copyedits to docs by @rpdelaney in https://github.com/life4/deal/pull/100
    • Check for _.result in 'ensure' contract linting by @rpdelaney in https://github.com/life4/deal/pull/101

    Full Changelog: https://github.com/life4/deal/compare/4.19.0...4.19.1

    Source code(tar.gz)
    Source code(zip)
  • 4.19.0(Dec 3, 2021)

    What's Changed

    • improve wording and fix typos in README by @jacobszpz in https://github.com/life4/deal/pull/98
    • Copyedits to docs by @rpdelaney in https://github.com/life4/deal/pull/99
    • Lint methods by @orsinium in https://github.com/life4/deal/pull/97

    New Contributors

    • @jacobszpz made their first contribution in https://github.com/life4/deal/pull/98
    • @rpdelaney made their first contribution in https://github.com/life4/deal/pull/99

    Full Changelog: https://github.com/life4/deal/compare/4.18.0...4.19.0

    Source code(tar.gz)
    Source code(zip)
  • 4.18.0(Nov 18, 2021)

    What's Changed

    • Code generation (python3 -m deal decorate CLI command) by @orsinium in https://github.com/life4/deal/pull/96

    Full Changelog: https://github.com/life4/deal/compare/4.17.0...4.18.0

    Source code(tar.gz)
    Source code(zip)
  • 4.17.0(Nov 10, 2021)

    What's Changed

    • Linter: support deal.inherit for methods by @orsinium in https://github.com/life4/deal/pull/95
    • Document CrossHair integration by @orsinium in https://github.com/life4/deal/pull/94

    Full Changelog: https://github.com/life4/deal/compare/4.16.0...4.17.0

    Source code(tar.gz)
    Source code(zip)
  • 4.16.0(Nov 5, 2021)

    What's Changed

    • deal.inherit by @orsinium in https://github.com/life4/deal/pull/93
    • Enable contracts when running @deal.dispatch by @orsinium in https://github.com/life4/deal/pull/92

    Full Changelog: https://github.com/life4/deal/compare/4.15.0...4.16.0

    Source code(tar.gz)
    Source code(zip)
  • 4.15.0(Oct 18, 2021)

    What's Changed

    • Better AST traversing by @orsinium in https://github.com/life4/deal/pull/89
    • Linter: require deal.ensure to have result arg by @orsinium in https://github.com/life4/deal/pull/90
    • deal.dispatch: propagate PreContractError by @orsinium in https://github.com/life4/deal/pull/91

    Full Changelog: https://github.com/life4/deal/compare/4.14.0...4.15.0

    Source code(tar.gz)
    Source code(zip)
  • 4.14.0(Oct 18, 2021)

    What's Changed

    • linter: more markers for deal.has by @orsinium in https://github.com/life4/deal/pull/88

    Full Changelog: https://github.com/life4/deal/compare/4.13.0...4.14.0

    Source code(tar.gz)
    Source code(zip)
  • 4.13.0(Oct 18, 2021)

    What's Changed

    • Rewrite runtime by @orsinium in https://github.com/life4/deal/pull/87

    Full Changelog: https://github.com/life4/deal/compare/4.12.0...4.13.0

    Source code(tar.gz)
    Source code(zip)
  • 4.12.0(Oct 18, 2021)

    What's Changed

    • @deal.example by @orsinium in https://github.com/life4/deal/pull/86

    Full Changelog: https://github.com/life4/deal/compare/4.11.0...4.12.0

    Source code(tar.gz)
    Source code(zip)
  • 4.11.0(Sep 27, 2021)

    What's Changed

    • Migrate from recommonmark to myst-parser by @orsinium in https://github.com/life4/deal/pull/84
    • MyPy plugin by @orsinium in https://github.com/life4/deal/pull/79
    • Much better performance for deal.inv by @orsinium in https://github.com/life4/deal/pull/85

    Full Changelog: https://github.com/life4/deal/compare/4.10.0...4.11.0

    Source code(tar.gz)
    Source code(zip)
  • 4.10.0(Sep 24, 2021)

  • 4.9.0(Sep 23, 2021)

  • 4.8.0(Sep 20, 2021)

  • 4.7.2(Jul 11, 2021)

  • 4.7.1(Jul 11, 2021)

  • 4.7.0(Jul 8, 2021)

Owner
Life4
Original cool Open Source projects
Life4
A static type analyzer for Python code

pytype - ?? ✔ Pytype checks and infers types for your Python code - without requiring type annotations. Pytype can: Lint plain Python code, flagging c

Google 4k Dec 31, 2022
Optional static typing for Python 3 and 2 (PEP 484)

Mypy: Optional Static Typing for Python Got a question? Join us on Gitter! We don't have a mailing list; but we are always happy to answer questions o

Python 14.4k Jan 8, 2023
Static type checker for Python

Static type checker for Python Speed Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fas

Microsoft 9.2k Jan 3, 2023
❄️ A flake8 plugin to help you write better list/set/dict comprehensions.

flake8-comprehensions A flake8 plugin that helps you write better list/set/dict comprehensions. Requirements Python 3.6 to 3.9 supported. Installation

Adam Johnson 398 Dec 23, 2022
A plugin for Flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle.

flake8-bugbear A plugin for Flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycode

Python Code Quality Authority 869 Dec 30, 2022
Pylint plugin for improving code analysis for when using Django

pylint-django About pylint-django is a Pylint plugin for improving code analysis when analysing code using Django. It is also used by the Prospector t

Python Code Quality Authority 544 Jan 6, 2023
Code audit tool for python.

Pylama Code audit tool for Python and JavaScript. Pylama wraps these tools: pycodestyle (formerly pep8) © 2012-2013, Florent Xicluna; pydocstyle (form

Kirill Klenov 967 Jan 7, 2023
coala provides a unified command-line interface for linting and fixing all your code, regardless of the programming languages you use.

"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." ― John F. Woods coala provides a

coala development group 3.4k Dec 29, 2022
Utilities for pycharm code formatting (flake8 and black)

Pycharm External Tools Extentions to Pycharm code formatting tools. Currently supported are flake8 and black on a selected code block. Usage Flake8 [P

Haim Daniel 13 Nov 3, 2022
OpenStack Hacking Style Checks. Mirror of code maintained at opendev.org.

Introduction hacking is a set of flake8 plugins that test and enforce the OpenStack StyleGuide Hacking pins its dependencies, as a new release of some

Mirrors of opendev.org/openstack 224 Jan 5, 2023
Flake8 plugin to find commented out or dead code

flake8-eradicate flake8 plugin to find commented out (or so called "dead") code. This is quite important for the project in a long run. Based on eradi

wemake.services 277 Dec 27, 2022
A plugin for Flake8 that checks pandas code

pandas-vet pandas-vet is a plugin for flake8 that provides opinionated linting for pandas code. It began as a project during the PyCascades 2019 sprin

Jacob Deppen 146 Dec 28, 2022
:sparkles: Surface lint errors during code review

✨ Linty Fresh ✨ Keep your codebase sparkly clean with the power of LINT! Linty Fresh parses lint errors and report them back to GitHub as comments on

Lyft 183 Dec 18, 2022
Simple Python style checker in one Python file

pycodestyle (formerly called pep8) - Python style guide checker pycodestyle is a tool to check your Python code against some of the style conventions

Python Code Quality Authority 4.7k Jan 1, 2023
The strictest and most opinionated python linter ever!

wemake-python-styleguide Welcome to the strictest and most opinionated python linter ever. wemake-python-styleguide is actually a flake8 plugin with s

wemake.services 2.1k Jan 1, 2023
A Python Parser

parso - A Python Parser Parso is a Python parser that supports error recovery and round-trip parsing for different Python versions (in multiple Python

Dave Halter 520 Dec 26, 2022
A simple program which checks Python source files for errors

Pyflakes A simple program which checks Python source files for errors. Pyflakes analyzes programs and detects various errors. It works by parsing the

Python Code Quality Authority 1.2k Dec 30, 2022
Performant type-checking for python.

Pyre is a performant type checker for Python compliant with PEP 484. Pyre can analyze codebases with millions of lines of code incrementally – providi

Facebook 6.2k Jan 4, 2023
Tool to check the completeness of MANIFEST.in for Python packages

check-manifest Are you a Python developer? Have you uploaded packages to the Python Package Index? Have you accidentally uploaded broken packages with

Marius Gedminas 270 Dec 26, 2022