Other Modules

typing_redirect

The typing_redirect module allows usage of Python 3.12 features without breaking support for 3.8 or requiring a dependency on typing-extensions.

typing_redirect.py

This module allows for Python version-agnostic typing and collections.abc imports.

It uses Python standard library batteries where possible. For older versions, this module will export from typing_extensions.

class bytemaker.typing_redirect.Any(*args, **kwargs)[source]

Bases: object

Special type indicating an unconstrained type.

  • Any is compatible with every type.

  • Any assumed to have all methods.

  • All values assumed to be instances of Any.

Note that all the above statements are true from the point of view of static type checkers. At runtime, Any should not be used with instance checks.

class bytemaker.typing_redirect.Buffer

Bases: object

class bytemaker.typing_redirect.Callable

Bases: object

class bytemaker.typing_redirect.ForwardRef(arg, is_argument=True, module=None, *, is_class=False)[source]

Bases: _Final

Internal wrapper to hold a forward reference.

class bytemaker.typing_redirect.Hashable

Bases: object

class bytemaker.typing_redirect.Iterable

Bases: object

class bytemaker.typing_redirect.Mapping

Bases: Collection

A Mapping is a generic container for associating key/value pairs.

This class provides concrete generic implementations of all methods except for __getitem__, __iter__, and __len__.

get(k[, d]) D[k] if k in D, else d.  d defaults to None.
items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
values() an object providing a view on D's values
class bytemaker.typing_redirect.MutableMapping

Bases: Mapping

A MutableMapping is a generic container for associating key/value pairs.

This class provides concrete generic implementations of all methods except for __getitem__, __setitem__, __delitem__, __iter__, and __len__.

clear() None.  Remove all items from D.
pop(k[, d]) v, remove specified key and return the corresponding value.

If key is not found, d is returned if given, otherwise KeyError is raised.

popitem() (k, v), remove and return some (key, value) pair

as a 2-tuple; but raise KeyError if D is empty.

setdefault(k[, d]) D.get(k,d), also set D[k]=d if k not in D
update([E, ]**F) None.  Update D from mapping/iterable E and F.

If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v

class bytemaker.typing_redirect.MutableSequence

Bases: Sequence

All the operations on a read-write sequence.

Concrete subclasses must provide __new__ or __init__, __getitem__, __setitem__, __delitem__, __len__, and insert().

append(value)

S.append(value) – append value to the end of the sequence

clear() None -- remove all items from S
extend(values)

S.extend(iterable) – extend sequence by appending elements from the iterable

abstract insert(index, value)

S.insert(index, value) – insert value before index

pop([index]) item -- remove and return item at index (default last).

Raise IndexError if list is empty or index is out of range.

remove(value)

S.remove(value) – remove first occurrence of value. Raise ValueError if the value is not present.

reverse()

S.reverse() – reverse IN PLACE

class bytemaker.typing_redirect.ParamSpec

Bases: object

Parameter specification variable.

The preferred way to construct a parameter specification is via the dedicated syntax for generic functions, classes, and type aliases, where the use of ‘**’ creates a parameter specification:

type IntFunc[**P] = Callable[P, int]

For compatibility with Python 3.11 and earlier, ParamSpec objects can also be created as follows:

P = ParamSpec('P')

Parameter specification variables exist primarily for the benefit of static type checkers. They are used to forward the parameter types of one callable to another callable, a pattern commonly found in higher-order functions and decorators. They are only valid when used in Concatenate, or as the first argument to Callable, or as parameters for user-defined Generics. See class Generic for more information on generic types.

An example for annotating a decorator:

def add_logging[**P, T](f: Callable[P, T]) -> Callable[P, T]:
    '''A type-safe decorator to add logging to a function.'''
    def inner(*args: P.args, **kwargs: P.kwargs) -> T:
        logging.info(f'{f.__name__} was called')
        return f(*args, **kwargs)
    return inner

@add_logging
def add_two(x: float, y: float) -> float:
    '''Add two numbers together.'''
    return x + y

