πŸ‘» Phantom types for Python

Overview

Depiction of phantom types in the wild

phantom-types

CI Build Status Documentation Build Status Test coverage report

Phantom types for Python will help you make illegal states unrepresentable and avoid shotgun parsing by enabling you to practice "Parse, don't validate".

This project is in early development and fundamental changes should be expected. Semantic versioning will be followed after version 1.0, but before that breaking changes might occur between minor versions.

Checkout the complete documentation on Read the Docs β†’

Installation

$  python3 -m pip install phantom-types

Examples

By introducing a phantom type we can define a pre-condition for a function argument.

from phantom import Phantom
from phantom.predicates.collection import contained


class Name(str, Phantom, predicate=contained({"Jane", "Joe"})):
    ...


def greet(name: Name):
    print(f"Hello {name}!")

Now this will be a valid call.

greet(Name.parse("Jane"))

... and so will this.

joe = "Joe"
assert isinstance(joe, Name)
greet(joe)

But this will yield a static type checking error.

greet("bird")

Runtime type checking

By combining phantom types with a runtime type-checker like beartype or typeguard, we can achieve the same level of security as you'd gain from using contracts.

import datetime
from beartype import beartype
from phantom.datetime import TZAware


@beartype
def soon(dt: TZAware) -> TZAware:
    return dt + datetime.timedelta(seconds=10)

The soon function will now validate that both its argument and return value is timezone aware, e.g. pre- and post conditions.

Pydantic support

Phantom types are ready to use with pydantic and have integrated support out-of-the-box. Subclasses of Phantom work with both pydantic's validation and its schema generation.

class Name(str, Phantom, predicate=contained({"Jane", "Joe"})):
    @classmethod
    def __schema__(cls) -> Schema:
        return super().__schema__() | {
            "description": "Either Jane or Joe",
            "format": "custom-name",
        }


class Person(BaseModel):
    name: Name
    created: TZAware


print(json.dumps(Person.schema(), indent=2))

The code above outputs the following JSONSchema.

{
  "title": "Person",
  "type": "object",
  "properties": {
    "name": {
      "title": "Name",
      "description": "Either Jane or Joe",
      "format": "custom-name",
      "type": "string"
    },
    "created": {
      "title": "TZAware",
      "description": "A date-time with timezone data.",
      "type": "string",
      "format": "date-time"
    }
  },
  "required": ["name", "created"]
}

Development

Install development requirements, preferably in a virtualenv:

$ python3 -m pip install .[test,pydantic,phonenumbers]

Run tests:

$ pytest
# or
$ make test

Linting and static type checking is setup with pre-commit, after installing it you can setup hooks with the following command, so that checks run before you push changes.

# configure hooks to run when pushing
$ pre-commit install -t pre-push
# or when committing
$ pre-commit install -t pre-commit
# run all checks
$ pre-commit run --all-files
# or just a single hook
$ pre-commit run mypy --all-files

In addition to static type checking, the project is setup with pytest-mypy-plugins to test that exposed mypy types work as expected, these checks will run together with the rest of the test suite, but you can single them out with the following command.

