Skip to content

Instantly share code, notes, and snippets.

@lolpack
Created August 8, 2025 13:43
Show Gist options
  • Select an option

  • Save lolpack/f09e5ccba93e381f1132cd84c5723914 to your computer and use it in GitHub Desktop.

Select an option

Save lolpack/f09e5ccba93e381f1132cd84c5723914 to your computer and use it in GitHub Desktop.
helpers.py
# 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",
]
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)
@lolpack
Copy link
Author

lolpack commented Aug 8, 2025

Pylance
helpers1
helpers2
helpers3
types_demo1
types_demo2

@lolpack
Copy link
Author

lolpack commented Aug 8, 2025

Pyrefly
helpers1
helpers2
helpers3
helpers4
types_demo1
types_demo2
types_demo3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment