Skip to content

Commit

Permalink
feat: re-organize utility modules
Browse files Browse the repository at this point in the history
  • Loading branch information
seandstewart committed Jul 11, 2024
1 parent f978464 commit 5019468
Show file tree
Hide file tree
Showing 29 changed files with 59 additions and 48 deletions.
3 changes: 2 additions & 1 deletion src/typelib/binding.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import inspect
import typing as tp

from typelib import classes, compat, inspection, unmarshal
from typelib import unmarshal
from typelib.py import classes, compat, inspection

P = compat.ParamSpec("P")
R = tp.TypeVar("R")
Expand Down
12 changes: 1 addition & 11 deletions src/typelib/constants.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import inspect
"""Constants used throughout the library."""


class empty:
"""A singleton for signalling no input."""


DEFAULT_ENCODING = "utf-8"
POSITIONAL_ONLY = inspect.Parameter.POSITIONAL_ONLY
POSITIONAL_OR_KEYWORD = inspect.Parameter.POSITIONAL_OR_KEYWORD
KEYWORD_ONLY = inspect.Parameter.KEYWORD_ONLY
SELF_NAME = "self"
TOO_MANY_POS = "too many positional arguments"
VAR_POSITIONAL = inspect.Parameter.VAR_POSITIONAL
VAR_KEYWORD = inspect.Parameter.VAR_KEYWORD
KWD_KINDS = (VAR_KEYWORD, KEYWORD_ONLY)
POS_KINDS = (VAR_POSITIONAL, POSITIONAL_ONLY)
NULLABLES = (None, Ellipsis, type(None), type(Ellipsis))
PKG_NAME = __name__.split(".", maxsplit=1)[0]
5 changes: 3 additions & 2 deletions src/typelib/ctx.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""A simple hashmap type for working with types in a contextual manner."""
"""A simple hashmap for working with types in a contextual manner."""

from __future__ import annotations

import dataclasses

from typelib import graph, inspection, refs
from typelib import graph
from typelib.py import inspection, refs


class TypeContext(dict):
Expand Down
3 changes: 2 additions & 1 deletion src/typelib/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import inspect
import typing

from typelib import classes, compat, constants, inspection, refs
from typelib import constants
from typelib.py import classes, compat, inspection, refs

__all__ = ("static_order", "itertypes", "get_type_graph")

Expand Down
2 changes: 1 addition & 1 deletion src/typelib/interchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import dataclasses
import typing as tp

from typelib import classes, compat, inspection
from typelib import codec as mcodec
from typelib import marshal as mmarshal
from typelib import unmarshal as munmarshal
from typelib.py import classes, compat, inspection

__all__ = ("protocol", "InterchangeProtocol")

Expand Down
3 changes: 2 additions & 1 deletion src/typelib/marshal/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

import typing as tp

from typelib import compat, ctx, graph, inspection, refs, serdes
from typelib import ctx, graph, serdes
from typelib.marshal import routines
from typelib.py import compat, inspection, refs

T = tp.TypeVar("T")

Expand Down
3 changes: 2 additions & 1 deletion src/typelib/marshal/routines.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
import typing as tp
import uuid

from typelib import compat, graph, inspection, serdes
from typelib import graph, serdes
from typelib.py import compat, inspection

T = tp.TypeVar("T")

Expand Down
1 change: 1 addition & 0 deletions src/typelib/py/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Components for Python compatibility, introspection, and reflection."""
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/typelib/frames.py → src/typelib/py/frames.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Utilities for with stack traces and frames."""
"""Utilities for working with stack traces and frames."""

from __future__ import annotations

Expand Down
23 changes: 15 additions & 8 deletions src/typelib/future.py → src/typelib/py/future.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,34 @@

@functools.cache
def transform(annotation: str, *, union: str = "typing.Union") -> str:
"""Transform a :py:class:`types.UnionType` (``str | int``) into a :py:class:`typing.Union`.
"""Transform a modern annotations into their :py:mod:`typing` equivalent:
- :py:class:`types.UnionType` into a :py:class:`typing.Union` (``str | int`` -> ``typing.Union[str, int]``)
- builtin generics into typing generics (``dict[str, int]`` -> ``typing.Dict[str, int]``)
Args:
annotation: The annotation to transform, as a string.
union: The name of the Union type to subscript (defaults `"typing.Union"`).
Notes:
This is a raw string transformation that does not test for the *correctness*
of your annotation. As such, if you attempt to evaluate the transformed string
at runtime and there are errors in your declaration, they will result in an
error in the transformed annotation as well.
While this transformation requires your expression be valid Python syntax, it
doesn't make sure the type annotation is valid.
"""
parsed = ast.parse(annotation, mode="eval")
transformed = TransformUnion(union=union).generic_visit(parsed)
transformed = TransformAnnotation(union=union).generic_visit(parsed)
unparsed = ast.unparse(transformed).strip()
return unparsed