Parameter specification variables can be introspected. e.g.:

>>> P = ParamSpec("P")
>>> P.__name__
'P'

Note that only parameter specification variables defined in the global scope can be pickled.

args

Represents positional arguments.

kwargs

Represents keyword arguments.

class bytemaker.typing_redirect.Protocol[source]

Bases: Generic

Base class for protocol classes.

Protocol classes are defined as:

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example:

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as:

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
class bytemaker.typing_redirect.Sequence

Bases: Reversible, Collection

All the operations on a read-only sequence.

Concrete subclasses must override __new__ or __init__, __getitem__, and __len__.

count(value) integer -- return number of occurrences of value
index(value[, start[, stop]]) integer -- return first index of value.

Raises ValueError if the value is not present.

Supporting start and stop arguments is optional, but recommended.

class bytemaker.typing_redirect.TypeVar

Bases: object

Type variable.

The preferred way to construct a type variable is via the dedicated syntax for generic functions, classes, and type aliases:

class Sequence[T]:  # T is a TypeVar
    ...

This syntax can also be used to create bound and constrained type variables:

# S is a TypeVar bound to str
class StrSequence[S: str]:
    ...

# A is a TypeVar constrained to str or bytes
class StrOrBytesSequence[A: (str, bytes)]:
    ...

However, if desired, reusable type variables can also be constructed manually, like so:

T = TypeVar('T')  # Can be anything
S = TypeVar('S', bound=str)  # Can be any subtype of str
A = TypeVar('A', str, bytes)  # Must be exactly str or bytes

Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for generic types as well as for generic function and type alias definitions.

The variance of type variables is inferred by type checkers when they are created through the type parameter syntax and when infer_variance=True is passed. Manually created type variables may be explicitly marked covariant or contravariant by passing covariant=True or contravariant=True. By default, manually created type variables are invariant. See PEP 484 and PEP 695 for more details.

class bytemaker.typing_redirect.UnionType

Bases: object

Represent a PEP 604 union type

E.g. for int | str

bytemaker.typing_redirect.get_args(tp)[source]

Get type arguments with all substitutions performed.

For unions, basic simplifications used by Union constructor are performed.

Examples:

>>> T = TypeVar('T')
>>> assert get_args(Dict[str, int]) == (str, int)
>>> assert get_args(int) == ()
>>> assert get_args(Union[int, Union[T, int], str][int]) == (int, str)
>>> assert get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
>>> assert get_args(Callable[[], T][int]) == ([], int)
bytemaker.typing_redirect.get_origin(tp)[source]

Get the unsubscripted version of a type.

This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar, Annotated, and others. Return None for unsupported types.

Examples:

>>> P = ParamSpec('P')
>>> assert get_origin(Literal[42]) is Literal
>>> assert get_origin(int) is None
>>> assert get_origin(ClassVar[int]) is ClassVar
>>> assert get_origin(Generic) is Generic
>>> assert get_origin(Generic[T]) is Generic
>>> assert get_origin(Union[T, int]) is Union
>>> assert get_origin(List[Tuple[T, T]][int]) is list
>>> assert get_origin(P.args) is P
bytemaker.typing_redirect.get_type_hints(obj, globalns=None, localns=None, include_extras=False)[source]

Return type hints for an object.

This is often the same as obj.__annotations__, but it handles forward references encoded as string literals and recursively replaces all ‘Annotated[T, …]’ with ‘T’ (unless ‘include_extras=True’).

The argument may be a module, class, method, or function. The annotations are returned as a dictionary. For classes, annotations include also inherited members.

TypeError is raised if the argument is not of a type that can contain annotations, and an empty dictionary is returned if no annotations are present.

