{"id":13468203,"url":"https://github.com/antonagestam/phantom-types","last_synced_at":"2025-09-22T00:41:49.117Z","repository":{"id":37563380,"uuid":"239025898","full_name":"antonagestam/phantom-types","owner":"antonagestam","description":"Phantom types for Python.","archived":false,"fork":false,"pushed_at":"2025-03-01T22:07:20.000Z","size":2168,"stargazers_count":210,"open_issues_count":13,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-15T14:04:17.517Z","etag":null,"topics":["mypy","phantom-types","python","python3","refined","refined-types","refinement-types","static-analysis","static-typing","typing","validation"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/phantom-types/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/antonagestam.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-07T21:28:59.000Z","updated_at":"2025-02-12T08:10:10.000Z","dependencies_parsed_at":"2023-12-29T23:28:01.219Z","dependency_job_id":"997a6fef-a24f-4e6b-b7b0-3f4b29208844","html_url":"https://github.com/antonagestam/phantom-types","commit_stats":null,"previous_names":["antonagestam/dependent-types"],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fphantom-types","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fphantom-types/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fphantom-types/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fphantom-types/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antonagestam","download_url":"https://codeload.github.com/antonagestam/phantom-types/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245597196,"owners_count":20641859,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mypy","phantom-types","python","python3","refined","refined-types","refinement-types","static-analysis","static-typing","typing","validation"],"created_at":"2024-07-31T15:01:07.024Z","updated_at":"2025-09-22T00:41:49.105Z","avatar_url":"https://github.com/antonagestam.png","language":"Python","readme":"\u003cp align=center\u003e\u003cimg src=https://raw.githubusercontent.com/antonagestam/phantom-types/main/docs/phantom.svg\u003e\u003c/p\u003e\n\n\u003ch1 align=center\u003ephantom-types\u003c/h1\u003e\n\n\u003cp align=center\u003e\n    \u003ca href=https://github.com/antonagestam/phantom-types/actions?query=workflow%3ACI+branch%3Amain\u003e\u003cimg src=https://github.com/antonagestam/phantom-types/actions/workflows/ci.yaml/badge.svg?branch=main alt=\"CI Build Status\"\u003e\u003c/a\u003e\n    \u003ca href=https://phantom-types.readthedocs.io/en/stable/\u003e\u003cimg src=https://readthedocs.org/projects/phantom-types/badge/?version=main alt=\"Documentation Build Status\"\u003e\u003c/a\u003e\n    \u003ca href=https://codecov.io/gh/antonagestam/phantom-types\u003e\u003cimg src=https://codecov.io/gh/antonagestam/phantom-types/branch/main/graph/badge.svg?token=UE85B7IA3Q alt=\"Test coverage report\"\u003e\u003c/a\u003e\n    \u003cbr\u003e\n    \u003ca href=https://pypi.org/project/phantom-types/\u003e\u003cimg src=https://img.shields.io/pypi/v/phantom-types.svg?color=informational\u0026label=PyPI alt=\"PyPI Package\"\u003e\u003c/a\u003e\n    \u003ca href=https://pypi.org/project/phantom-types/\u003e\u003cimg src=https://img.shields.io/pypi/pyversions/phantom-types.svg?color=informational\u0026label=Python alt=\"Python versions\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n[Phantom types][ghosts] for Python will help you make illegal states unrepresentable and\navoid shotgun parsing by enabling you to practice [\"Parse, don't validate\"][parse].\n\n\u003ch4 align=center\u003e\n    \u003ca href=https://phantom-types.readthedocs.io/en/stable/\u003eCheckout the complete documentation on Read the Docs →\u003c/a\u003e\n\u003c/h4\u003e\n\n## Installation\n\n```bash\n$  python3 -m pip install phantom-types\n```\n\n#### Extras\n\nThere are a few extras available that can be used to either enable a feature or install\na compatible version of a third-party library.\n\n| Extra name       | Feature                                                                                                    |\n| ---------------- | ---------------------------------------------------------------------------------------------------------- |\n| `[dateutil]`     | Installs [python-dateutil]. Required for parsing strings with [`TZAware` and `TZNaive`][phantom-datetime]. |\n| `[phonenumbers]` | Installs [phonenumbers]. Required to use [`phantom.ext.phonenumbers`][phantom-phonenumbers].               |\n| `[pydantic]`     | Installs [pydantic].                                                                                       |\n| `[hypothesis]`   | Installs [hypothesis].                                                                                     |\n| `[all]`          | Installs all of the above.                                                                                 |\n\n[python-dateutil]: https://pypi.org/project/python-dateutil/\n[phonenumbers]: https://pypi.org/project/phonenumbers/\n[pydantic]: https://pypi.org/project/pydantic/\n[hypothesis]: https://pypi.org/project/hypothesis/\n[phantom-datetime]:\n  https://phantom-types.readthedocs.io/en/main/pages/types.html#module-phantom.datetime\n[phantom-phonenumbers]:\n  https://phantom-types.readthedocs.io/en/main/pages/external-wrappers.html#module-phantom.ext.phonenumbers\n\n```bash\n$  python3 -m pip install phantom-types[all]\n```\n\n## Examples\n\nBy introducing a phantom type we can define a pre-condition for a function argument.\n\n```python\nfrom phantom import Phantom\nfrom phantom.predicates.collection import contained\n\n\nclass Name(str, Phantom, predicate=contained({\"Jane\", \"Joe\"})): ...\n\n\ndef greet(name: Name):\n    print(f\"Hello {name}!\")\n```\n\nNow this will be a valid call.\n\n```python\ngreet(Name.parse(\"Jane\"))\n```\n\n... and so will this.\n\n```python\njoe = \"Joe\"\nassert isinstance(joe, Name)\ngreet(joe)\n```\n\nBut this will yield a static type checking error.\n\n```python\ngreet(\"bird\")\n```\n\nTo be clear, the reason the first example passes is not because the type checker somehow\nmagically knows about our predicate, but because we provided the type checker with proof\nthrough the `assert`. All the type checker cares about is that runtime cannot continue\nexecuting past the assertion, unless the variable is a `Name`. If we move the calls\naround like in the example below, the type checker would give an error for the `greet()`\ncall.\n\n```python\njoe = \"Joe\"\ngreet(joe)\nassert isinstance(joe, Name)\n```\n\n### Runtime type checking\n\nBy combining phantom types with a runtime type-checker like [beartype] or [typeguard],\nwe can achieve the same level of security as you'd gain from using [contracts][dbc].\n\n```python\nimport datetime\nfrom beartype import beartype\nfrom phantom.datetime import TZAware\n\n\n@beartype\ndef soon(dt: TZAware) -\u003e TZAware:\n    return dt + datetime.timedelta(seconds=10)\n```\n\nThe `soon` function will now validate that both its argument and return value is\ntimezone aware, e.g. pre- and post conditions.\n\n### Pydantic support\n\nPhantom types are ready to use with [pydantic] and have [integrated\nsupport][pydantic-support] out-of-the-box. Subclasses of `Phantom` work with both\npydantic's validation and its schema generation.\n\n```python\nclass Name(str, Phantom, predicate=contained({\"Jane\", \"Joe\"})):\n    @classmethod\n    def __schema__(cls) -\u003e Schema:\n        return super().__schema__() | {\n            \"description\": \"Either Jane or Joe\",\n            \"format\": \"custom-name\",\n        }\n\n\nclass Person(BaseModel):\n    name: Name\n    created: TZAware\n\n\nprint(json.dumps(Person.schema(), indent=2))\n```\n\nThe code above outputs the following JSONSchema.\n\n```json\n{\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"name\": {\n      \"title\": \"Name\",\n      \"description\": \"Either Jane or Joe\",\n      \"format\": \"custom-name\",\n      \"type\": \"string\"\n    },\n    \"created\": {\n      \"title\": \"TZAware\",\n      \"description\": \"A date-time with timezone data.\",\n      \"type\": \"string\",\n      \"format\": \"date-time\"\n    }\n  },\n  \"required\": [\"name\", \"created\"]\n}\n```\n\n## Development\n\nInstall development requirements, preferably in a virtualenv:\n\n```bash\n$ python3 -m pip install .[all,test,type-check]\n```\n\nRun tests:\n\n```bash\n$ pytest\n# or\n$ make test\n```\n\nRun type checker:\n\n```bash\n$ mypy\n```\n\nLinters and formatters are set up with [goose], after installing it you can run it as:\n\n```bash\n# run all checks\n$ goose run --select=all\n# or just a single hook\n$ goose run mypy --select=all\n```\n\nIn addition to static type checking, the project is set up with [pytest-mypy-plugins] to\ntest that exposed mypy types work as expected, these checks will run together with the\nrest of the test suite, but you can single them out with the following command.\n\n```bash\n$ make test-typing\n```\n\n[parse]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/\n[ghosts]: https://kataskeue.com/gdp.pdf\n[build-status]:\n  https://github.com/antonagestam/phantom-types/actions?query=workflow%3ACI+branch%3Amain\n[coverage]: https://codecov.io/gh/antonagestam/phantom-types\n[typeguard]: https://github.com/agronholm/typeguard\n[beartype]: https://github.com/beartype/beartype\n[dbc]: https://en.wikipedia.org/wiki/Design_by_contract\n[pydantic]: https://pydantic-docs.helpmanual.io/\n[pydantic-support]:\n  https://phantom-types.readthedocs.io/en/stable/pages/pydantic-support.html\n[goose]: https://github.com/antonagestam/goose\n[pytest-mypy-plugins]: https://github.com/TypedDjango/pytest-mypy-plugins\n","funding_links":[],"categories":["Python","Additional types"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonagestam%2Fphantom-types","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantonagestam%2Fphantom-types","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonagestam%2Fphantom-types/lists"}