class TransformUnion(ast.NodeTransformer):
class TransformAnnotation(ast.NodeTransformer):
"""A :py:class:`ast.NodeTransformer` that transforms :py:class:`typing.Union`."""

def __init__(self, union: str = "typing.Union") -> None:
self.union = union

def visit_BinOp(self, node: ast.BinOp):
# Ignore anython but a bitwise OR `|`
"""Transform a :py:class:`ast.BinOp` to :py:class:`typing.Union`."""
# Ignore anything but a bitwise OR `|`
if not isinstance(node.op, ast.BitOr):
return node
# Build a stack of args to the bitor
Expand All @@ -59,6 +63,7 @@ def visit_BinOp(self, node: ast.BinOp):
return union

def visit_Name(self, node: ast.Name):
"""Transform a builtin :py:class:`ast.Name` to the `typing` equivalent."""
# Re-write new-style builtin generics as old-style typing generics
if node.id not in _GENERICS:
return node
Expand All @@ -68,6 +73,7 @@ def visit_Name(self, node: ast.Name):
return new

def visit_Subscript(self, node: ast.Subscript):
"""Transform all subscripts within a :py:class:`ast.Subscript`."""
# Scan all subscripts to we transform nested new-style types.
transformed = self.visit(node.slice)
new = ast.Subscript(
Expand All @@ -80,6 +86,7 @@ def visit_Subscript(self, node: ast.Subscript):
return new

def visit_Tuple(self, node: ast.Tuple):
"""Transform all values within a :py:class:`ast.Tuple`."""
# Scan all tuples to ensure we transform nested new-style types.
transformed = [self.visit(n) for n in node.elts]
new = ast.Tuple(elts=transformed, ctx=node.ctx)
Expand Down
15 changes: 8 additions & 7 deletions src/typelib/inspection.py → src/typelib/py/inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
overload,
)

from typelib import compat, constants, contrib, refs
from typelib import constants
from typelib.py import compat, contrib, refs