BEWARE – the behavior of globalns and localns is counterintuitive (unless you are familiar with how eval() and exec() work). The search order is locals first, then globals.

  • If no dict arguments are passed, an attempt is made to use the globals from obj (or the respective module’s globals for classes), and these are also used as the locals. If the object does not appear to have globals, an empty dictionary is used. For classes, the search order is globals first then locals.

  • If one dict argument is passed, it is used for both globals and locals.

  • If two dict arguments are passed, they specify globals and locals, respectively.

bytemaker.typing_redirect.overload(func)[source]

Decorator for overloaded functions/methods.

In a stub file, place two or more stub definitions for the same function in a row, each decorated with @overload.

For example:

@overload
def utf8(value: None) -> None: ...
@overload
def utf8(value: bytes) -> bytes: ...
@overload
def utf8(value: str) -> bytes: ...

In a non-stub file (i.e. a regular .py file), do the same but follow it with an implementation. The implementation should not be decorated with @overload:

@overload
def utf8(value: None) -> None: ...
@overload
def utf8(value: bytes) -> bytes: ...
@overload
def utf8(value: str) -> bytes: ...
def utf8(value):
    ...  # implementation goes here

The overloads for a function can be retrieved at runtime using the get_overloads() function.

bytemaker.typing_redirect.runtime_checkable(cls)[source]

Mark a protocol class as a runtime protocol.

Such protocol can be used with isinstance() and issubclass(). Raise TypeError if applied to a non-protocol class. This allows a simple-minded structural check very similar to one trick ponies in collections.abc such as Iterable.

For example:

@runtime_checkable
class Closable(Protocol):
    def close(self): ...

assert isinstance(open('/some/file'), Closable)

Warning: this will check only the presence of the required methods, not their type signatures!

utils

Various helper functions/classes (bytemaker.utils.Trie, bytemaker.utils.classproperty, bytemaker.utils.instance_of_union(), etc.) exposed for general use.

class bytemaker.utils.ByteConvertible[source]

Bases: object

Has __instancecheck__ and __subclasscheck__ methods to allow checking whether an object can be converted to a bytes object using python:bytes

class bytemaker.utils.DataClassType[source]

Bases: object

class bytemaker.utils.DataClassTypeMeta[source]

Bases: type

class bytemaker.utils.FrozenDict(input_dict: Dict[K, V])[source]

Bases: HashableMapping[K, V]

items() a set-like object providing a view on D's items[source]
class bytemaker.utils.HashableMapping[source]

Bases: Mapping[K, V], Hashable

class bytemaker.utils.Trie[source]

Bases: object

static build_prefix_trie(prefixes: Iterable[Iterable[int]]) Trie[source]
static build_suffix_trie(suffixes: Iterable[Sequence[int]]) Trie[source]
class bytemaker.utils.classproperty(fget=None, fset=None, fdel=None, doc=None)[source]

Bases: object

Emulate class-level property similar to instance-level property

deleter(fdel)[source]
getter(fget)[source]
setter(fset)[source]
bytemaker.utils.is_instance_of_union(obj, union_type: type)[source]
Determines if an object is an instance of a union type

(to support Python versions <3.10).

Parameters:
  • obj (object) – The object to check

  • union_type (type) – The union type to check against

Returns:

Whether the object is an instance of the union type

Return type:

bool

bytemaker.utils.is_subclass_of_union(subtype: type, supertype)[source]
Determines if an object is a subclass of a union type

(to support Python versions <3.10).

Parameters:
  • subtype (type) – The object type to check

  • supertype (type) – The union type to check against

Returns:

Whether the object is a subclass of the union type

Return type:

bool

bytemaker.utils.twos_complement(number, n_bits=32)[source]

Convert an integer to its two’s complement representation.

Parameters:
  • number – The integer to convert.

  • bits – The bit width for the two’s complement representation.

Returns:

A string representing the two’s complement of the number.

bytemaker.utils.twos_complement_bit_length(n: int)[source]

Determines the number of bits required to represent a signed integer in two’s complement.

Parameters:

n (int) – The (signed) integer for which to determine the number of bits required

Returns:

The number of bits required to represent the integer in two’s-complement notation

Return type:

int