diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index f914bfd6345d..83703dec408b 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -9,7 +9,7 @@ import os import sys from collections.abc import Iterable -from typing import Optional, TypeVar +from typing import List, Optional, TypeVar from mypy.build import ( BuildResult, @@ -911,15 +911,23 @@ def emit_module_methods( if fn.class_name is not None or fn.name == TOP_LEVEL_NAME: continue name = short_id_from_name(fn.name, fn.decl.shortname, fn.line) + flags: List[str] = [] if is_fastcall_supported(fn, emitter.capi_version): - flag = "METH_FASTCALL" + flags.append("METH_FASTCALL") else: - flag = "METH_VARARGS" + flags.append("METH_VARARGS") + flags.append("METH_KEYWORDS") + if fn.decl.is_async: + flags.append("METH_COROUTINE") emitter.emit_line( ( - '{{"{name}", (PyCFunction){prefix}{cname}, {flag} | METH_KEYWORDS, ' - "NULL /* docstring */}}," - ).format(name=name, cname=fn.cname(emitter.names), prefix=PREFIX, flag=flag) + '{{"{name}", (PyCFunction){prefix}{cname}, {flags}, ' "NULL /* docstring */}}," + ).format( + name=name, + cname=fn.cname(emitter.names), + prefix=PREFIX, + flags=" | ".join(flags), + ) ) emitter.emit_line("{NULL, NULL, 0, NULL}") emitter.emit_line("};") diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index beef8def7f43..4173387e6cf6 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -141,6 +141,7 @@ def __init__( is_prop_getter: bool = False, implicit: bool = False, internal: bool = False, + is_async: bool = False, ) -> None: self.name = name self.class_name = class_name @@ -160,6 +161,7 @@ def __init__( # If True, not present in the mypy AST and must be synthesized during irbuild # Currently only supported for property getters/setters self.implicit = implicit + self.is_async = is_async # If True, only direct C level calls are supported (no wrapper function) self.internal = internal @@ -209,6 +211,7 @@ def serialize(self) -> JsonDict: "is_prop_getter": self.is_prop_getter, "implicit": self.implicit, "internal": self.internal, + "is_async": self.is_async, } # TODO: move this to FuncIR? @@ -232,6 +235,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> FuncDecl: data["is_prop_getter"], data["implicit"], data["internal"], + data["is_async"], ) diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 65951999dcf9..53491b0e3889 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -185,7 +185,7 @@ def prepare_func_def( else (FUNC_CLASSMETHOD if fdef.is_class else FUNC_NORMAL) ) sig = mapper.fdef_to_sig(fdef, options.strict_dunders_typing) - decl = FuncDecl(fdef.name, class_name, module_name, sig, kind) + decl = FuncDecl(fdef.name, class_name, module_name, sig, kind=kind, is_async=fdef.is_coroutine) mapper.func_to_decl[fdef] = decl return decl diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test index 11ce67077270..5bc0683ebfd6 100644 --- a/mypyc/test-data/run-async.test +++ b/mypyc/test-data/run-async.test @@ -643,3 +643,16 @@ def test_async_def_contains_two_nested_functions() -> None: [file asyncio/__init__.pyi] def run(x: object) -> object: ... + +[case testIsCoroutineFunction] +import asyncio +import inspect + +async def foo(): + return 1 + +def test_asyncio_iscoroutinefunction(): + assert asyncio.iscoroutinefunction(foo) is True, "foo should be recognized as coroutine function" + +def test_inspect_iscoroutinefunction(): + assert inspect.iscoroutinefunction(foo) is True, "foo should be recognized as coroutine function"