{"id":9178,"library":"phantom-types","title":"Phantom Types for Python","description":"phantom-types is a Python library that enables the creation of 'phantom types' to enforce type safety at compile time without incurring runtime overhead. It leverages Python's `__instancecheck__` protocol and boolean predicates to allow developers to define stricter type constraints, helping to make 'illegal states unrepresentable' and mitigate 'shotgun parsing'. The library is currently at version 3.0.2 and follows semantic versioning after its 1.0 release, with new versions released periodically to add features, fix bugs, and maintain compatibility.","status":"active","version":"3.0.2","language":"en","source_language":"en","source_url":"https://github.com/antonagestam/phantom-types","tags":["type-checking","phantom-types","static-analysis","python3"],"install":[{"cmd":"pip install phantom-types","lang":"bash","label":"Base installation"},{"cmd":"pip install phantom-types[all]","lang":"bash","label":"With all optional dependencies"}],"dependencies":[{"reason":"Required for parsing strings with TZAware and TZNaive types.","package":"python-dateutil","optional":true},{"reason":"Required to use phantom.ext.phonenumbers.","package":"phonenumbers","optional":true},{"reason":"Required for Pydantic integration and schema generation.","package":"pydantic","optional":true},{"reason":"For advanced testing utilities.","package":"hypothesis","optional":true}],"imports":[{"symbol":"Phantom","correct":"from phantom import Phantom"},{"symbol":"contained","correct":"from phantom.predicates.collection import contained"},{"note":"TZAware was moved from phantom.tz to phantom.datetime in newer versions.","wrong":"from phantom.tz import TZAware","symbol":"TZAware","correct":"from phantom.datetime import TZAware"}],"quickstart":{"code":"from phantom import Phantom\nfrom phantom.predicates.collection import contained\nfrom typing import TYPE_CHECKING\n\n# Define a phantom type 'Name' that only accepts 'Jane' or 'Joe'\nclass Name(str, Phantom, predicate=contained({\"Jane\", \"Joe\"})):\n    pass\n\ndef greet(name: Name):\n    print(f\"Hello {name}!\")\n\n# Valid usage: explicitly parsing a value into the phantom type\ngreet(Name.parse(\"Jane\"))\n\n# Valid usage: using runtime type checking (e.g., isinstance) to narrow the type\njoe = \"Joe\"\nassert isinstance(joe, Name) # This assertion informs type checkers\ngreet(joe)\n\n# This call would typically be caught by a static type checker like MyPy\n# as 'bird' does not satisfy the predicate for 'Name'.\n# if TYPE_CHECKING:\n#     greet(\"bird\") # E.g., Uncommenting this line would cause a MyPy error\n","lang":"python","description":"This quickstart demonstrates how to define a simple phantom type `Name` that narrows the `str` type to only accept specific values ('Jane' or 'Joe'). It shows both explicit parsing using `Name.parse()` and leveraging Python's `isinstance()` for type narrowing recognized by static type checkers. Invalid inputs are caught at compile time by type checkers, preventing runtime errors."},"warnings":[{"fix":"Consult the changelog for specific migration steps if upgrading from <1.0 or if moving to 2.0.0+ from Python 3.7. Ensure your environment uses Python 3.9 or newer.","message":"Prior to version 1.0, breaking changes could occur between minor versions. After 1.0, the library adheres to semantic versioning. Version 2.0.0 specifically dropped support for Python 3.7.","severity":"breaking","affected_versions":"<1.0, 2.0.0+"},{"fix":"To resolve, create a new metaclass that inherits from both `PhantomMeta` (the metaclass used by `Phantom`) and the other custom metaclass, then use this new metaclass for your composite phantom type. Example: `class NewMeta(PhantomMeta, OldMeta): pass; class New(Old, Phantom, metaclass=NewMeta): ...`","message":"Phantom types are implemented using a metaclass, which can lead to 'metaclass conflicts' when attempting to subclass a phantom type from another type that also uses a custom metaclass.","severity":"gotcha","affected_versions":"All"},{"fix":"Only apply phantom types to immutable base types (e.g., `str`, `int`, `tuple`, `datetime.datetime`). Avoid using them with mutable types like `list` or custom mutable objects where internal state can change.","message":"Phantom types are incompatible with mutable data types. A type checker cannot detect if a mutation to an object changes it to no longer satisfy the phantom type's predicate, leading to silent inconsistencies.","severity":"gotcha","affected_versions":"All"},{"fix":"Always ensure the phantom type explicitly inherits from its runtime base type as the first base class (e.g., `class MyPhantom(MyBaseType, Phantom, ...): ...`).","message":"If a phantom type does not properly specify its runtime type bound (e.g., by not inheriting from the base type), static type checkers might 'erase' the runtime type information when performing type guarding, leading to `AttributeError` or other type-related issues.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Install the `dateutil` extra: `pip install 'phantom-types[dateutil]'` or `pip install phantom-types[all]`.","cause":"Attempting to use `TZAware.parse()` or `TZNaive.parse()` without `python-dateutil` installed.","error":"phantom.errors.MissingDependency: To parse timezone aware/naive datetimes from strings, install 'python-dateutil' (e.g., pip install 'phantom-types[dateutil]')."},{"fix":"Explicitly convert the value using the phantom type's `parse` method (e.g., `greet(Name.parse(my_string))`) or assert its type at runtime to inform the static type checker (e.g., `assert isinstance(my_string_var, Name); greet(my_string_var)`).","cause":"Passing a plain string literal or a string variable that has not been explicitly narrowed (e.g., via `Name.parse()` or `assert isinstance(my_var, Name)`) to a function expecting a phantom type.","error":"mypy: Argument 1 to \"greet\" has incompatible type \"str\"; expected \"Name\" [arg-type]"},{"fix":"Ensure the phantom type inherits directly from its runtime type as the first base class. For instance, `class UTCDateTime(datetime.datetime, Phantom, predicate=is_utc): ...` instead of `class UTCDateTime(Phantom, predicate=is_utc): ...`.","cause":"When a phantom type is defined without inheriting from its concrete runtime base type (e.g., `datetime.datetime`), static type checkers might lose the knowledge of the underlying type's attributes after a type guard or parse operation.","error":"AttributeError: 'UTCDateTime' object has no attribute 'year'"}]}