πŸ‘» 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)
Backport Python 3.8+ typing utils & add issubtype & more

typing-utils Backport Python3.8+ typing utils & issubtype & more Install API issubtype get_origin get_args get_type_hints Install pip install typi

10 Nov 09, 2022
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
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
: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
open source tools to generate mypy stubs from protobufs

mypy-protobuf: Generate mypy stub files from protobuf specs We just released a new major release mypy-protobuf 2. on 02/02/2021! It includes some back

Dropbox 527 Jan 03, 2023
An enhanced version of the Python typing library.

typingplus An enhanced version of the Python typing library that always uses the latest version of typing available, regardless of which version of Py

Contains 6 Mar 26, 2021
Run isort, pyupgrade, mypy, pylint, flake8, and more on Jupyter Notebooks

Run isort, pyupgrade, mypy, pylint, flake8, mdformat, black, blacken-docs, and more on Jupyter Notebooks βœ… handles IPython magics robustly βœ… respects

663 Jan 08, 2023
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 01, 2023
A plugin for Flake8 that provides specializations for type hinting stub files

flake8-pyi A plugin for Flake8 that provides specializations for type hinting stub files, especially interesting for linting typeshed. Functionality A

Łukasz Langa 58 Jan 04, 2023
πŸ¦† Better duck-typing with mypy-compatible extensions to Protocol

πŸ¦† Quacks If it walks like a duck and it quacks like a duck, then it must be a duck Thanks to PEP544, Python now has protocols: a way to define duck t

Arie Bovenberg 9 Nov 14, 2022
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 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
An extension for flake8 that forbids some imports statements in some modules.

flake8-obey-import-goat An extension for flake8 that forbids some imports statements in some modules. Important: this project is developed using DDD,

Ilya Lebedev 10 Nov 09, 2022
It's not just a linter that annoys you!

README for Pylint - https://pylint.pycqa.org/ Professional support for pylint is available as part of the Tidelift Subscription. Tidelift gives softwa

Python Code Quality Authority 4.4k Jan 04, 2023
Pymxs, the 3DsMax bindings of Maxscript to Python doesn't come with any stubs

PyMXS Stubs generator What Pymxs, the 3DsMax bindings of Maxscript to Python doe

Frieder Erdmann 19 Dec 27, 2022
flake8 plugin which checks that typing imports are properly guarded

flake8-typing-imports flake8 plugin which checks that typing imports are properly guarded installation pip install flake8-typing-imports flake8 codes

Anthony Sottile 50 Nov 01, 2022
Flashcards - A flash card application with 2 optional command line arguments

Flashcards A flash card application with 2 optional command line arguments impor

Γ–zgΓΌr Yildirim 2 Jul 15, 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
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 04, 2023
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