Skip to content

Commit b9c845e

Browse files
committed
as_functiontype
1 parent 404b7df commit b9c845e

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

basedtyping/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from __future__ import annotations
66

77
import sys
8+
from types import FunctionType
89
from typing import ( # type: ignore[attr-defined]
910
TYPE_CHECKING,
1011
Any,
@@ -53,6 +54,7 @@
5354
"Untyped",
5455
"Intersection",
5556
"TypeForm",
57+
"as_functiontype",
5658
)
5759

5860
if TYPE_CHECKING:
@@ -574,3 +576,21 @@ def f[T](t: TypeForm[T]) -> T: ...
574576
reveal_type(f(int | str)) # int | str
575577
"""
576578
)
579+
580+
581+
# TODO: conditionally declare FunctionType with a BASEDMYPY so that this doesn't break everyone else
582+
# https://github.com/KotlinIsland/basedmypy/issues/524
583+
def as_functiontype(fn: Callable[P, T]) -> FunctionType[P, T]: # type: ignore[type-arg]
584+
"""Asserts that a ``Callable`` is a ``FunctionType`` and returns it
585+
586+
best used as a decorator to fix other incorrectly typed decorators:
587+
588+
def deco(fn: Callable[[], None]) -> Callable[[], None]: ...
589+
590+
@as_functiontype
591+
@deco
592+
def foo(): ...
593+
"""
594+
if not isinstance(fn, FunctionType): # type: ignore[redundant-expr]
595+
raise TypeError(f"{fn} is not a FunctionType")
596+
return fn # type: ignore[unreachable]

tests/test_as_functiontype.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from __future__ import annotations
2+
3+
import pytest
4+
5+
from basedtyping import as_functiontype
6+
7+
8+
def test_as_functiontype():
9+
with pytest.raises(TypeError):
10+
as_functiontype(all)
11+
assert as_functiontype(test_as_functiontype) is test_as_functiontype # type: ignore[comparison-overlap]

0 commit comments

Comments
 (0)