Created
August 8, 2025 13:43
-
-
Save lolpack/f09e5ccba93e381f1132cd84c5723914 to your computer and use it in GitHub Desktop.
helpers.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Requires Python 3.11+ (or install typing_extensions for older versions) | |
| from __future__ import annotations | |
| import os | |
| import re | |
| from collections import Counter, defaultdict, deque, ChainMap | |
| from dataclasses import dataclass, field | |
| from enum import Enum, IntEnum, Flag, auto | |
| from pathlib import Path | |
| from typing import ( | |
| Any, | |
| Callable, | |
| ClassVar, | |
| Concatenate, | |
| Coroutine, | |
| Generator, | |
| Iterable, | |
| Iterator, | |
| AsyncIterable, | |
| AsyncIterator, | |
| Mapping, | |
| MutableMapping, | |
| Sequence, | |
| MutableSequence, | |
| MutableSet, | |
| Set, | |
| FrozenSet, | |
| Dict, | |
| List, | |
| Tuple, | |
| Union, | |
| Optional, | |
| IO, | |
| TextIO, | |
| BinaryIO, | |
| Protocol, | |
| runtime_checkable, | |
| Type, | |
| Final, | |
| NamedTuple, | |
| TypeVar, | |
| ParamSpec, | |
| TypeVarTuple, | |
| Unpack, | |
| overload, | |
| SupportsInt, | |
| SupportsFloat, | |
| SupportsComplex, | |
| ) | |
| # Pull newer typing features from typing (3.11+) or typing_extensions | |
| try: | |
| from typing import ( | |
| Annotated, | |
| Literal, | |
| Never, | |
| NoReturn, | |
| Self, | |
| TypedDict, | |
| TypeAlias, | |
| TypeGuard, | |
| Required, | |
| NotRequired, | |
| LiteralString, | |
| assert_never, | |
| ) | |
| except Exception: # pragma: no cover | |
| from typing_extensions import ( # type: ignore | |
| Annotated, | |
| Literal, | |
| Never, | |
| NoReturn, | |
| Self, | |
| TypedDict, | |
| TypeAlias, | |
| TypeGuard, | |
| Required, | |
| NotRequired, | |
| LiteralString, | |
| assert_never, | |
| ) | |
| # --------------------------- | |
| # Type variables & aliases | |
| # --------------------------- | |
| T = TypeVar("T") | |
| U = TypeVar("U", bound=SupportsInt) | |
| P = ParamSpec("P") | |
| Ts = TypeVarTuple("Ts") | |
| JSONScalar: TypeAlias = Union[str, int, float, bool, None] | |
| JSON: TypeAlias = Union[JSONScalar, List["JSON"], Dict[str, "JSON"]] | |
| UserId: TypeAlias = int # alias | |
| # NewType is handy for highlighting as distinct from int | |
| try: | |
| from typing import NewType | |
| except Exception: # pragma: no cover | |
| from typing_extensions import NewType # type: ignore | |
| UserPk = NewType("UserPk", int) | |
| # --------------------------- | |
| # Enums | |
| # --------------------------- | |
| class Color(Enum): | |
| RED = "red" | |
| GREEN = "green" | |
| BLUE = "blue" | |
| class Status(IntEnum): | |
| PENDING = 0 | |
| RUNNING = 1 | |
| DONE = 2 | |
| class Perm(Flag): | |
| READ = auto() | |
| WRITE = auto() | |
| EXEC = auto() | |
| # --------------------------- | |
| # NamedTuple & dataclass | |
| # --------------------------- | |
| class Point(NamedTuple): | |
| x: float | |
| y: float | |
| @dataclass | |
| class DataRow: | |
| id: int | |
| name: str | |
| tags: list[str] = field(default_factory=list) | |
| ACTIVE: ClassVar[bool] = True # ClassVar | |
| KIND: Final[str] = "row" # Final | |
| # --------------------------- | |
| # TypedDict (with Required/NotRequired) | |
| # --------------------------- | |
| class UserTD(TypedDict): | |
| id: int | |
| name: str | |
| admin: NotRequired[bool] | |
| email: Required[str] | |
| # --------------------------- | |
| # Protocols | |
| # --------------------------- | |
| @runtime_checkable | |
| class SupportsClose(Protocol): | |
| def close(self) -> None: ... | |
| @runtime_checkable | |
| class Stringifier(Protocol): | |
| def __call__(self, __x: Any) -> str: ... | |
| class Doubler(Protocol[T]): | |
| def double(self, item: T) -> T: ... | |
| # --------------------------- | |
| # Generic class using Self, overload, etc. | |
| # --------------------------- | |
| class Box(Iterable[T]): | |
| _items: list[T] | |
| def __init__(self, *items: T) -> None: | |
| self._items = list(items) | |
| def __iter__(self) -> Iterator[T]: | |
| return iter(self._items) | |
| def add(self, item: T) -> Self: | |
| self._items.append(item) | |
| return self | |
| @overload | |
| def get(self, index: int) -> T: ... | |
| @overload | |
| def get(self, index: slice) -> list[T]: ... | |
| def get(self, index: int | slice) -> T | list[T]: | |
| return self._items[index] | |
| # --------------------------- | |
| # Higher-order / Callable with ParamSpec | |
| # --------------------------- | |
| def wrap_with_logging(func: Callable[P, T]) -> Callable[P, T]: | |
| def inner(*args: P.args, **kwargs: P.kwargs) -> T: | |
| return func(*args, **kwargs) | |
| return inner | |
| def bind_first(func: Callable[Concatenate[int, P], T], first: int) -> Callable[P, T]: | |
| def bound(*args: P.args, **kwargs: P.kwargs) -> T: | |
| return func(first, *args, **kwargs) | |
| return bound | |
| # --------------------------- | |
| # Variadic tuples: TypeVarTuple / Unpack | |
| # --------------------------- | |
| def head_tail(*tup: Unpack[Ts]) -> Tuple[Unpack[Ts]]: | |
| return tup # type: ignore[return-value] | |
| # --------------------------- | |
| # IO, regex, pathlike | |
| # --------------------------- | |
| def takes_streams(txt: TextIO, binf: BinaryIO) -> None: ... | |
| def regex_example(p: re.Pattern[str], m: re.Match[str] | None) -> None: ... | |
| def pathy(p: os.PathLike[str] | Path) -> None: ... | |
| # --------------------------- | |
| # Iteration / Async types | |
| # --------------------------- | |
| def make_iter() -> Iterator[int]: | |
| yield from (1, 2, 3) | |
| async def aiters() -> AsyncIterator[str]: | |
| yield "a" | |
| yield "b" | |
| def gen() -> Generator[int, None, str]: | |
| x = yield 1 | |
| return "done" | |
| async def coro() -> Coroutine[Any, Any, int]: | |
| return 42 | |
| # --------------------------- | |
| # TypeGuard example | |
| # --------------------------- | |
| def is_int_list(xs: list[object]) -> TypeGuard[list[int]]: | |
| return all(isinstance(x, int) for x in xs) | |
| # --------------------------- | |
| # LiteralString / Annotated | |
| # --------------------------- | |
| def sql_query(q: LiteralString) -> None: ... | |
| def tagged(x: Annotated[int, "primary-key"]) -> int: | |
| return x | |
| # --------------------------- | |
| # Mapping / collections | |
| # --------------------------- | |
| Numbers = list[int] # simple alias for readability | |
| def tally(xs: Iterable[int]) -> Counter[int]: | |
| return Counter(xs) | |
| def dd() -> defaultdict[str, list[int]]: | |
| return defaultdict(list) | |
| def chains(*maps: Mapping[str, int]) -> ChainMap[str, int]: | |
| return ChainMap(*maps) | |
| def dqs() -> deque[str]: | |
| return deque(["a", "b"]) | |
| # --------------------------- | |
| # Context Managers | |
| # --------------------------- | |
| class DummyCM: | |
| def __enter__(self) -> "DummyCM": | |
| return self | |
| def __exit__(self, exc_type, exc, tb) -> bool: | |
| return False | |
| # --------------------------- | |
| # Supports* protocols | |
| # --------------------------- | |
| def numeric_upconvert( | |
| a: SupportsInt, b: SupportsFloat, c: SupportsComplex | |
| ) -> complex: | |
| return complex(int(a)) + float(b) + complex(c) | |
| __all__ = [ | |
| "JSON", | |
| "JSONScalar", | |
| "UserPk", | |
| "Color", | |
| "Status", | |
| "Perm", | |
| "Point", | |
| "DataRow", | |
| "UserTD", | |
| "SupportsClose", | |
| "Stringifier", | |
| "Doubler", | |
| "Box", | |
| "wrap_with_logging", | |
| "bind_first", | |
| "head_tail", | |
| "takes_streams", | |
| "regex_example", | |
| "pathy", | |
| "make_iter", | |
| "aiters", | |
| "gen", | |
| "coro", | |
| "is_int_list", | |
| "sql_query", | |
| "tagged", | |
| "tally", | |
| "dd", | |
| "chains", | |
| "dqs", | |
| "DummyCM", | |
| "numeric_upconvert", | |
| "T", | |
| "U", | |
| "P", | |
| "Ts", | |
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from __future__ import annotations | |
| import io | |
| import os | |
| import re | |
| from decimal import Decimal | |
| from fractions import Fraction | |
| from pathlib import Path | |
| from types import EllipsisType | |
| from typing import ( | |
| Any, | |
| Optional, | |
| Union, | |
| Final, | |
| ClassVar, | |
| Type, | |
| Iterable, | |
| Iterator, | |
| Mapping, | |
| Sequence, | |
| overload, | |
| ) | |
| # Pull newer typing features (3.11+) or fall back to typing_extensions | |
| try: | |
| from typing import Literal, Never, NoReturn, TypeAlias, assert_never, LiteralString, Annotated | |
| except Exception: # pragma: no cover | |
| from typing_extensions import Literal, Never, NoReturn, TypeAlias, assert_never, LiteralString, Annotated # type: ignore | |
| # Import a zoo of typed things from helpers to trigger import highlighting | |
| from helpers import ( | |
| JSON, | |
| JSONScalar, | |
| UserPk, | |
| Color, | |
| Status, | |
| Perm, | |
| Point, | |
| DataRow, | |
| UserTD, | |
| SupportsClose, | |
| Stringifier, | |
| Doubler, | |
| Box, | |
| wrap_with_logging, | |
| bind_first, | |
| head_tail, | |
| takes_streams, | |
| regex_example, | |
| pathy, | |
| make_iter, | |
| aiters, | |
| gen, | |
| coro, | |
| is_int_list, | |
| sql_query, | |
| tagged, | |
| tally, | |
| dd, | |
| chains, | |
| dqs, | |
| DummyCM, | |
| numeric_upconvert, | |
| T, | |
| ) | |
| # --------------------------- | |
| # Builtins & literals | |
| # --------------------------- | |
| i: int = 123 | |
| f: float = 3.14 | |
| c: complex = 1 + 2j | |
| b: bool = True | |
| s: str = "hello" | |
| by: bytes = b"\x00\x01" | |
| ba: bytearray = bytearray(b"abc") | |
| mv: memoryview = memoryview(by) | |
| n: None = None | |
| el: EllipsisType = ... | |
| rng: range = range(5) | |
| sl: slice = slice(0, 10, 2) | |
| fr: Fraction = Fraction(1, 3) | |
| dec: Decimal = Decimal("1.23") | |
| # Containers (homogeneous & heterogeneous) | |
| li: list[int] = [1, 2, 3] | |
| tu: tuple[int, str, bool] = (1, "a", False) | |
| tv: tuple[int, ...] = (1, 2, 3) | |
| se: set[str] = {"a", "b"} | |
| fs: frozenset[str] = frozenset({"x", "y"}) | |
| di: dict[str, float] = {"pi": 3.14159} | |
| # Union / Optional / Literal | |
| u1: int | str = 5 | |
| u2: Union[int, str] = "x" | |
| opt: Optional[str] = None | |
| lit: Literal["red", "green", 7] = "red" | |
| # TypeAlias for local uses | |
| Pathish: TypeAlias = os.PathLike[str] | Path | |
| # Final/ClassVar in a class | |
| class C: | |
| KIND: Final[str] = "C" | |
| COUNT: ClassVar[int] = 0 | |
| # Type/instance relationships | |
| t_int: Type[int] = int | |
| t_path: Type[Path] = Path | |
| # Annotated & LiteralString | |
| username: Annotated[str, "user-field"] = "ari" | |
| def only_literal_strings(q: LiteralString) -> None: | |
| sql_query(q) | |
| # JSON-ish alias use | |
| config: JSON = {"a": [1, 2, {"b": True}]} | |
| scalar: JSONScalar = 42 | |
| # TypedDict use | |
| user: UserTD = {"id": 1, "name": "Ada", "email": "[email protected]"} | |
| # Enums & Flags | |
| color: Color = Color.RED | |
| status: Status = Status.RUNNING | |
| perm: Perm = Perm.READ | Perm.WRITE | |
| # NamedTuple & dataclass | |
| p: Point = Point(1.0, 2.0) | |
| row = DataRow(1, "alpha") | |
| # Protocols & runtime_checkable | |
| def close_it(x: SupportsClose) -> None: | |
| x.close() | |
| def stringify(s: Stringifier, obj: object) -> str: | |
| return s(obj) | |
| class IntDoubler: | |
| def double(self, item: int) -> int: | |
| return item * 2 | |
| def use_doubler(d: Doubler[int]) -> int: | |
| return d.double(21) | |
| # Generics, overloads, Self | |
| box = Box[int](1, 2, 3).add(4) | |
| first = box.get(0) | |
| slice_part = box.get(slice(1, 3)) | |
| # ParamSpec/Concatenate via helpers | |
| @wrap_with_logging | |
| def adder(x: int, y: int) -> int: | |
| return x + y | |
| def add_with_bind(prefix: int, x: int) -> int: | |
| return prefix + x | |
| bound = bind_first(add_with_bind, 10) | |
| bound_sum = bound(5) | |
| # TypeVarTuple/Unpack via head_tail | |
| _ = head_tail(1, "a", True) | |
| # Iterators / Generators / Async | |
| it: Iterator[int] = make_iter() | |
| g = gen() | |
| async_it = aiters() | |
| async_cor = coro() | |
| # IO & regex & pathlike | |
| txt = io.StringIO("hello") | |
| binf = io.BytesIO(b"data") | |
| takes_streams(txt, binf) | |
| pattern: re.Pattern[str] = re.compile(r"[a-z]+") | |
| match: re.Match[str] | None = pattern.match("abc") | |
| regex_example(pattern, match) | |
| pathy(Path(".")) | |
| # Collections utilities | |
| counts = tally([1, 1, 2, 3]) | |
| defaulted = dd() | |
| defaulted["k"].append(1) | |
| cm = chains({"a": 1}, {"b": 2}) | |
| dq = dqs() | |
| # Context manager | |
| with DummyCM() as cmgr: | |
| pass | |
| # Supports* numeric conversions | |
| _ = numeric_upconvert(True, 3.0, 4+0j) | |
| # UserPk (NewType) import | |
| uid: UserPk = UserPk(42) | |
| # Type narrowing with TypeGuard | |
| maybe_ints: list[object] = [1, 2, "nope"] | |
| if is_int_list(maybe_ints): | |
| total_ok: int = sum(maybe_ints) # narrowed to list[int] | |
| # Never / NoReturn / assert_never | |
| def die(msg: str) -> NoReturn: | |
| raise RuntimeError(msg) | |
| def exhaustive(e: Literal["x", "y"]) -> None: | |
| if e == "x": | |
| return | |
| if e == "y": | |
| return | |
| assert_never(e) # type: Never | |
| # Overloads (value vs sequence) | |
| @overload | |
| def first_of(x: Sequence[int]) -> int: ... | |
| @overload | |
| def first_of(x: Sequence[str]) -> str: ... | |
| def first_of(x: Sequence[Any]) -> Any: | |
| return x[0] | |
| # A function returning Union to highlight optionality | |
| def find_user_name(m: Mapping[int, str], uid: int) -> str | None: | |
| return m.get(uid) | |
| # Some async helpers to make tokens appear | |
| async def main_async() -> None: | |
| async for _ in async_it: | |
| pass | |
| _ = await async_cor | |
| if __name__ == "__main__": | |
| # Trivial runtime path to ensure nothing explodes if you run it. | |
| print(first_of([1, 2, 3])) | |
| print(first_of(["a", "b"])) | |
| print(status.name, color.value, perm.value) | |
| print(p, row, uid) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment







Pylance




