tpy-lang 0.3.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tpy_lang-0.3.0.dev0.dist-info/METADATA +151 -0
- tpy_lang-0.3.0.dev0.dist-info/RECORD +333 -0
- tpy_lang-0.3.0.dev0.dist-info/WHEEL +4 -0
- tpy_lang-0.3.0.dev0.dist-info/entry_points.txt +3 -0
- tpyc/__init__.py +104 -0
- tpyc/__main__.py +6 -0
- tpyc/_buildinfo.py +1 -0
- tpyc/_data/docs/LANGUAGE_FEATURES.md +6278 -0
- tpyc/_data/docs/STDLIB_ROADMAP.md +1258 -0
- tpyc/_data/docs/TPY_FOR_AGENTS.md +556 -0
- tpyc/_data/lib/tpy/_bindings/__init__.py +6 -0
- tpyc/_data/lib/tpy/_bindings/pcre2.py +173 -0
- tpyc/_data/lib/tpy/_bindings/posix_socket.py +161 -0
- tpyc/_data/lib/tpy/_functools_macros.py +80 -0
- tpyc/_data/lib/tpy/_macro_helpers.py +161 -0
- tpyc/_data/lib/tpy/argparse.py +2062 -0
- tpyc/_data/lib/tpy/asyncio/__init__.py +744 -0
- tpyc/_data/lib/tpy/asyncio/_executor.py +515 -0
- tpyc/_data/lib/tpy/base64.py +410 -0
- tpyc/_data/lib/tpy/bisect.py +39 -0
- tpyc/_data/lib/tpy/builtins.py +38 -0
- tpyc/_data/lib/tpy/dataclasses.py +354 -0
- tpyc/_data/lib/tpy/enum.py +23 -0
- tpyc/_data/lib/tpy/functools.py +33 -0
- tpyc/_data/lib/tpy/hashlib.py +206 -0
- tpyc/_data/lib/tpy/heapq.py +118 -0
- tpyc/_data/lib/tpy/io.py +395 -0
- tpyc/_data/lib/tpy/json.py +221 -0
- tpyc/_data/lib/tpy/math.py +406 -0
- tpyc/_data/lib/tpy/random.py +597 -0
- tpyc/_data/lib/tpy/re.py +467 -0
- tpyc/_data/lib/tpy/socket.py +379 -0
- tpyc/_data/lib/tpy/struct.py +178 -0
- tpyc/_data/lib/tpy/sys.py +40 -0
- tpyc/_data/lib/tpy/time.py +39 -0
- tpyc/_data/lib/tpy/tpy/__init__.py +78 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/__init__.py +10 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/_decorators.py +37 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/_extern.py +64 -0
- tpyc/_data/lib/tpy/tpy/_builtins/__init__.py +11 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_bytes.py +378 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_dict.py +151 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_exceptions.py +125 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_funcs.py +681 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_io.py +97 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_list.py +127 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_range.py +52 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_set.py +139 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_super.py +11 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_types.py +661 -0
- tpyc/_data/lib/tpy/tpy/_core/__init__.py +23 -0
- tpyc/_data/lib/tpy/tpy/_core/_bytes_view.py +129 -0
- tpyc/_data/lib/tpy/tpy/_core/_containers.py +137 -0
- tpyc/_data/lib/tpy/tpy/_core/_functions.py +40 -0
- tpyc/_data/lib/tpy/tpy/_core/_types.py +2061 -0
- tpyc/_data/lib/tpy/tpy/_typing/__init__.py +77 -0
- tpyc/_data/lib/tpy/tpy/_version.py +29 -0
- tpyc/_data/lib/tpy/tpy/bits.py +28 -0
- tpyc/_data/lib/tpy/tpy/coro/__init__.py +127 -0
- tpyc/_data/lib/tpy/tpy/extern.py +8 -0
- tpyc/_data/lib/tpy/tpy/mem.py +49 -0
- tpyc/_data/lib/tpy/tpy/unsafe.py +195 -0
- tpyc/_data/lib/tpy/tpy/version.py +21 -0
- tpyc/_data/lib/tpy/typing.py +13 -0
- tpyc/_data/runtime/cpp/include/tpy/any.hpp +461 -0
- tpyc/_data/runtime/cpp/include/tpy/as_ostream.hpp +117 -0
- tpyc/_data/runtime/cpp/include/tpy/async.hpp +76 -0
- tpyc/_data/runtime/cpp/include/tpy/bigint.hpp +1343 -0
- tpyc/_data/runtime/cpp/include/tpy/builtins.hpp +400 -0
- tpyc/_data/runtime/cpp/include/tpy/bytes_ops.hpp +469 -0
- tpyc/_data/runtime/cpp/include/tpy/container_ops.hpp +487 -0
- tpyc/_data/runtime/cpp/include/tpy/copy_iter.hpp +82 -0
- tpyc/_data/runtime/cpp/include/tpy/core.hpp +558 -0
- tpyc/_data/runtime/cpp/include/tpy/dict_ops.hpp +289 -0
- tpyc/_data/runtime/cpp/include/tpy/dunder.hpp +750 -0
- tpyc/_data/runtime/cpp/include/tpy/dynamic.hpp +44 -0
- tpyc/_data/runtime/cpp/include/tpy/enum.hpp +40 -0
- tpyc/_data/runtime/cpp/include/tpy/file.hpp +245 -0
- tpyc/_data/runtime/cpp/include/tpy/fixed_int.hpp +317 -0
- tpyc/_data/runtime/cpp/include/tpy/format.hpp +954 -0
- tpyc/_data/runtime/cpp/include/tpy/frame_slot.hpp +120 -0
- tpyc/_data/runtime/cpp/include/tpy/generator.hpp +47 -0
- tpyc/_data/runtime/cpp/include/tpy/iterable_ops.hpp +122 -0
- tpyc/_data/runtime/cpp/include/tpy/itertools.hpp +749 -0
- tpyc/_data/runtime/cpp/include/tpy/next_iter.hpp +82 -0
- tpyc/_data/runtime/cpp/include/tpy/ordered_map.hpp +518 -0
- tpyc/_data/runtime/cpp/include/tpy/ordered_set.hpp +337 -0
- tpyc/_data/runtime/cpp/include/tpy/own_iter.hpp +54 -0
- tpyc/_data/runtime/cpp/include/tpy/pascal_graph_sdl.hpp +192 -0
- tpyc/_data/runtime/cpp/include/tpy/printing.hpp +302 -0
- tpyc/_data/runtime/cpp/include/tpy/protocols.hpp +61 -0
- tpyc/_data/runtime/cpp/include/tpy/range.hpp +115 -0
- tpyc/_data/runtime/cpp/include/tpy/ranges.hpp +212 -0
- tpyc/_data/runtime/cpp/include/tpy/set_ops.hpp +265 -0
- tpyc/_data/runtime/cpp/include/tpy/slice.hpp +47 -0
- tpyc/_data/runtime/cpp/include/tpy/span_iter.hpp +42 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/math.hpp +41 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/pcre2_h.hpp +96 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/random.hpp +25 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/socket_h.hpp +145 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/time.hpp +62 -0
- tpyc/_data/runtime/cpp/include/tpy/system.hpp +121 -0
- tpyc/_data/runtime/cpp/include/tpy/throwable.hpp +55 -0
- tpyc/_data/runtime/cpp/include/tpy/tpy.hpp +156 -0
- tpyc/_data/runtime/cpp/include/tpy/type_name.hpp +77 -0
- tpyc/_data/runtime/cpp/include/tpy/type_traits.hpp +240 -0
- tpyc/_data/runtime/cpp/include/tpy/uninit_array_storage.hpp +250 -0
- tpyc/_data/runtime/cpp/include/tpy/uninit_heap_storage.hpp +277 -0
- tpyc/_data/runtime/cpp/include/tpy/varargs.hpp +174 -0
- tpyc/_data/runtime/cpp/include/tpy/variant_ref.hpp +118 -0
- tpyc/_data/runtime/cpp/src/stdlib/socket_impl.cpp +104 -0
- tpyc/_data/runtime/cpp/third_party/README.md +58 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/AUTHORS +36 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/CMakeLists.txt +1233 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/COPYING +5 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/ChangeLog +3097 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/HACKING +853 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/INSTALL +368 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/LICENCE +94 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/NEWS +492 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/NON-AUTOTOOLS-BUILD +430 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/README +956 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/COPYING-CMAKE-SCRIPTS +22 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindEditline.cmake +16 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindPackageHandleStandardArgs.cmake +58 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindReadline.cmake +29 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config-version.cmake.in +15 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config.cmake.in +148 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/config-cmake.h.in +56 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-16.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-32.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-8.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-posix.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/pcre2-config.in +121 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h +483 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.generic +483 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.in +460 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.generic +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.in +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_auto_possess.c +1371 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c +196 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c.dist +196 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chkdint.c +96 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_compile.c +11001 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_config.c +252 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_context.c +510 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_convert.c +1189 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dfa_match.c +4119 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dftables.c +297 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_error.c +345 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_extuni.c +162 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_find_bracket.c +219 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_fuzzsupport.c +792 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_internal.h +2084 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_intmodedep.h +940 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_compile.c +14972 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_match.c +200 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_misc.c +234 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_neon_inc.h +354 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_simd_inc.h +2355 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_test.c +2528 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_maketables.c +165 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match.c +7777 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match_data.c +185 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_newline.c +243 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ord2utf.c +120 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_pattern_info.c +432 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_printint.c +886 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_script_run.c +344 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_serialize.c +286 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_string_utils.c +237 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_study.c +1915 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substitute.c +1009 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substring.c +550 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_tables.c +234 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucd.c +5460 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucp.h +396 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucptables.c +1533 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_valid_utf.c +398 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_xclass.c +308 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2demo.c +497 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2grep.c +4606 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.c +425 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.h +187 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix_test.c +209 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2test.c +9708 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorApple.c +137 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorCore.c +327 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorFreeBSD.c +89 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorPosix.c +62 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorWindows.c +40 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorNetBSD.c +72 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorPosix.c +172 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorPosix.c +141 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorWindows.c +102 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfig.h +142 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigCPU.h +188 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigInternal.h +907 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.c +3561 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.h +2466 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_32.c +4636 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_64.c +3491 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_T2_32.c +4302 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeLOONGARCH_64.c +3765 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_32.c +472 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_64.c +387 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_common.c +4259 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_32.c +485 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_64.c +719 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_common.c +3161 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_32.c +142 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_64.c +222 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_common.c +3121 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeS390X.c +4526 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_32.c +1685 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_64.c +1398 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_common.c +5001 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitSerialize.c +516 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitUtils.c +344 -0
- tpyc/_data/runtime/cpp/third_party/pcre2.sources.txt +54 -0
- tpyc/_data/runtime/cpp/third_party/pcre2.vendor.json +7 -0
- tpyc/build/__init__.py +7 -0
- tpyc/build/pcre2.py +122 -0
- tpyc/build/third_party.py +413 -0
- tpyc/cli.py +822 -0
- tpyc/codegen_cpp/__init__.py +18 -0
- tpyc/codegen_cpp/builtins.py +484 -0
- tpyc/codegen_cpp/context.py +2064 -0
- tpyc/codegen_cpp/expressions.py +5940 -0
- tpyc/codegen_cpp/functions.py +1913 -0
- tpyc/codegen_cpp/gen_async.py +3258 -0
- tpyc/codegen_cpp/gen_generators.py +657 -0
- tpyc/codegen_cpp/generator.py +2258 -0
- tpyc/codegen_cpp/match.py +1997 -0
- tpyc/codegen_cpp/param_const.py +172 -0
- tpyc/codegen_cpp/protocols.py +907 -0
- tpyc/codegen_cpp/records.py +1654 -0
- tpyc/codegen_cpp/resumable_cfg.py +1651 -0
- tpyc/codegen_cpp/statements.py +4963 -0
- tpyc/codegen_cpp/string_dispatch.py +76 -0
- tpyc/codegen_cpp/test_context.py +46 -0
- tpyc/codegen_cpp/test_param_const.py +113 -0
- tpyc/codegen_cpp/test_resumable_cfg.py +182 -0
- tpyc/codegen_cpp/type_resolution.py +53 -0
- tpyc/codegen_cpp/types.py +436 -0
- tpyc/codegen_cpp/variant_access.py +135 -0
- tpyc/coercions.py +749 -0
- tpyc/compilation_context.py +57 -0
- tpyc/compiler.py +3945 -0
- tpyc/cycle_detection.py +358 -0
- tpyc/diagnostics.py +135 -0
- tpyc/dump_types.py +353 -0
- tpyc/frontend_diagnostics.py +47 -0
- tpyc/frontend_ir/__init__.py +140 -0
- tpyc/frontend_ir/lower.py +1098 -0
- tpyc/frontend_ir/nodes.py +718 -0
- tpyc/frontend_ir/resolver_adapter.py +151 -0
- tpyc/frontend_plugin.py +209 -0
- tpyc/install_docs.py +81 -0
- tpyc/liveness.py +756 -0
- tpyc/macro_api.py +1724 -0
- tpyc/macro_loader.py +497 -0
- tpyc/module_names.py +64 -0
- tpyc/modules/__init__.py +31 -0
- tpyc/modules/defs.py +89 -0
- tpyc/modules/registry.py +36 -0
- tpyc/modules/resolver.py +192 -0
- tpyc/modules/type_resolution.py +629 -0
- tpyc/namespace.py +172 -0
- tpyc/parse/__init__.py +84 -0
- tpyc/parse/imports.py +490 -0
- tpyc/parse/nodes.py +1732 -0
- tpyc/parse/parser.py +4043 -0
- tpyc/parse/resolve_refs.py +466 -0
- tpyc/parse/type_resolver.py +1060 -0
- tpyc/prescan.py +254 -0
- tpyc/qnames.py +149 -0
- tpyc/repl.py +529 -0
- tpyc/repl_backends.py +848 -0
- tpyc/sema/__init__.py +21 -0
- tpyc/sema/analyzer.py +3625 -0
- tpyc/sema/bound_check.py +72 -0
- tpyc/sema/builder_trace.py +684 -0
- tpyc/sema/calls.py +5406 -0
- tpyc/sema/compatibility.py +2107 -0
- tpyc/sema/context.py +1243 -0
- tpyc/sema/expressions.py +3737 -0
- tpyc/sema/flow_facts.py +199 -0
- tpyc/sema/init_tracker.py +150 -0
- tpyc/sema/list_literals.py +69 -0
- tpyc/sema/literal_utils.py +27 -0
- tpyc/sema/local_deduction.py +1088 -0
- tpyc/sema/macros.py +179 -0
- tpyc/sema/match.py +1177 -0
- tpyc/sema/method_expansion.py +347 -0
- tpyc/sema/methods.py +2197 -0
- tpyc/sema/mutation_propagation.py +268 -0
- tpyc/sema/narrowing.py +857 -0
- tpyc/sema/numeric_lattice.py +160 -0
- tpyc/sema/operators.py +402 -0
- tpyc/sema/overloads.py +841 -0
- tpyc/sema/protocols.py +1209 -0
- tpyc/sema/reach_analysis.py +202 -0
- tpyc/sema/registration.py +3156 -0
- tpyc/sema/scope_tracker.py +193 -0
- tpyc/sema/statements.py +4426 -0
- tpyc/sema/type_ops.py +1879 -0
- tpyc/sema/value_range.py +181 -0
- tpyc/symbol_binding.py +259 -0
- tpyc/test_c3_mro.py +208 -0
- tpyc/test_cli_argv.py +52 -0
- tpyc/test_compiler.py +559 -0
- tpyc/test_contains_type_param.py +101 -0
- tpyc/test_cycle_detection.py +221 -0
- tpyc/test_dump_types.py +225 -0
- tpyc/test_install_docs.py +65 -0
- tpyc/test_local_cpp_form.py +135 -0
- tpyc/test_macro_loader.py +76 -0
- tpyc/test_method_expansion.py +254 -0
- tpyc/test_nominal_identity.py +182 -0
- tpyc/test_overloads.py +410 -0
- tpyc/test_parse.py +303 -0
- tpyc/test_parse_type_ref.py +506 -0
- tpyc/test_parse_version_info.py +58 -0
- tpyc/test_reach_analysis.py +72 -0
- tpyc/test_ref_type.py +216 -0
- tpyc/test_send_sync_substitution.py +276 -0
- tpyc/test_tuple_mutation_propagation.py +206 -0
- tpyc/test_type_def_registry.py +1729 -0
- tpyc/test_union_types.py +195 -0
- tpyc/type_def_registry.py +975 -0
- tpyc/typesys.py +5104 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# tpy: cpp_namespace("tpystd::asyncio::_executor")
|
|
2
|
+
# tpy: include("<tpy/async.hpp>")
|
|
3
|
+
# tpy: include("<tpy/stdlib/time.hpp>")
|
|
4
|
+
"""Internal asyncio scaffolding -- the TPy executor and the small
|
|
5
|
+
helpers it needs. Not part of the public asyncio API; imported only
|
|
6
|
+
by `lib/tpy/asyncio/` modules. See `docs/ASYNC_DESIGN.md` for the
|
|
7
|
+
dispatch model and `docs/ASYNC_PROGRESS.md` for port history.
|
|
8
|
+
|
|
9
|
+
`Waker` (defined in `tpy.coro`) holds a `Ptr[Awaker]` to the running
|
|
10
|
+
executor; `Waker.wake()` dispatches `mark_runnable` through the
|
|
11
|
+
`@dynamic Awaker` vtable. `Executor` inherits `Awaker` so it provides
|
|
12
|
+
the vtable slot directly -- no C++ ExecutorOps table, no templated
|
|
13
|
+
thunks, no thread-local handle.
|
|
14
|
+
"""
|
|
15
|
+
import heapq
|
|
16
|
+
|
|
17
|
+
from typing import Protocol
|
|
18
|
+
from builtins import BaseException
|
|
19
|
+
from time import monotonic, sleep_until_steady
|
|
20
|
+
from tpy import Int32, Own, Ptr, Throwable, dynamic, nocopy, readonly
|
|
21
|
+
from tpy.extern import builtin_type, cpp_template
|
|
22
|
+
from tpy.coro import Awaker, Cancellable, Poll, Waker, poll_ready, poll_pending
|
|
23
|
+
from tpy.mem import UninitArrayStorage
|
|
24
|
+
from tplib import Box
|
|
25
|
+
from tplib.rc import Rc
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Type-erased task machinery is co-located with the executor (rather
|
|
29
|
+
# than split into a sibling submodule) to avoid the parent-package
|
|
30
|
+
# auto-include cycle in `tpyc/codegen_cpp/generator.py:1530+`; see
|
|
31
|
+
# BUGS.md.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# `Cancellable[T]` (`__poll__` + `cancel`) lives in `tpy.coro` now;
|
|
35
|
+
# we re-export it from `asyncio` for user-facing API typing.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# T-erased view of a task for the executor's slot table. TaskState[T]
|
|
39
|
+
# structurally conforms via its poll_any / cancel_any methods; the
|
|
40
|
+
# slot table holds Box[AnyTask] without parameterization on T.
|
|
41
|
+
@dynamic
|
|
42
|
+
class AnyTask(Protocol):
|
|
43
|
+
def poll_any(self, waker: Waker) -> bool: ...
|
|
44
|
+
def cancel_any(self) -> None: ...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@nocopy
|
|
48
|
+
class TaskState[T]:
|
|
49
|
+
"""Shared backing for a `Task[T]`.
|
|
50
|
+
|
|
51
|
+
Owns a `Box[Cancellable[T]]` (the type-erased coroutine frame) and
|
|
52
|
+
caches result/exception once the frame completes. Single-awaiter
|
|
53
|
+
for v1: a second `__poll__` after Ready panics.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
frame: Box[Cancellable[T]] | None
|
|
57
|
+
result: UninitArrayStorage[T, 1]
|
|
58
|
+
exc: Box[Throwable] | None
|
|
59
|
+
awaiter: Waker
|
|
60
|
+
done: bool
|
|
61
|
+
has_result: bool
|
|
62
|
+
executor_owned: bool
|
|
63
|
+
|
|
64
|
+
def __init__(self, frame: Own[Box[Cancellable[T]]]) -> None:
|
|
65
|
+
self.frame = frame
|
|
66
|
+
self.result = UninitArrayStorage[T, 1]()
|
|
67
|
+
self.exc = None
|
|
68
|
+
self.awaiter = Waker()
|
|
69
|
+
self.done = False
|
|
70
|
+
self.has_result = False
|
|
71
|
+
self.executor_owned = False
|
|
72
|
+
|
|
73
|
+
def __del__(self) -> None:
|
|
74
|
+
if self.has_result:
|
|
75
|
+
self.result.take0()
|
|
76
|
+
self.has_result = False
|
|
77
|
+
|
|
78
|
+
# User-facing poll. Drives the frame for non-executor-owned tasks;
|
|
79
|
+
# for executor-owned tasks, parks (the executor's poll_any drives).
|
|
80
|
+
def __poll__(self, w: Waker) -> Own[Poll[T]]:
|
|
81
|
+
if self.done:
|
|
82
|
+
exc = self.exc
|
|
83
|
+
if exc is not None:
|
|
84
|
+
raise exc
|
|
85
|
+
if not self.has_result:
|
|
86
|
+
raise RuntimeError(
|
|
87
|
+
"Task: __poll__ after Ready was already consumed")
|
|
88
|
+
self.has_result = False
|
|
89
|
+
return poll_ready(self.result.take0())
|
|
90
|
+
if self.executor_owned:
|
|
91
|
+
self.awaiter = w
|
|
92
|
+
return poll_pending()
|
|
93
|
+
# Non-executor-owned: drive the frame directly.
|
|
94
|
+
frame = self.frame
|
|
95
|
+
if frame is None:
|
|
96
|
+
raise RuntimeError("Task: __poll__ on empty TaskState")
|
|
97
|
+
try:
|
|
98
|
+
p = frame.get().__poll__(w)
|
|
99
|
+
if p.is_ready():
|
|
100
|
+
self.done = True
|
|
101
|
+
return p
|
|
102
|
+
except BaseException as e:
|
|
103
|
+
self.done = True
|
|
104
|
+
self.exc = Box(e.clone())
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
# AnyTask interface: drives the frame and caches result/exc. Used
|
|
108
|
+
# by the executor's slot table via the TaskStateView adapter.
|
|
109
|
+
def poll_any(self, w: Waker) -> bool:
|
|
110
|
+
if self.done:
|
|
111
|
+
return True
|
|
112
|
+
frame = self.frame
|
|
113
|
+
if frame is None:
|
|
114
|
+
return True
|
|
115
|
+
try:
|
|
116
|
+
p = frame.get().__poll__(w)
|
|
117
|
+
if p.is_ready():
|
|
118
|
+
self.done = True
|
|
119
|
+
self.result.init0(p.value())
|
|
120
|
+
self.has_result = True
|
|
121
|
+
self.awaiter.wake()
|
|
122
|
+
return True
|
|
123
|
+
return False
|
|
124
|
+
except BaseException as e:
|
|
125
|
+
self.done = True
|
|
126
|
+
self.exc = Box(e.clone())
|
|
127
|
+
self.awaiter.wake()
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
def cancel_any(self) -> None:
|
|
131
|
+
if self.done:
|
|
132
|
+
return
|
|
133
|
+
frame = self.frame
|
|
134
|
+
if frame is not None:
|
|
135
|
+
frame.get().cancel()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# Adapter that exposes a TaskState[T] through the non-generic AnyTask
|
|
139
|
+
# protocol. Holds an Rc clone of the same TaskState; the executor's
|
|
140
|
+
# slot table holds Box[AnyTask] wrapping this view.
|
|
141
|
+
@nocopy
|
|
142
|
+
class TaskStateView[T]:
|
|
143
|
+
state: Rc[TaskState[T]]
|
|
144
|
+
|
|
145
|
+
def __init__(self, state: Own[Rc[TaskState[T]]]) -> None:
|
|
146
|
+
self.state = state
|
|
147
|
+
|
|
148
|
+
def poll_any(self, w: Waker) -> bool:
|
|
149
|
+
return self.state.get().poll_any(w)
|
|
150
|
+
|
|
151
|
+
def cancel_any(self) -> None:
|
|
152
|
+
self.state.get().cancel_any()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@nocopy
|
|
156
|
+
class Task[T]:
|
|
157
|
+
"""Type-erased async task. Holds a `Rc[TaskState[T]]` shared with
|
|
158
|
+
the executor's slot table for spawned tasks.
|
|
159
|
+
|
|
160
|
+
Regular @nocopy generic-class machinery: the C++ name
|
|
161
|
+
(`::tpystd::asyncio::_executor::Task<T>`) is derived from this
|
|
162
|
+
module's `# tpy: cpp_namespace` directive through
|
|
163
|
+
`NominalType._fallback_cpp_base_name`, and awaitability comes from
|
|
164
|
+
the `__poll__(self, w: Waker) -> Poll[T]` method below being
|
|
165
|
+
structurally matched by `_extract_awaitable_inner` in
|
|
166
|
+
`tpyc/sema/expressions.py`.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
_state: Rc[TaskState[T]]
|
|
170
|
+
# Default-constructed (null awaker) for non-executor-owned tasks
|
|
171
|
+
# (`task_from_coro`); reassigned by `create_task` to a Waker stamped
|
|
172
|
+
# with the spawn slot's (slot_id, generation). `cancel()` calls
|
|
173
|
+
# `wake()` on it so the slot is marked runnable promptly -- the
|
|
174
|
+
# in-flight frame observes the cancel flag on its next poll instead
|
|
175
|
+
# of waiting on a timer / IO wake. Wake on a null-awaker Waker is a
|
|
176
|
+
# safe no-op (see Waker.wake in tpy.coro), so the non-executor-
|
|
177
|
+
# owned case stays unchanged.
|
|
178
|
+
#
|
|
179
|
+
# Lifetime: this Waker holds a raw `Ptr[Awaker]` into the running
|
|
180
|
+
# `Executor`. A Task[T] handle that survives `asyncio.run`'s scope
|
|
181
|
+
# and is later cancel()'d will dispatch through a dangling pointer
|
|
182
|
+
# -- same invariant `_ExecutorScope` (below) documents for every
|
|
183
|
+
# other stamped Waker. v1 asyncio.run drains spawned tasks during
|
|
184
|
+
# teardown to make this case unreachable in practice.
|
|
185
|
+
_waker: Waker
|
|
186
|
+
|
|
187
|
+
def __init__(self, state: Own[Rc[TaskState[T]]]) -> None:
|
|
188
|
+
self._state = state
|
|
189
|
+
self._waker = Waker()
|
|
190
|
+
|
|
191
|
+
def __poll__(self, w: Waker) -> Own[Poll[T]]:
|
|
192
|
+
return self._state.get().__poll__(w)
|
|
193
|
+
|
|
194
|
+
def cancel(self) -> None:
|
|
195
|
+
self._state.get().cancel_any()
|
|
196
|
+
# Wake routes through the @dynamic Awaker vtable to the
|
|
197
|
+
# executor's mark_runnable. The generation guard there filters
|
|
198
|
+
# late wakes against a completed slot, so this is safe even
|
|
199
|
+
# after the underlying task has already finished.
|
|
200
|
+
self._waker.wake()
|
|
201
|
+
|
|
202
|
+
# Cheap duplication: Task is an Rc handle into the shared TaskState,
|
|
203
|
+
# so clone() just bumps the refcount. Used by asyncio.gather and
|
|
204
|
+
# asyncio.gather_list to take owned task handles without consuming
|
|
205
|
+
# the caller's list/varargs.
|
|
206
|
+
#
|
|
207
|
+
# WARNING (v1.5): the clone aliases the SAME TaskState as `self`.
|
|
208
|
+
# TaskState is single-awaiter (one `awaiter: Waker` slot, one cached
|
|
209
|
+
# result slot consumed on first __poll__ that observes Ready). Two
|
|
210
|
+
# live handles must not be awaited concurrently and must not both
|
|
211
|
+
# consume the result -- only one consumer survives, the other's
|
|
212
|
+
# poll panics or its waker is overwritten. Likewise `cancel()` on
|
|
213
|
+
# either handle propagates through the shared TaskState to BOTH;
|
|
214
|
+
# the clone ALSO inherits the parent's stamped `_waker` (see field
|
|
215
|
+
# comment above), so cancel-via-clone marks the same executor slot
|
|
216
|
+
# runnable -- load-bearing for `_GatherFuture._propagate_cancel`,
|
|
217
|
+
# which cancels its Rc-cloned tasks and relies on the wake landing
|
|
218
|
+
# on the user's original spawn slot. `gather` and `gather_list` are
|
|
219
|
+
# the only safe internal users today: each drives its clones
|
|
220
|
+
# exclusively, and the user is expected to drop their original
|
|
221
|
+
# `tasks[i]` (gather_list) or stop using each positional Task arg
|
|
222
|
+
# (gather) once they've handed off to either entrypoint. A multi-
|
|
223
|
+
# awaiter TaskState is filed in TODO.md.
|
|
224
|
+
def clone(self) -> Own[Task[T]]:
|
|
225
|
+
t = Task[T](self._state.clone())
|
|
226
|
+
t._waker = self._waker
|
|
227
|
+
return t
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def task_from_coro[T](coro: Own[Cancellable[T]]) -> Own[Task[T]]:
|
|
231
|
+
"""Box an awaitable into a heap-allocated Task[T] without
|
|
232
|
+
registering with an executor (no `asyncio.run` required).
|
|
233
|
+
"""
|
|
234
|
+
return _build_task[T](coro, False)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def make_executor_owned_task[T](coro: Own[Cancellable[T]]) -> Own[Task[T]]:
|
|
238
|
+
"""Build a Task[T] flagged `executor_owned=True` (ready to be
|
|
239
|
+
spawned via the executor's slot table)."""
|
|
240
|
+
return _build_task[T](coro, True)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _build_task[T](coro: Own[Cancellable[T]], executor_owned: bool) -> Own[Task[T]]:
|
|
244
|
+
frame = Box[Cancellable[T]](coro)
|
|
245
|
+
state = TaskState[T](frame)
|
|
246
|
+
state.executor_owned = executor_owned
|
|
247
|
+
return Task[T](Rc.new(state))
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def task_to_any_box[T](task: Task[T]) -> Own[Box[AnyTask]]:
|
|
251
|
+
"""Mirror a `Task[T]`'s shared state into a `Box[AnyTask]` for the
|
|
252
|
+
executor's slot table."""
|
|
253
|
+
return Box[AnyTask](TaskStateView[T](task._state.clone()))
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# --- Awaker-side helpers (call sites inside Executor) -------------------
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _make_waker(handle: Awaker, task_id: Int32,
|
|
260
|
+
generation: Int32) -> Waker:
|
|
261
|
+
w = Waker()
|
|
262
|
+
w.awaker = handle
|
|
263
|
+
w.task_id = task_id
|
|
264
|
+
w.generation = generation
|
|
265
|
+
return w
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class TimerEntry:
|
|
269
|
+
"""One entry in the executor's timer min-heap. Ordered by deadline."""
|
|
270
|
+
deadline: float
|
|
271
|
+
waker: Waker
|
|
272
|
+
|
|
273
|
+
def __init__(self, deadline: float, waker: Waker) -> None:
|
|
274
|
+
self.deadline = deadline
|
|
275
|
+
self.waker = waker
|
|
276
|
+
|
|
277
|
+
def __lt__(self, other: 'TimerEntry') -> bool:
|
|
278
|
+
return self.deadline < other.deadline
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@nocopy
|
|
282
|
+
class Slot:
|
|
283
|
+
"""One entry in the executor's slot table.
|
|
284
|
+
|
|
285
|
+
Holds a `Box[AnyTask]` driving a spawned task (None once the task
|
|
286
|
+
completes). The generation counter advances when the slot
|
|
287
|
+
completes, invalidating any stale wakers that were handed out
|
|
288
|
+
before completion -- `Waker.wake()` (in `tpy.coro`) and the
|
|
289
|
+
runnable-drain loop in `Executor.drain_runnable` both check the
|
|
290
|
+
slot's generation and silently drop late wakes.
|
|
291
|
+
|
|
292
|
+
`runnable` mirrors the slot's presence in the executor's runnable
|
|
293
|
+
deque: set true when `mark_runnable` adds the slot's id to the
|
|
294
|
+
queue, cleared when the executor pops it to poll.
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
box: Box[AnyTask] | None
|
|
298
|
+
generation: Int32
|
|
299
|
+
runnable: bool
|
|
300
|
+
|
|
301
|
+
def __init__(self) -> None:
|
|
302
|
+
self.box = None
|
|
303
|
+
self.generation = 0
|
|
304
|
+
self.runnable = False
|
|
305
|
+
|
|
306
|
+
@readonly
|
|
307
|
+
def is_done(self) -> bool:
|
|
308
|
+
return self.box is None
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@nocopy
|
|
312
|
+
class Executor(Awaker):
|
|
313
|
+
"""v1 asyncio executor: runnable-queue + timer-heap driver.
|
|
314
|
+
|
|
315
|
+
Spawned tasks live in indexed slots; `Waker.wake()` marks a slot
|
|
316
|
+
runnable if the slot is still live and the generation matches.
|
|
317
|
+
Timers store the parked task's waker so a fired timer wakes only
|
|
318
|
+
the task that registered it.
|
|
319
|
+
|
|
320
|
+
Inherits `Awaker` so a `Ptr[Awaker]` stamped on each handed-out
|
|
321
|
+
Waker dispatches `mark_runnable` / `register_timer` back to this
|
|
322
|
+
instance through the @dynamic vtable. See `docs/ASYNC_PROGRESS.md`
|
|
323
|
+
for the v1.2 step 7 pivot history.
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
slots: list[Slot]
|
|
327
|
+
runnable_q: list[Int32]
|
|
328
|
+
timer_heap: list[TimerEntry]
|
|
329
|
+
|
|
330
|
+
def __init__(self) -> None:
|
|
331
|
+
# Backstop for `asyncio.run`'s nested-loop check: a non-null
|
|
332
|
+
# current_executor means a `_ExecutorScope` is already active.
|
|
333
|
+
# Bare `Executor()` in unit tests is unaffected because those
|
|
334
|
+
# tests never set the global.
|
|
335
|
+
if _get_current_executor() is not None:
|
|
336
|
+
raise RuntimeError(
|
|
337
|
+
"Executor: another executor is already running "
|
|
338
|
+
"(nested asyncio.run or leaked _ExecutorScope)")
|
|
339
|
+
self.slots = []
|
|
340
|
+
self.runnable_q = []
|
|
341
|
+
self.timer_heap = []
|
|
342
|
+
|
|
343
|
+
def register_timer(self, deadline_seconds: float, waker: Waker) -> None:
|
|
344
|
+
heapq.heappush(self.timer_heap, TimerEntry(deadline_seconds, waker))
|
|
345
|
+
|
|
346
|
+
# Mint a Waker stamped with the given slot identity. Used by
|
|
347
|
+
# `asyncio.create_task` to stash a wake-handle on the Task so its
|
|
348
|
+
# `cancel()` can mark the slot runnable promptly. Lives on Executor
|
|
349
|
+
# rather than as a free function so the call site can pass a
|
|
350
|
+
# method receiver instead of trying to coerce `Ptr[Executor]` to
|
|
351
|
+
# the `Awaker` protocol param of `_make_waker`.
|
|
352
|
+
def make_waker_for_slot(self, slot_id: Int32, generation: Int32) -> Waker:
|
|
353
|
+
return _make_waker(self, slot_id, generation)
|
|
354
|
+
|
|
355
|
+
def spawn(self, box: Own[Box[AnyTask]]) -> Int32:
|
|
356
|
+
new_id = len(self.slots)
|
|
357
|
+
slot = Slot()
|
|
358
|
+
slot.box = box
|
|
359
|
+
slot.runnable = True
|
|
360
|
+
self.slots.append(slot)
|
|
361
|
+
self.runnable_q.append(new_id)
|
|
362
|
+
return new_id
|
|
363
|
+
|
|
364
|
+
def mark_runnable(self, slot_id: Int32, generation: Int32) -> None:
|
|
365
|
+
if slot_id >= len(self.slots):
|
|
366
|
+
return
|
|
367
|
+
slot = self.slots[slot_id]
|
|
368
|
+
if slot.is_done() or slot.generation != generation or slot.runnable:
|
|
369
|
+
return
|
|
370
|
+
slot.runnable = True
|
|
371
|
+
self.runnable_q.append(slot_id)
|
|
372
|
+
|
|
373
|
+
def poll_slot(self, slot_id: Int32) -> bool:
|
|
374
|
+
if slot_id >= len(self.slots):
|
|
375
|
+
return False
|
|
376
|
+
if not self.slots[slot_id].runnable or self.slots[slot_id].is_done():
|
|
377
|
+
return False
|
|
378
|
+
self.slots[slot_id].runnable = False
|
|
379
|
+
gen = self.slots[slot_id].generation
|
|
380
|
+
waker = _make_waker(self, slot_id, gen)
|
|
381
|
+
# poll_any may recursively spawn new tasks which can reallocate
|
|
382
|
+
# the slots vector. Hold no Slot reference across the call --
|
|
383
|
+
# re-index after it returns. The AnyTask object lives on the
|
|
384
|
+
# heap and is stable; only the vector storage moves.
|
|
385
|
+
box = self.slots[slot_id].box
|
|
386
|
+
if box is None:
|
|
387
|
+
return False
|
|
388
|
+
if box.get().poll_any(waker):
|
|
389
|
+
self.slots[slot_id].box = None
|
|
390
|
+
self.slots[slot_id].generation += 1
|
|
391
|
+
return True
|
|
392
|
+
|
|
393
|
+
def drain_runnable(self) -> bool:
|
|
394
|
+
any_polled = False
|
|
395
|
+
# TODO(async-v1.2): `list.pop(0)` is O(n); draining N runnable tasks costs
|
|
396
|
+
# O(N^2). Swap `runnable_q` to `collections.deque[Int32]` and use
|
|
397
|
+
# `popleft()` once deque lands in TPy stdlib. See BUGS.md entry on
|
|
398
|
+
# runnable_q O(n) pop.
|
|
399
|
+
while len(self.runnable_q) > 0:
|
|
400
|
+
slot_id = self.runnable_q.pop(0)
|
|
401
|
+
if self.poll_slot(slot_id):
|
|
402
|
+
any_polled = True
|
|
403
|
+
return any_polled
|
|
404
|
+
|
|
405
|
+
@readonly
|
|
406
|
+
def slot_done(self, slot_id: Int32) -> bool:
|
|
407
|
+
if slot_id >= len(self.slots):
|
|
408
|
+
return False
|
|
409
|
+
return self.slots[slot_id].is_done()
|
|
410
|
+
|
|
411
|
+
@readonly
|
|
412
|
+
def has_live_tasks(self, skip_id: Int32) -> bool:
|
|
413
|
+
n = len(self.slots)
|
|
414
|
+
i: Int32 = 0
|
|
415
|
+
while i < n:
|
|
416
|
+
if i != skip_id and not self.slots[i].is_done():
|
|
417
|
+
return True
|
|
418
|
+
i += 1
|
|
419
|
+
return False
|
|
420
|
+
|
|
421
|
+
def wait_for_event(self) -> bool:
|
|
422
|
+
if len(self.timer_heap) == 0:
|
|
423
|
+
return False
|
|
424
|
+
sleep_until_steady(self.timer_heap[0].deadline)
|
|
425
|
+
now = monotonic()
|
|
426
|
+
while len(self.timer_heap) > 0 and self.timer_heap[0].deadline <= now:
|
|
427
|
+
entry = heapq.heappop(self.timer_heap)
|
|
428
|
+
entry.waker.wake()
|
|
429
|
+
return True
|
|
430
|
+
|
|
431
|
+
def run_until(self, main_id: Int32) -> None:
|
|
432
|
+
while True:
|
|
433
|
+
if self.slot_done(main_id):
|
|
434
|
+
return
|
|
435
|
+
if self.drain_runnable():
|
|
436
|
+
continue
|
|
437
|
+
if self.slot_done(main_id):
|
|
438
|
+
return
|
|
439
|
+
if not self.wait_for_event():
|
|
440
|
+
raise RuntimeError(
|
|
441
|
+
"asyncio.run: no progress possible (coroutine "
|
|
442
|
+
"returned Pending with no pending timers; v1 has "
|
|
443
|
+
"no I/O reactor)")
|
|
444
|
+
|
|
445
|
+
def drain_spawned_with_cancel(self, skip_id: Int32,
|
|
446
|
+
max_polls: Int32 = 8) -> None:
|
|
447
|
+
n = len(self.slots)
|
|
448
|
+
i: Int32 = 0
|
|
449
|
+
while i < n:
|
|
450
|
+
if i != skip_id and not self.slots[i].is_done():
|
|
451
|
+
box = self.slots[i].box
|
|
452
|
+
if box is not None:
|
|
453
|
+
box.get().cancel_any()
|
|
454
|
+
i += 1
|
|
455
|
+
attempt: Int32 = 0
|
|
456
|
+
while attempt < max_polls and self.has_live_tasks(skip_id):
|
|
457
|
+
j: Int32 = 0
|
|
458
|
+
n2 = len(self.slots)
|
|
459
|
+
while j < n2:
|
|
460
|
+
if j != skip_id and not self.slots[j].is_done():
|
|
461
|
+
self.mark_runnable(j, self.slots[j].generation)
|
|
462
|
+
j += 1
|
|
463
|
+
if not self.drain_runnable():
|
|
464
|
+
break
|
|
465
|
+
attempt += 1
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
# --- Current-executor global + helpers ---------------------------------
|
|
469
|
+
# Defined after `Executor` so signatures can spell the concrete
|
|
470
|
+
# `Ptr[Executor]` without a forward-reference string. Plain (non-
|
|
471
|
+
# thread-local) global; v1 asyncio is single-executor per process.
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
_current_executor: Ptr[Executor] = None
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _get_current_executor() -> Ptr[Executor]:
|
|
478
|
+
return _current_executor
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def _set_current_executor(handle: Ptr[Executor]) -> None:
|
|
482
|
+
global _current_executor
|
|
483
|
+
_current_executor = handle
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def _clear_current_executor() -> None:
|
|
487
|
+
global _current_executor
|
|
488
|
+
_current_executor = None
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
# Test-only Box[AnyTask] factory: builds a TaskState[T] without
|
|
492
|
+
# executor registration so tests can drive the executor directly.
|
|
493
|
+
def _make_any_task_for_test[T](coro: Own[Cancellable[T]]) -> Own[Box[AnyTask]]:
|
|
494
|
+
task = make_executor_owned_task[T](coro)
|
|
495
|
+
return task_to_any_box[T](task)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
@nocopy
|
|
499
|
+
class _ExecutorScope:
|
|
500
|
+
"""RAII guard for the `_current_executor` module global. Stores
|
|
501
|
+
the executor pointer on construction; clears it on `__del__`.
|
|
502
|
+
|
|
503
|
+
Wakers stamped against this executor that fire after teardown still
|
|
504
|
+
hold a (now-stale) `Ptr[Awaker]` -- that's a use-after-free at the
|
|
505
|
+
raw-pointer level. v1 asyncio.run drains all spawned tasks during
|
|
506
|
+
teardown to make this case unreachable in practice; multi-threaded
|
|
507
|
+
async (v3+) would need a per-Waker generation guard or a refcounted
|
|
508
|
+
Awaker handle.
|
|
509
|
+
"""
|
|
510
|
+
|
|
511
|
+
def __init__(self, executor: Executor) -> None:
|
|
512
|
+
_set_current_executor(executor)
|
|
513
|
+
|
|
514
|
+
def __del__(self) -> None:
|
|
515
|
+
_clear_current_executor()
|