$ make test-typing
Comments
  • Reintroduce support for Python 3.7

    Reintroduce support for Python 3.7

    Hi! A quick question:

    Why have you dropped python3.7 support in this commit https://github.com/antonagestam/phantom-types/commit/c0817edd886237d288b277c93a89c04d286e54e1 ?

    Looks like python3.7 was supported not a long time ago: https://github.com/antonagestam/phantom-types/commit/c2e6d37a0afa2a9dd531df016414a12688341006

    From what I can see the only feature of python3.8 you are using is / arguments, which can be easily converted into regular arguments (which is even cleaner in my opinion). Anything else that I've missed?

    enhancement 
    opened by sobolevn 10
  • Fix Mypy errors in strict mode

    Fix Mypy errors in strict mode

    Python program in README.md:

    from phantom import Phantom
    from phantom.predicates.collection import contained
    
    
    class Name(str, Phantom, predicate=contained({"Jane", "Joe"})):
        ...
    
    
    def greet(name: Name):
        print(f"Hello {name}!")
    
    
    greet(Name.parse("Jane"))
    
    joe = "Joe"
    assert isinstance(joe, Name)
    greet(joe)
    
    greet("bird")
    

    Mypy output:

    $ mypy foobar.py --strict                
    foobar.py:5: error: Missing type parameters for generic type "Phantom"
    foobar.py:9: error: Function is missing a return type annotation
    foobar.py:18: error: Argument 1 to "greet" has incompatible type "str"; expected "Name"
    Found 3 errors in 1 file (checked 1 source file)
    
    opened by maggyero 6
  • Improves docs a bit

    Improves docs a bit

    1. Adds links to types that we are talking about
    2. Adds .. code-block:: python in all places, otherwise this code was not highlighted on github

    I really hope that RTD will build my PR to test it out, I am too lazy to test it locally πŸ˜†

    opened by sobolevn 5
  • `mypy` crash due to `CachingProtocolMeta`

    `mypy` crash due to `CachingProtocolMeta`

    I'm suddenly getting this on cached mypy runs ...

    Traceback (most recent call last):
      File "/app/venv/bin/mypy", line 8, in <module>
        sys.exit(console_entry())
      File "/app/venv/lib/python3.10/site-packages/mypy/__main__.py", line 15, in console_entry
        main()
      File "/app/venv/lib/python3.10/site-packages/mypy/main.py", line 95, in main
        res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
      File "/app/venv/lib/python3.10/site-packages/mypy/main.py", line 174, in run_build
        res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
      File "/app/venv/lib/python3.10/site-packages/mypy/build.py", line 193, in build
        result = _build(
      File "/app/venv/lib/python3.10/site-packages/mypy/build.py", line 276, in _build
        graph = dispatch(sources, manager, stdout)
      File "/app/venv/lib/python3.10/site-packages/mypy/build.py", line 2903, in dispatch
        process_graph(graph, manager)
      File "/app/venv/lib/python3.10/site-packages/mypy/build.py", line 3280, in process_graph
        process_fresh_modules(graph, prev_scc, manager)
      File "/app/venv/lib/python3.10/site-packages/mypy/build.py", line 3361, in process_fresh_modules
        graph[id].fix_cross_refs()
      File "/app/venv/lib/python3.10/site-packages/mypy/build.py", line 2112, in fix_cross_refs
        fixup_module(self.tree, self.manager.modules, self.options.use_fine_grained_cache)
      File "/app/venv/lib/python3.10/site-packages/mypy/fixup.py", line 53, in fixup_module
        node_fixer.visit_symbol_table(tree.names, tree.fullname)
      File "/app/venv/lib/python3.10/site-packages/mypy/fixup.py", line 125, in visit_symbol_table
        self.visit_type_info(value.node)
      File "/app/venv/lib/python3.10/site-packages/mypy/fixup.py", line 87, in visit_type_info
        info.declared_metaclass.accept(self.type_fixer)
      File "/app/venv/lib/python3.10/site-packages/mypy/types.py", line 1283, in accept
        return visitor.visit_instance(self)
      File "/app/venv/lib/python3.10/site-packages/mypy/fixup.py", line 196, in visit_instance
        inst.type = lookup_fully_qualified_typeinfo(
      File "/app/venv/lib/python3.10/site-packages/mypy/fixup.py", line 337, in lookup_fully_qualified_typeinfo
        assert (
    AssertionError: Should never get here in normal mode, got Var:phantom._utils.types.CachingProtocolMeta instead of TypeInfo
    

    Seems to be an issue with CachingProtocolMeta meta class being exposed as Any when looking at the mypy cache ...

    {
        "CachingProtocolMeta": {
            ".class": "SymbolTableNode",
            "kind": "Gdef",
            "module_hidden": true,
            "module_public": false,
            "node": {
                ".class": "Var",
                "flags": [
                    "is_suppressed_import",
                    "is_ready",
                    "is_inferred"
                ],
                "fullname": "phantom._utils.types.CachingProtocolMeta",
                "name": "CachingProtocolMeta",
                "type": {
                    ".class": "AnyType",
                    "missing_import_name": "phantom._utils.types.CachingProtocolMeta",
                    "source_any": null,
                    "type_of_any": 3
                }
            }
        }
    }
    
    opened by lundberg 4
  • Intervals Closed/Open backwards??

    Intervals Closed/Open backwards??

    Hi,

    reading the docs and the code, I feel there's an inversion.

    def open(low: T, high: T) -> Predicate[SupportsLeGe[T]]:
        """
        Create a predicate that succeeds when its argument is in the range ``(low, high)``.
        """
    
        @bind_name(open, low, high)
        def check(value: SupportsLeGe[T]) -> bool:
            return low <= value <= high
    
        return check
    

    Here I would have expected return low < value < high

    https://en.wikipedia.org/wiki/Interval_(mathematics)#Terminology

    In an open interval, bounds are not included, while in a closed one, they are.

    Am I missing something? Am I reading the code wrong?

    bug 
    opened by g-as 4
  • `NonEmpty[str]` type cannot be used as sorting key

    `NonEmpty[str]` type cannot be used as sorting key

    Not sure if I'm using NonEmpty incorrectly here with str. But the code below does not typecheck, and I think it should? (Since the underlying data type is a string)

    from typing import NamedTuple
    from phantom.sized import NonEmpty
    
    
    class X(NamedTuple):
        value: NonEmpty[str]
    
    sorted(
        [X(value=NonEmpty[str].parse("first")), X(value=NonEmpty[str].parse("second"))],
        key=lambda x: x.value
    )
    

    Results in the following error

    10: error: Argument "key" to "sorted" has incompatible type "Callable[[X], NonEmpty[str]]"; expected "Callable[[X], Union[SupportsDunderLT, SupportsDunderGT]]"  [arg-type]
    
    10: error: Incompatible return value type (got "NonEmpty[str]", expected "Union[SupportsDunderLT, SupportsDunderGT]")  [return-value]
    
    opened by flaeppe 4
  • AttributeError: 'operator.attrgetter' object has no attribute '__qualname__'

    AttributeError: 'operator.attrgetter' object has no attribute '__qualname__'

    >>> compose2(print, attrgetter("a.b"))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/Users/anton/.pyenv/versions/gina-api/lib/python3.9/site-packages/phantom/fn.py", line 26, in compose2
        b_name = _name(b)
      File "/Users/anton/.pyenv/versions/gina-api/lib/python3.9/site-packages/phantom/fn.py", line 9, in _name
        return fn.__qualname__
    AttributeError: 'operator.attrgetter' object has no attribute '__qualname__'
    

    This should fall back to __name__ and when that doesn't exist either, should fall back to __str__().

    bug good first issue 
    opened by antonagestam 4
  • Integrate numerary to gain support for numeric tower

    Integrate numerary to gain support for numeric tower

    Closes #34.

    @posita Thanks for pointing me towards your library, it seems to work like a charm! πŸ™

    A question: is it correctly interpreted that the type argument to RealLike is whatever the type returns for round() and abs()? Does it make sense to use RealLike[IntegralLike[int]] like I've done?

    I ran into one issue in that I get metaclass conflicts when using RealLike as a bound for a python type, but it should be possible to resolve that (I think). It's been brought up here earlier that it could be nice to provide helper functions for resolving metaclass conflicts, I'm now leaning towards going in that direction.

    opened by antonagestam 3
  • Usage with other types with metaclasses / metaclass conflicts

    Usage with other types with metaclasses / metaclass conflicts

    While working on #123 and solving a metaclass conflict, I was wondering about potential usage of phantom together with django / sqlalchemy projects that make a heavy use of metaclasses.

    For example, this code will produce a metaclass conflict:

    from django.db import models
    
    class  User(models.Model):
        is_paid = models.BooleanField(default=False)
    
    def is_paid_user(value: object) -> bool:
        return isinstance(value, User) and value.is_paid
    
    class PaidUser(User, Phantom, predicate=is_paid_user):
        ...  # metaclass conflict
    

    Is there any simple way to support this? We can probably ship some util metaclass solver like the one from here: https://stackoverflow.com/a/41266737/4842742

    Or at least we can document how to solve this.

    documentation help wanted 
    opened by sobolevn 3
  • `UK` not accepted as a `CountryCode`

    `UK` not accepted as a `CountryCode`

    I know the official alpha-2 code is GB and UK is part of the "exceptional reservations" list of iso3166-1 alpha-2, but frankly, all addresses in Great Britain have a post code of UK.

    I think it could be useful to include UK as well, in cases where one wants to use e.g. country: CountryCode for address validation or the like.

    enhancement 
    opened by flaeppe 2
  • Trying to set up local development, but can't run YAML tests

    Trying to set up local development, but can't run YAML tests

    Hi! I heard about this library from typing-sig and it looks really interesting. I tried setting up local development and ran into a couple issues.

    1. When I run pre-commit run --all, there's a flake8 error:
    (env) ➜  phantom-types git:(main) βœ— pre-commit run --all
    Check for case conflicts.................................................Passed
    Check for merge conflicts................................................Passed
    Fix End of Files.........................................................Passed
    Trim Trailing Whitespace.................................................Passed
    Debug Statements (Python)................................................Passed
    Detect Private Key.......................................................Passed
    pyupgrade................................................................Passed
    autoflake................................................................Passed
    isort....................................................................Passed
    black....................................................................Passed
    blacken-docs.............................................................Passed
    flake8...................................................................Failed
    - hook id: flake8
    - exit code: 1
    
    src/phantom/fn.py:36:9: B018 Found useless expression. Either assign it to a variable or remove it.
    
    Validate GitHub Workflows................................................Passed
    mypy.....................................................................Passed
    check-manifest...........................................................Passed
    format-readme............................................................Failed
    - hook id: format-readme
    - exit code: 1
    
    Executable `docker` not found
    
    (env) ➜  phantom-types git:(main) βœ—
    

    And indeed, looking at line 36 of that file shows an unassigned string expression. I assume this is just leftover since that line hasn't been touched since April, but maybe I'm misunderstanding.

    1. Running make test and/or pytest fails on all of the yaml-based tests. Here's an example:
    (env) ➜  phantom-types git:(main) βœ— pytest tests/ext/test_phonenumbers.yaml
    ============================================================================== test session starts ==============================================================================
    platform darwin -- Python 3.9.9, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
    rootdir: /Users/tushar/code/phantom-types, configfile: pyproject.toml
    plugins: mypy-plugins-1.9.2, typeguard-2.13.3
    collected 3 items
    
    tests/ext/test_phonenumbers.yaml FFF                                                                                                                                      [100%]
    
    =================================================================================== FAILURES ====================================================================================
    _____________________________________________________________________________ bound_is_not_subtype ______________________________________________________________________________
    /Users/tushar/code/phantom-types/tests/ext/test_phonenumbers.yaml:9:
    E   pytest_mypy_plugins.utils.TypecheckAssertionError: Invalid output:
    E   Actual:
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:53: error: Unused "type: ignore[misc]" comment (diff)
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:144: error: ClassVar cannot contain type variables  [misc] (diff)
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:162: error: Unused "type: ignore" comment (diff)
    E     main:7: error: Argument 1 to "takes_phone_number" has incompatible type "str"; expected "PhoneNumber"  [arg-type] (diff)
    E     main:12: error: Argument 1 to "takes_formatted_phone_number" has incompatible type "str"; expected "FormattedPhoneNumber"  [arg-type] (diff)
    E   Expected:
    E     main:7: error: Argument 1 to "takes_phone_number" has incompatible type "str"; expected "PhoneNumber"  [arg-type] (diff)
    E     main:12: error: Argument 1 to "takes_formatted_phone_number" has incompatible type "str"; expected "FormattedPhoneNumber"  [arg-type] (diff)
    E   Alignment of first line difference:
    E     E: main:7: error: Argument 1 to "takes_phone_number" has incompatible type ...
    E     A: ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:53: e...
    E        ^
    ________________________________________________________________________________ can_instantiate ________________________________________________________________________________
    /Users/tushar/code/phantom-types/tests/ext/test_phonenumbers.yaml:16:
    E   pytest_mypy_plugins.utils.TypecheckAssertionError: Output is not expected:
    E   Actual:
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:53: error: Unused "type: ignore[misc]" comment (diff)
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:144: error: ClassVar cannot contain type variables  [misc] (diff)
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:162: error: Unused "type: ignore" comment (diff)
    E   Expected:
    E     (empty)
    ___________________________________________________________________________________ can_infer ___________________________________________________________________________________
    /Users/tushar/code/phantom-types/tests/ext/test_phonenumbers.yaml:30:
    E   pytest_mypy_plugins.utils.TypecheckAssertionError: Output is not expected:
    E   Actual:
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:53: error: Unused "type: ignore[misc]" comment (diff)
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:144: error: ClassVar cannot contain type variables  [misc] (diff)
    E     ../../../../../../Users/tushar/code/phantom-types/src/phantom/base:162: error: Unused "type: ignore" comment (diff)
    E   Expected:
    E     (empty)
    ============================================================================ short test summary info ============================================================================
    FAILED tests/ext/test_phonenumbers.yaml::bound_is_not_subtype -
    FAILED tests/ext/test_phonenumbers.yaml::can_instantiate -
    FAILED tests/ext/test_phonenumbers.yaml::can_infer -
    =============================================================================== 3 failed in 0.81s ===============================================================================
    (env) ➜  phantom-types git:(main) βœ—
    

    It looks to me like every test is throwing errors on lines 53, 144, and 162 of src/phantom/base.py, e.g., here. If I run mypy ., the same errors appear in the output there.

    The first test example (bound_is_not_subtype) shows that I am getting the expected results, but every test has these three extra errors appended to it. I assume this is a difference in mypy config that I'm missing, but I'd love help figuring this out.

    Thanks for your great work on this library!

    opened by tuchandra 2
  • Support for hypothesis

    Support for hypothesis

    I currently wanted to add hypothesis to my tests and I use phantom types in my pydantic models. Hypothesis has built-in support for pydantic, but it turns out it does not work with phantom types.

    from phantom.interval import Open
    from hypothesis import given, strategies as st
    from pydantic import BaseModel
    
    
    class Int0To10(int, Open, low=0, high=10):
        ...
    
    # st.register_type_strategy(Int0To10, st.integers(0, 10))
    
    class MySchema(BaseModel):
        val: Int0To10
    
    @given(st.builds(MySchema))
    def test_schema(instance):
        print(instance.json(indent=2))
    
    test_schema()
    

    results in

    Traceback (most recent call last):
      File "/local/home/a.pirogov/.cache/pypoetry/virtualenvs/metador-core-AXME58BY-py3.8/lib/python3.8/site-packages/hypothesis/strategies/_internal/core.py", line 836, in do_draw
        return self.target(
    TypeError: __call__() missing 1 required positional argument: 'instance'
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "scratch_hypo.py", line 24, in <module>
        test_schema()
      File "scratch_hypo.py", line 20, in test_schema
        def test_schema(instance):
      File "/local/home/a.pirogov/.cache/pypoetry/virtualenvs/metador-core-AXME58BY-py3.8/lib/python3.8/site-packages/hypothesis/core.py", line 1325, in wrapped_test
        raise the_error_hypothesis_found
      File "/local/home/a.pirogov/.cache/pypoetry/virtualenvs/metador-core-AXME58BY-py3.8/lib/python3.8/site-packages/hypothesis/strategies/_internal/core.py", line 855, in do_draw
        raise InvalidArgument(
    hypothesis.errors.InvalidArgument: Calling <class '__main__.Int0To10'> with no arguments raised an error - try using from_type(<class '__main__.Int0To10'>) instead of builds(<class '__main__.Int0To10'>)
    

    but works when registering the type strategy (line in example is uncommented)

    Would it be possible to register the corresponding generating strategies for all built-in types and provide a hook like __hypothesis_strategy__ where an automatic strategy is not feasible / to override the default one?

    I think that probably should be done in the metaclass, so the strategy is registered whenever a type is defined and hypothesis is installed in the environment.

    enhancement 
    opened by apirogov 2
  • Use GIthub Pages for code coverage and docs

    Use GIthub Pages for code coverage and docs

    See draft PR: https://github.com/antonagestam/phantom-types/pull/257

    The preview feature is not yet available: https://github.com/antonagestam/phantom-types/blob/18a79f6662399ce970fb10422e86a1312cf86252/.github/workflows/docs.yaml#L54

    Once it is, will open up to some nice workflows and not having to depend on external services for these things.

    blocked 
    opened by antonagestam 0
  • Replacing `tzinfo=None` still considered TZAware

    Replacing `tzinfo=None` still considered TZAware

    from phantom.datetime import TZAware
    import datetime
    
    t = TZAware.parse(datetime.datetime.now(tz=datetime.timezone.utc))
    u = t.replace(tzinfo=None)
    reveal_type(u)  # -> TZAware
    
    opened by aiven-anton 0
  • feat: class docstring overrides default description from __schema__

    feat: class docstring overrides default description from __schema__

    fixes #238

    I do it in __modify_schema__, so that the docstring, if present, is applied last and not first (which would be the case when just adding it in __schema__)

    opened by apirogov 6
Releases(1.1.0)
  • 1.1.0(Nov 17, 2022)

    • Support Python 3.11 final by @antonagestam in https://github.com/antonagestam/phantom-types/pull/242
    • Remove ignored typing error of import of CachingProtocolMeta by @flaeppe in https://github.com/antonagestam/phantom-types/pull/244
    • Drop dependency on ammaraskar/sphinx-action by @antonagestam in https://github.com/antonagestam/phantom-types/pull/247

    Full Changelog: https://github.com/antonagestam/phantom-types/compare/1.0.0...v1.1.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Sep 24, 2022)

    Breaking changes

    There are two breaking changes in this release. Because interval types and predicate functions were incorrectly named, they have been renamed to use the terms inclusive/exclusive instead of closed/open. Renaming them has the benefit that more breakage downstream should be discovered in earlier stages, e.g. in test suites and at import-time, instead of breaking at runtime in production environments.

    A parse method has been implemented for TZAware and TZNaive using python-dateutil. When python-dateutil is not installed and the parse methods are called with strings, they now raise MissingDependency.

    What's changed

    • Make datetime.tzinfo non-optional for TZAware by @antonagestam in https://github.com/antonagestam/phantom-types/pull/233
    • Give parsing ability to datetime types by @antonagestam in https://github.com/antonagestam/phantom-types/pull/234
    • Fixed open/closed interval definitions by @g-as in https://github.com/antonagestam/phantom-types/pull/232

    New Contributors

    • @g-as made their first contribution in https://github.com/antonagestam/phantom-types/pull/232

    Full Changelog: https://github.com/antonagestam/phantom-types/compare/0.18.0...1.0.0

    Source code(tar.gz)
    Source code(zip)
  • 0.18.0(Sep 18, 2022)

    What's Changed

    • Canonicalize mutable bound check by @antonagestam in https://github.com/antonagestam/phantom-types/pull/224
    • Introduce PhantomBound and NonEmptyStr by @antonagestam in https://github.com/antonagestam/phantom-types/pull/193
    • Drop workaround for fixed 3.11 issue (see #220) by @antonagestam in https://github.com/antonagestam/phantom-types/pull/228

    Full Changelog: https://github.com/antonagestam/phantom-types/compare/v0.17.1...0.18.0

    Source code(tar.gz)
    Source code(zip)
  • v0.17.1(Jul 6, 2022)

    New features

    • Support Python 3.11 https://github.com/antonagestam/phantom-types/pull/220 https://github.com/antonagestam/phantom-types/pull/221

    Full Changelog: https://github.com/antonagestam/phantom-types/compare/v0.17.0...v0.17.1

    Source code(tar.gz)
    Source code(zip)
  • v0.17.0(Jul 6, 2022)

    New features

    • Use narrower types in predicates and bounds by @antonagestam in https://github.com/antonagestam/phantom-types/pull/218

    Fixed

    • Fix broken docstring function references by @antonagestam in https://github.com/antonagestam/phantom-types/pull/204
    • Pin numerary on Python 3.7 by @antonagestam in https://github.com/antonagestam/phantom-types/pull/213

    Meta

    • Add .editorconfig pre-commit check by @antonagestam in https://github.com/antonagestam/phantom-types/pull/205
    • Add project URLs to setup.cfg by @antonagestam in https://github.com/antonagestam/phantom-types/pull/212
    • Remove duplicate section in docs by @antonagestam in https://github.com/antonagestam/phantom-types/pull/208
    • Make base modules private by @antonagestam in https://github.com/antonagestam/phantom-types/pull/215
    • Bump pre-commit hooks by @antonagestam in https://github.com/antonagestam/phantom-types/pull/216
    • Run test suite on Python 3.11 by @antonagestam in https://github.com/antonagestam/phantom-types/pull/217

    Full Changelog: https://github.com/antonagestam/phantom-types/compare/0.16.0...v0.17.0

    Source code(tar.gz)
    Source code(zip)
  • 0.16.0(Mar 30, 2022)

    What's Changed

    • Explode partials in reprs by @antonagestam in https://github.com/antonagestam/phantom-types/pull/191
    • Remove instability notice by @antonagestam in https://github.com/antonagestam/phantom-types/pull/192
    • Introduce SequenceNotStr by @antonagestam in https://github.com/antonagestam/phantom-types/pull/194
    • Add minLength/maxLength to Schema by @antonagestam in https://github.com/antonagestam/phantom-types/pull/198

    Full Changelog: https://github.com/antonagestam/phantom-types/compare/0.15.1...0.16.0

    Source code(tar.gz)
    Source code(zip)
  • 0.15.0(Jan 22, 2022)

    • Add initial support for numeric tower through numerary, thanks to @posita for writing the library and making me aware of it 🍰 (#179)
    Source code(tar.gz)
    Source code(zip)
  • 0.14.0(Nov 28, 2021)

  • 0.13.0(Sep 25, 2021)

    • Remove deprecated phantom.ext.iso3166 (#152)
    • Raise an error for known mutable bounds. Subclassing from Phantom now raises MutableType if the bound type is known to be mutable, e.g. list, set, dict and unfrozen dataclasses will all raise an error. (#156)
    Source code(tar.gz)
    Source code(zip)
  • 0.12.0(Sep 24, 2021)

    Added

    • Improved __repr__ for predicate factories (#148)
    • Introduce src directory layout, to make tests run against an installed version of the library (#149)
    • Make CountryCode a union of a Literal and a phantom type. This gets rid of the external dependency on the iso3166 package for country code support and moves the country code support into phantom.iso3166. The old module is still importable but yields a deprecation warning and will be removed in 0.13.0. (#136)
    Source code(tar.gz)
    Source code(zip)
  • 0.11.0(Sep 24, 2021)

    Added

    • Expose and document the @excepts decorator (#142). This decorator wasn't documented and therefor not previously supported, however this change moves the decorator from phantom.utils to phantom.fn so code that depended on this private function will now how to update imports.
    • Enable blacken-docs for formatting example code (#141)

    Fixed

    • Don't expect callables to have __qualname__ (#143)
    • Make Interval parse strings (#145)
    Source code(tar.gz)
    Source code(zip)
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
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
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
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
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
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
A python documentation linter which checks that the docstring description matches the definition.

Darglint A functional docstring linter which checks whether a docstring's description matches the actual function/method implementation. Darglint expe

Terrence Reilly 463 Dec 31, 2022
Flake8 plugin that checks import order against various Python Style Guides

flake8-import-order A flake8 and Pylama plugin that checks the ordering of your imports. It does not check anything else about the imports. Merely tha

Python Code Quality Authority 270 Nov 24, 2022
Flake8 extension for checking quotes in python

Flake8 Extension to lint for quotes. Major update in 2.0.0 We automatically encourage avoiding escaping quotes as per PEP 8. To disable this, use --no

Zachary Heller 157 Dec 13, 2022
Check for python builtins being used as variables or parameters

Flake8 Builtins plugin Check for python builtins being used as variables or parameters. Imagine some code like this: def max_values(list, list2):

Gil Forcada Codinachs 98 Jan 8, 2023
flake8 plugin to run black for checking Python coding style

flake8-black Introduction This is an MIT licensed flake8 plugin for validating Python code style with the command line code formatting tool black. It

Peter Cock 146 Dec 15, 2022
Custom Python linting through AST expressions

bellybutton bellybutton is a customizable, easy-to-configure linting engine for Python. What is this good for? Tools like pylint and flake8 provide, o

H. Chase Stevens 249 Dec 31, 2022
Unbearably fast O(1) runtime type-checking in pure Python.

Look for the bare necessities, the simple bare necessities. Forget about your worries and your strife. β€” The Jungle Book.

beartype 1.4k Jan 1, 2023
Naming Convention checker for Python

PEP 8 Naming Conventions Check your code against PEP 8 naming conventions. This module provides a plugin for flake8, the Python code checker. (It repl

Python Code Quality Authority 411 Dec 23, 2022
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
Flake8 extension for enforcing trailing commas in python

Flake8 Extension to enforce better comma placement. Usage If you are using flake8 it's as easy as: pip install flake8-commas Now you can avoid those a

Python Code Quality Authority 127 Sep 3, 2022
Tool for pinpointing circular imports in Python. Find cyclic imports in any project

Pycycle: Find and fix circular imports in python projects Pycycle is an experimental project that aims to help python developers fix their circular de

Vadim Kravcenko 311 Dec 15, 2022