bagof.core.magic
Attributes
REAL_TYPES
module-attribute
The real-number types eq_safenan recognises.
Note
NumPy registers numpy.floating as a numbers.Real, so NumPy
floats are recognised without this package importing NumPy.
UNION_TYPES
module-attribute
The union spellings this package understands.
UNSET
module-attribute
A value that indicates that an argument was not set.
Note
This is different from None, which may be a valid value.
Classes
MagicError
MultipleCauses
MagicHint
Bases: Generic[T]
Base class for magic objects (factories, converters).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hint
|
Any
|
The type hint to use for this magic object. If not provided, the default hint for the class is used. |
UNSET
|
Attributes
BOUND
class-attribute
instance-attribute
The type hint that this magic object is bound to.
DEFAULT
class-attribute
instance-attribute
The default type hint for this magic object.
FALLBACK
class-attribute
instance-attribute
UNWRAP
class-attribute
instance-attribute
The hints that unwrapped, origin and args transparently
unwrap before introspecting [hint][bagof.core.magic._hint.MagicHint.hint].
Note
A TypeVar is resolved to its default, its
(union of) constraints, or its bound - in that order - so that a
typevar is introspected exactly like the hint it stands for. This
matches fallback, which resolves typevars through
get_concrete_type.
Set to (tx.Annotated,) to opt out and introspect typevars as-is.
has_explicit_hint
property
Whether a hint was passed to the constructor.
False when the object fell back to its DEFAULT - which is
not the same as carrying a hint that happens to equal it.
unwrapped
property
origin
property
args
property
Methods:
rebind
Return a copy of this object describing a different hint.
Every other attribute is carried over, so a configured object
keeps its configuration - a threshold, a pattern, a length. The
memoised properties listed in [_CACHED][bagof.core.magic._hint.MagicHint._CACHED] are recomputed.
error
Build a MagicError for the given value and message.
The error is returned, not raised, so that the caller keeps
the raise and its traceback starts where the failure is:
Tip
Subclasses override this to build their own error type.
lazy_import
Class attribute holding an object that is only imported on first access, so that defining a class for an optional library does not import it.
The name must be fully qualified ("package.module.Name"); it is
imported the first time the attribute is read, and kept.
Warning
Any read of the class attribute imports it - including
dir-and-getattr walks such as inspect.getmembers,
documentation generators, or cls.DEFAULT used as a fallback
registry key. Pass explicit (lazy) keys where that matters.
Functions:
eq_safenan
Map a value to a form that compares equal across NaNs.
Since float("nan") != float("nan"), comparing values that
may contain NaN with == is unsafe. Apply this function to both
operands before comparing them: real NaN values are all mapped to one
sentinel (so that two NaNs compare equal), while every other value is
returned unchanged.
Note
Only real numbers are recognised. A complex NaN is returned unchanged, and so still compares unequal to itself.
get_args_uw
Safe version of tx.get_args that unwraps
Annotated hints.
Returns an empty tuple if the input is not a generic type.
get_concrete_type
Get a valid concrete type from a type hint.
- If the hint is annotated, the
Annotatedwrapper is removed. - If the hint has an origin, it is used.
- If the hint is a
TypeVar:- its default value is used, if it has one; otherwise
- the first of its constraints is used, if it has any; otherwise
- its bound is used, if it has one; otherwise
- the fallback type is used, if it is provided; otherwise
- a
TypeErroris raised.
- If the (resolved) hint is a concrete, non-abstract type, it is returned as is; otherwise
- The fallback type is used, if it is provided; otherwise
- A
TypeErroris raised.
Note
A constrained typevar has no single concrete type - it stands for
the union of its constraints - so the first constraint is taken,
the same way get_default takes the first value of a
Literal.
get_origin_uw
Safe version of tx.get_origin that unwraps
Annotated hints.
Returns the input type, instead of None, if the input is not a
generic type.
is_typeddict
Return true if an object is a TypedDict or a subclass
of it.
Tip
This function differs from
typing.is_typeddict in that it returns True
for TypedDict itself.
issubclassable
issubscriptable
Check that an object is subscriptable (i.e. can be used with []).
True if the object is a type and has __class_getitem__, or if it
is an instance and has __getitem__. Otherwise, returns False.
safe_get_args
Safe version of tx.get_args.
Returns an empty tuple if the input is not a generic type.
Can also unwrap some hints (e.g. Annotated) if asked.
safe_get_origin
Safe version of tx.get_origin.
Can also unwrap some hints (e.g. Annotated)
if asked.
Note
Unlike typing.get_origin, this returns the input hint
itself, instead of None, when the hint is not a generic type.
safe_isinstance
Safe isinstance (does not fail if second argument is not a type).
safe_issubclass
type2hint
Convert a type to a (subscriptable) type hint.
- If the input is a type, and it does not have
__class_getitem__, we try to find its corresponding type hint. For example, in python 3.8,type2hint(list)returnstyping.List. - Otherwise, the value is returned as is.
typeddict_required_keys
The required keys of a TypedDict.
Reads __required_keys__ where the class has it -- the only source
that accounts for Required /
NotRequired (in either nesting with
Annotated) and for inheriting from bases
declared with a different total=.
Falls back to __total__ where it does not:
typing.TypedDict gained __required_keys__ only in Python 3.9,
and before that a key's requiredness came from the class's total=
alone -- per-key Required/NotRequired did not exist.
Warning
On older Pythons, a typing.TypedDict that inherits from a
base declared with a different total= reports every
inherited key as required. The stdlib does not record which class
declared a key, nor a usable link back to the base -- a subclass
has no __orig_bases__ and its __mro__ reaches only
dict - so the true answer is not recoverable.
The error is in the safe direction: a required key that is really
optional makes a valid value fail loudly, rather than letting an
invalid one through. Use
typing_extensions.TypedDict, which reimplements
the class precisely to fix this, when it matters.
unwrap
Unwrap a type hint from its origin, if it is in the unwrap list.
If TypeVar is one of the origins to unwrap, it will
be unwrapped to its default, its (union of) constraints, or its bound -
in that order.
clear_pending
Drop the pending registrations (of registry, or of every registry)
without applying them. Meant for test isolation.
defer
Register value in registry under the object ref names, once that
object has been imported - see find_name for how it is found.
This is the lazy counterpart of registry[key] = value: if
the object can already be found, it is registered at once; otherwise
the registration is kept pending, and resolve_pending (which
get_from_registry calls
before every lookup) moves it into the registry as soon as the object
can be found. Nothing is ever imported
or evaluated to find it.
Names that only exist for type checkers
A name imported under if TYPE_CHECKING: does not exist
at runtime, so a key spelled with it never resolves. Use the real
dotted path - ForwardRef("dask.array.Array"), not an alias such
as "da.Array" - or ForwardRef("Array", module="dask.array").
Builtins are not looked up either: ForwardRef("int") never
resolves (key the registry by int itself). pending lists
the keys still waiting.
Ordering
"Last registration wins" holds as for plain keys:
- a newer registration of the same name in the same registry replaces an older pending one;
- pending entries are applied oldest first, and an entry that resolves now is registered at once;
- a pending entry never overrides a key already present in the
registry when it resolves. It is pending only because its
object could not be reached when it was registered, so a key
already there for that object was (almost always) written
later - typically a plain
registry[obj] = value.
Consequently, two different spellings of one object that resolve in different passes are not ordered by registration: the first one to resolve wins. (Entries resolving in the same pass keep registration order.)
Lifetime
A pending entry holds a weak reference to registry when it
supports one (a dict subclass), and is dropped once the registry
is collected; a plain dict is held strongly until the entry
resolves, or until clear_pending. value is always held
strongly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
registry
|
dict
|
The registry to fill. |
required |
ref
|
ForwardRef
|
The forward reference. It must hold a dotted name
( |
required |
value
|
Any
|
The value to register. |
required |
context
|
str
|
The module a relative name is looked up in, usually the
|
None
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If |
find_name
The object a dotted name refers to, if it is already imported. Never imports anything, and never evaluates the name.
- Relative: the first part of the name is looked up in the
globals of the
contextmodule (if it is imported), and the rest is walked as attributes. This follows the module's own runtime imports, such asimport numpy as npfor"np.ndarray". - Absolute: the longest dotted prefix of the name present in
sys.modulesis taken, and the rest is walked as attributes.
A module is always read through its __dict__, so a module-level
__getattr__ (PEP 562) cannot
import anything: a submodule that has not been imported yet is not
found. Builtins are not looked up.
Returns:
| Type | Description |
|---|---|
Any
|
The object. |
Raises:
| Type | Description |
|---|---|
NameError
|
If the name cannot be found (yet). |
has_module
Whether the top-level package of name can be imported, without
importing it.
Only the top-level name is looked up - finding a submodule's spec
would import its parent package - so has_module("dask.array")
tells whether dask is installed.
pending
The registrations still pending (in registry, or in any registry),
oldest first, as (context_module, "dotted.name", value). Meant for
debugging a key that never resolves.
resolve_pending
Move the pending registrations that can now be resolved into their registry, oldest first. Returns at once when nothing is pending.
get_from_registry calls this
before every lookup, so a caller only needs it before reading a
registry directly. A resolved entry is not written over a key already
present for its object - see the ordering note of defer.
Cost
Every pending entry is looked up on each call, so a lookup costs time linear in the number of entries that never resolve (about a microsecond each - an optional library that is installed but never imported, or a misspelt name).
Note
A name that resolves to an unhashable object cannot key a
registry: its entry is dropped with a RuntimeWarning.
get_default
Get a default value from a type hint.
- If the hint is a
Literal, the first value in the literal is returned (None, ifNoneis one of the literal's values). - If the hint is a
Unionthat containsNoneType,Noneis returned. - Otherwise, if the hint is a
Union, we recurse through its sub-hints and return the first default found. - If no default value can be found, a
TypeErroris raised. A factory should then be used.
get_from_registry
Get the best matching value from a registry whose keys are types or type hints.
The best match is the registry key that is the narrowest superclass
(or superhint) of hint, following its MRO; exact matches are always
preferred. If hint is Annotated and no match is
found for it directly, the search is retried against its unwrapped
hint.
Exact matches are by hint equality, so List[int] and list[int]
are different keys, but Union[int, str] and Union[str, int] are the
same one.
Pending lazy keys (see defer) whose target has been imported
since are moved into their registry first, so every caller sees them.
Two keys at the same distance from hint are told apart by
specificity (a subclass key beats its superclass), and then by
registry order. Distances along an MRO are all different, so a true
tie only happens between keys hint is a virtual subclass of (such
as two unrelated ABCs it is registered with). A lazy key enters the
registry when it resolves, so that is the position it ties at.
ishintstance
Like isinstance, but the second argument can be a type hint.
- If
hintistypeorType[...], checks thatobjis a type and that it is valid subclass of the hint argument. - If
hintis aLiteral, checks thatobjis one of its values. The value must match in type as well:Trueis not a validLiteral[1], even thoughTrue == 1. - If
hintis aUnion, checksobjagainst each of its members. - Otherwise, returns
issubhint(type(obj), hint).
Warning
A container's item types are not checked: a value carries its
type, and a type carries no arguments, so [1, 2] is a
valid List[str] as far as this function is concerned.
(Python itself refuses isinstance(x, list[int]) for the
same reason.) Checking the items means iterating them, which is
the caller's decision to make - bagof.validators does it.
issubhint
Check that a hint is a sub-hint for another hint.
A hint is a valid subhint if all values that are valid for the hint are also valid for the superhint.
Arguments are compared covariantly, so List[bool] is a
subhint of List[int]. A hint with no arguments is not a
subhint of one that has them - a bare list may hold
anything, so it cannot stand in for a List[int].
Note
An unparametrised Union or Literal asks
a different question: is this hint one of those? So
issubhint(int, Union) is False (an
int is not a union) even though
issubhint(int, Union[int, str]) is True.
This makes them usable as a BOUND, and it is why the
relation is not transitive through a bare Union.