__all__ = (
"BUILTIN_TYPES",
Expand Down Expand Up @@ -94,7 +95,7 @@ def origin(annotation: Any) -> Any:
For the purposes of this library, if we can resolve to a builtin type, we will.
Examples:
>>> from typelib import inspection
>>> from typelib.py import inspection
>>> from typing import Dict, Mapping, NewType, Optional
>>> origin(Dict)
<class 'dict'>
Expand Down Expand Up @@ -167,7 +168,7 @@ def get_args(annotation: Any) -> Tuple[Any, ...]:
-> return Any
Examples:
>>> from typelib import inspection
>>> from typelib.py import inspection
>>> from typing import Dict, TypeVar, Any
>>> T = TypeVar("T")
>>> get_args(Dict)
Expand Down Expand Up @@ -213,7 +214,7 @@ def get_name(obj: Union[type, refs.ForwardRef, Callable]) -> str:
"""Safely retrieve the name of either a standard object or a type annotation.
Examples:
>>> from typelib import inspection
>>> from typelib.py import inspection
>>> from typing import Dict, Any
>>> T = TypeVar("T")
>>> get_name(Dict)
Expand All @@ -234,7 +235,7 @@ def get_qualname(obj: Union[type, refs.ForwardRef, Callable]) -> str:
"""Safely retrieve the qualname of either a standard object or a type annotation.
Examples:
>>> from typelib import inspection
>>> from typelib.py import inspection
>>> from typing import Dict, Any
>>> T = TypeVar("T")
>>> get_qualname(Dict)
Expand Down Expand Up @@ -269,7 +270,7 @@ def resolve_supertype(annotation: type[Any] | types.FunctionType) -> Any:
"""Get the highest-order supertype for a NewType.
Examples:
>>> from typelib import inspection
>>> from typelib.py import inspection
>>> from typing import NewType
>>> UserID = NewType("UserID", int)
>>> AdminID = NewType("AdminID", UserID)
Expand Down Expand Up @@ -579,7 +580,7 @@ def isfinal(obj: type) -> bool:
Examples:
>>> from typing import NewType
>>> from typelib.compat import Final
>>> from typelib.py.compat import Final
>>> isfinal(Final[str])
True
>>> isfinal(NewType("Foo", Final[str]))
Expand Down
2 changes: 1 addition & 1 deletion src/typelib/refs.py → src/typelib/py/refs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys
import typing

from typelib import frames, future
from typelib.py import frames, future

__all__ = ("ForwardRef", "evaluate", "forwardref")

Expand Down
3 changes: 2 additions & 1 deletion src/typelib/serdes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import pendulum
from more_itertools import peekable

from typelib import compat, constants, inspection
from typelib import constants
from typelib.py import compat, inspection


@t.overload
Expand Down
3 changes: 2 additions & 1 deletion src/typelib/unmarshal/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

import typing as tp

from typelib import compat, ctx, graph, inspection, refs
from typelib import ctx, graph
from typelib.py import compat, inspection, refs
from typelib.unmarshal import routines

T = tp.TypeVar("T")
Expand Down
3 changes: 2 additions & 1 deletion src/typelib/unmarshal/routines.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
import typing as tp
import uuid

from typelib import compat, constants, graph, inspection, serdes
from typelib import constants, graph, serdes
from typelib.py import compat, inspection

T = tp.TypeVar("T")

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/marshal/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import uuid

import pytest
from typelib import compat, refs
from typelib.marshal import api
from typelib.py import compat, refs

from tests import models

Expand Down
Empty file added tests/unit/py/__init__.py
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import dataclasses

from typelib import classes
from typelib.py import classes


def test_slotted():
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_frames.py → tests/unit/py/test_frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from unittest import mock

import pytest
from typelib import frames
from typelib.py import frames

GLOBAL: str = "FOO"
_LOCAL: str = "BAR"
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_future.py → tests/unit/py/test_future.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from typelib import future
from typelib.py import future


@pytest.mark.suite(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from unittest import mock

import pytest
from typelib import inspection, refs
from typelib.py import inspection, refs


class MyClass: ...
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_refs.py → tests/unit/py/test_refs.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from unittest import mock

import pytest
from typelib import refs
from typelib.py import refs


def evaluated_ref(t: str) -> refs.ForwardRef:
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import typing

import pytest
from typelib import graph, refs
from typelib import graph
from typelib.py import refs


@dataclasses.dataclass
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/test_interchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
import uuid

import pytest
from typelib import compat, interchange, refs
from typelib import interchange
from typelib.py import compat, refs

from tests import models

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/unmarshal/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import uuid

import pytest
from typelib import compat, refs
from typelib.py import compat, refs
from typelib.unmarshal import api

from tests import models
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/unmarshal/test_routines.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ def test_date_unmarshaller(given_input, expected_output):
),
time=dict(
given_input=datetime.time(tzinfo=datetime.timezone.utc),
expected_output=datetime.datetime.today().replace(
expected_output=datetime.datetime.today()
.astimezone(tz=datetime.timezone.utc)
.replace(
hour=0, minute=0, second=0, microsecond=0, tzinfo=datetime.timezone.utc
),
),
Expand Down

0 comments on commit 5019468

Please sign in to comment.