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,1258 @@
|
|
|
1
|
+
# Python Stdlib Coverage
|
|
2
|
+
|
|
3
|
+
Tracks TPy's coverage of the CPython standard library. This is a living
|
|
4
|
+
document -- update it whenever a stdlib module or item gains/loses support.
|
|
5
|
+
|
|
6
|
+
**Scope**: modules present in CPython's `stdlib` plus the implicit `builtins`
|
|
7
|
+
module (len, print, list, dict, exceptions, etc.). TPy-native extras
|
|
8
|
+
(`tplib.Box`, `tplib.ArrayList`, `tplib.FixStr`, `tplib.json`) are tracked in
|
|
9
|
+
`LANGUAGE_FEATURES.md` since they don't have CPython equivalents.
|
|
10
|
+
|
|
11
|
+
Status legend:
|
|
12
|
+
- **Done** -- matches CPython semantics for the items listed below
|
|
13
|
+
- **Partial** -- usable; some items missing or differ from CPython
|
|
14
|
+
- **Stub** -- minimal scaffolding, most items missing
|
|
15
|
+
- **Missing** -- not started
|
|
16
|
+
- **Blocked** -- needs a language/runtime feature before meaningful work
|
|
17
|
+
|
|
18
|
+
Priority reflects impact for typical Python programs (P0 = essential for
|
|
19
|
+
most real programs; P3 = rarely needed / specialized).
|
|
20
|
+
|
|
21
|
+
Approach legend: **native** (thin `@native` bindings to libc / OS / existing
|
|
22
|
+
C++ libraries), **pure** (pure TPy), **macro** (compile-time macro module),
|
|
23
|
+
**mixed** (combination).
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Implementation Policy
|
|
28
|
+
|
|
29
|
+
Stdlib modules should be written in **pure TPy (.py)** by default. C++ code
|
|
30
|
+
is introduced only where it genuinely must be:
|
|
31
|
+
|
|
32
|
+
1. **OS / libc / syscalls** -- wall-clock time, filesystem, sockets, process
|
|
33
|
+
spawning, memory maps. There is no TPy-level equivalent.
|
|
34
|
+
2. **Existing C++ library bindings** -- regex engines (std::regex, PCRE2),
|
|
35
|
+
crypto (OpenSSL), compression (zlib), etc. We wrap, we don't rewrite.
|
|
36
|
+
3. **Performance-critical inner loops where TPy generation is demonstrably
|
|
37
|
+
worse.** Must be justified with a benchmark; not a default assumption.
|
|
38
|
+
|
|
39
|
+
Everything else is pure TPy. If the language is missing something that blocks
|
|
40
|
+
a clean pure-TPy implementation -- closures, generators, a specific dunder,
|
|
41
|
+
module-level mutable state, `*args`/`**kwargs`, runtime type info -- the
|
|
42
|
+
**right fix is to extend the language**, not to drop into C++. A stdlib that
|
|
43
|
+
hits language gaps is the best possible driver for language work, because
|
|
44
|
+
each gap has a concrete user-visible payoff.
|
|
45
|
+
|
|
46
|
+
For modules that do need native code, follow the **thin-binding pattern**:
|
|
47
|
+
import raw symbols via `@native` into a minimal module (e.g.
|
|
48
|
+
`tplib.cppstd.re`, `tpy._os.libc`), then build the CPython-compatible surface
|
|
49
|
+
as pure-TPy wrappers on top. The native layer should expose the underlying
|
|
50
|
+
primitive faithfully -- no Python semantics baked in -- and the pure-TPy
|
|
51
|
+
layer translates to Python semantics (error handling, optional args,
|
|
52
|
+
defaults, naming, iteration protocols). Benefits:
|
|
53
|
+
|
|
54
|
+
- One place to swap backends (e.g. std::regex -> PCRE2) without touching
|
|
55
|
+
user-visible code.
|
|
56
|
+
- CPython stubs in `lib/cpy/` can mirror the pure-TPy wrappers directly --
|
|
57
|
+
only the bottom layer differs.
|
|
58
|
+
- Type checker, IDE, and LLMs see real Python code, not opaque C++ symbols.
|
|
59
|
+
- Macro-based modules can inspect the pure-TPy layer.
|
|
60
|
+
|
|
61
|
+
### C++ helper convention
|
|
62
|
+
|
|
63
|
+
When a stdlib module genuinely needs a small C++ helper beyond raw libc /
|
|
64
|
+
`std::` calls -- e.g. wrappers that return `std::tuple` to bridge CPython's
|
|
65
|
+
multi-return APIs (`math.modf`, `math.frexp`) or that compose a handful of
|
|
66
|
+
`std::` calls into a Python-semantics primitive (`math.ulp`) -- place it in
|
|
67
|
+
`runtime/cpp/include/tpy/stdlib/<module>.hpp` under namespace
|
|
68
|
+
`tpy::stdlib::<module>`. Example: `tpy/stdlib/math.hpp` defines
|
|
69
|
+
`tpy::stdlib::math::modf(...)`.
|
|
70
|
+
|
|
71
|
+
This keeps stdlib-backing helpers cleanly separated from TPy's own runtime
|
|
72
|
+
core (`tpy::` at the top level, covering `tpy::BigInt`, `tpy::ordered_map`,
|
|
73
|
+
`tpy::varargs`, etc.). Path mirrors namespace, standard C++ convention, and
|
|
74
|
+
future modules slot in predictably: `tpy/stdlib/random.hpp` ->
|
|
75
|
+
`tpy::stdlib::random`, `tpy/stdlib/time.hpp` -> `tpy::stdlib::time`, and so
|
|
76
|
+
on.
|
|
77
|
+
|
|
78
|
+
Complementary Python-side convention (already in use): stdlib `.py` modules
|
|
79
|
+
declare `# tpy: cpp_namespace("tpystd::<module>")` so the generated code for
|
|
80
|
+
the pure-TPy wrappers lives under `tpystd::math::`, `tpystd::bisect::`, etc.
|
|
81
|
+
Three layers, three namespaces, all distinct:
|
|
82
|
+
|
|
83
|
+
| Layer | Namespace | Example |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| Raw C/C++ primitives | `std::` (or third-party) | `std::log`, `std::modf` |
|
|
86
|
+
| TPy-authored C++ helpers | `tpy::stdlib::<module>::` | `tpy::stdlib::math::modf` |
|
|
87
|
+
| Generated code for pure-TPy wrappers | `tpystd::<module>::` | `tpystd::math::gcd` |
|
|
88
|
+
|
|
89
|
+
The `approach` column in the overview table below reflects the **public-facing**
|
|
90
|
+
strategy. Internally nearly every module ends up "mixed" if it touches the OS --
|
|
91
|
+
the distinction is whether the Python-visible logic lives in .py or C++.
|
|
92
|
+
|
|
93
|
+
### Test location convention
|
|
94
|
+
|
|
95
|
+
Stdlib-module tests live under `tests/cases/stdlib/<module>/` -- one directory
|
|
96
|
+
per CPython module, matching the import name (`stdlib/bisect`, `stdlib/heapq`,
|
|
97
|
+
`stdlib/math/...` for grouped subcases, etc.). This keeps the stdlib surface
|
|
98
|
+
discoverable as a group (`pytest -k stdlib/`) and separates it from feature
|
|
99
|
+
tests under `cases/<feature>/`. Earlier stdlib tests that ended up under
|
|
100
|
+
`cases/imports/` or `cases/builtins/` are being migrated as they're touched.
|
|
101
|
+
|
|
102
|
+
Examples of the policy in action:
|
|
103
|
+
|
|
104
|
+
- `bisect` -- fully pure TPy over the `Comparable` protocol. No native code.
|
|
105
|
+
- `math` -- thin `@native` bindings to `std::log`, `std::sqrt`, etc. (libc
|
|
106
|
+
math is the primitive); no TPy-visible C++ logic beyond the bindings.
|
|
107
|
+
`log(x, base)`, `radians`, `degrees` are pure-TPy overloads/wrappers.
|
|
108
|
+
- `random` -- currently thin binds to `std::rand`; long-term should be a
|
|
109
|
+
pure-TPy Mersenne Twister (matches CPython), with only `os.urandom`-style
|
|
110
|
+
entropy as the native primitive.
|
|
111
|
+
- `re` -- thin `tplib.cppstd.re` / `_bindings.pcre2.re` binding (the regex engine
|
|
112
|
+
is the primitive), with a pure-TPy facade for the Python surface and a
|
|
113
|
+
pure-TPy syntax translator.
|
|
114
|
+
- `pathlib` -- pure TPy over thin filesystem-syscall bindings.
|
|
115
|
+
- `json` (stdlib-compat) -- pure TPy on top of `tplib.json`'s parser/writer.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Module Overview
|
|
120
|
+
|
|
121
|
+
| Module | Priority | Status | % | Approach | Blockers / Notes |
|
|
122
|
+
|---|---|---|---|---|---|
|
|
123
|
+
| [`builtins`](#builtins) | P0 | Partial | ~75% | mixed | Implicit import. Core types + most common functions + most exception types present and catchable (`Index/Key/Value/Type/Attribute/Assertion/OS/FileNotFound/ZeroDivision/Overflow/Arithmetic/Runtime/NotImplemented/Memory/StopIteration`); fixed-int arithmetic overflow stays panic by design (future policy switch). Missing: `frozenset`, `complex`, `memoryview`, `input`, `format`, `ascii`, `callable`, `id`, `type(x)` runtime. D16 dyn-attrs (`getattr`/`setattr`/`delattr`/`hasattr` for both literal and runtime names) fully shipped. See [builtins](#builtins) for per-item status |
|
|
124
|
+
| [`math`](#math) | P0 | Done | ~99% | mixed | Thin libc bindings + pure TPy wrappers. All CPython funcs present with matching signatures (`Iterable[float]` for fsum/sumprod/dist; `prod` has Int32 / int (BigInt) / float overloads). Remaining gap: tuple as iterable (blocked on tuple-iteration bundle) |
|
|
125
|
+
| [`time`](#time) | P0 | Partial | ~50% | mixed | Thin clock/sleep syscalls. `time`, `sleep`, `perf_counter`, `monotonic`, `time_ns`, `perf_counter_ns`, `monotonic_ns`, `process_time` all done. Missing `struct_time`/`strftime`/`gmtime`/`localtime`/timezone constants |
|
|
126
|
+
| [`sys`](#sys) | P0 | Stub | ~15% | mixed | Thin syscall bindings + pure TPy. `argv`, `stdout`, `stderr` done; needs `stdin`/`exit`/`path`/`version_info` |
|
|
127
|
+
| [`os`](#os) | P0 | Missing | 0% | -- | Needs filesystem wrapper + path handling |
|
|
128
|
+
| [`os.path`](#ospath) | P0 | Missing | 0% | -- | Independent of `os`; candidate for pure TPy over C++ `<filesystem>` |
|
|
129
|
+
| [`pathlib`](#pathlib) | P0 | Missing | 0% | -- | Class-heavy; depends on filesystem bindings |
|
|
130
|
+
| [`io`](#io) | P0 | Partial | ~30% | pure | `StringIO` / `BytesIO` (chunked storage, write/read/readline/seek/tell/truncate/iter/context-manager). `Readable` / `Writable` / `BinaryReadable` / `BinaryWritable` protocols on tpy core, re-exported from `io`. Missing: `IOBase` ABC hierarchy (deliberately deferred -- protocols cover the static-dispatch use case), `TextIOWrapper`, `io.SEEK_SET/CUR/END` constants (collide with `<cstdio>` macros), encoding/newline/errors kwargs, `read(size=-1)` arg |
|
|
131
|
+
| [`json`](#json) | P0 | Partial | ~70% | pure | `loads` / `dumps` + `JSONDecodeError` done over a recursive union `JsonValue`. CPython byte-compatible across cpy phase. Missing: `load(fp)` / `dump(obj, fp)` (needs `io`), `JSONEncoder` / `JSONDecoder`, most `dumps`/`loads` kwargs |
|
|
132
|
+
| [`re`](#re) | P0 | Partial | ~50% | pure | Pure-TPy facade over `_bindings.pcre2` raw bindings. PCRE2 vendored under `runtime/cpp/third_party/pcre2/` (5MB) and built bundled by default; `--pcre2={bundled,system,auto}` selects backend. compile/search/match/fullmatch/findall/sub/split + Pattern/Match classes + IGNORECASE/MULTILINE/DOTALL/VERBOSE/ASCII flags + `re.error`. Missing: named-group accessors, `count` arg on sub, true generator finditer, bytes input, compile cache |
|
|
133
|
+
| [`collections`](#collections) | P0 | Missing | 0% | -- | OrderedDict trivial (have ordered_map); deque needs C++ struct; Counter/defaultdict/namedtuple need macros |
|
|
134
|
+
| [`itertools`](#itertools) | P0 | Missing | 0% | -- | C++ primitives exist in `runtime/itertools.hpp`; needs Python-surface module |
|
|
135
|
+
| [`functools`](#functools) | P0 | Partial | ~25% | pure | `reduce(func, a, initial)`, `reduce(func, a)`, `total_ordering` done. `cmp_to_key`, `wraps` blocked on specific compiler / macro-infrastructure gaps (see section). partial/lru_cache/singledispatch/cached_property/partialmethod need closures + macros |
|
|
136
|
+
| [`random`](#random) | P1 | Partial | ~90% | pure | Pure-TPy MT19937 + CPython's distribution suite, byte-identical to CPython on the same seed. Done: `Random` class, `random`, `seed(Int32)` (negatives mapped to abs), `seed()` no-arg auto-seed via OS entropy, `getrandbits(k)` for arbitrary k, `randint`, `randrange`, `randbytes`, `choice`, `shuffle`, `uniform`, `triangular`, `gauss`, `normalvariate`, `lognormvariate`, `expovariate`, `paretovariate`, `weibullvariate`, `gammavariate`, `betavariate`, `vonmisesvariate`. Missing: `choices`/`sample`/`SystemRandom`/`binomialvariate`/`getstate` (Tier 3). See module docstring TODOs |
|
|
137
|
+
| [`struct`](#struct) | P1 | Partial | ~60% | macro | unpack/calcsize only; `pack` needs statement-expr or buffer builder |
|
|
138
|
+
| [`bisect`](#bisect) | P1 | Done | 100% | pure | All four functions implemented generically over `Comparable` |
|
|
139
|
+
| [`enum`](#enum) | P1 | Partial | ~50% | macro | Enum/IntEnum/auto; missing functional API, lookup by name/value, iteration |
|
|
140
|
+
| [`dataclasses`](#dataclasses) | P1 | Partial | ~75% | macro | frozen/order/inheritance/asdict/astuple; missing InitVar, __post_init__, replace(), metadata |
|
|
141
|
+
| [`typing`](#typing) | P1 | Partial | ~60% | native | Protocols/Sized/Iterator/TypedDict/Unpack; missing Generic, TypeVar, ParamSpec, ClassVar |
|
|
142
|
+
| [`datetime`](#datetime) | P1 | Missing | 0% | -- | Class-heavy; needs timedelta arithmetic and timezone handling |
|
|
143
|
+
| [`csv`](#csv) | P1 | Missing | 0% | -- | Depends on `io` |
|
|
144
|
+
| [`base64`](#base64) | P1 | Partial | ~95% | pure | Pure-TPy b64/b32/b16 encode+decode + urlsafe/standard variants + altchars=/validate=/casefold=/map01= kwargs + encodebytes/decodebytes. bytes/bytearray/str accepted on decoders (matches CPython). Missing: b85/a85 (rare, separate algorithms); `memoryview` depends on builtin gap |
|
|
145
|
+
| [`hashlib`](#hashlib) | P1 | Partial | ~20% | pure | SHA-256 pure-TPy. MD5/SHA-1/SHA-512 are straight follow-ups (same class pattern, different round functions / endian). BLAKE2/SHA-3 later. Optional OpenSSL backend also later |
|
|
146
|
+
| [`argparse`](#argparse) | P1 | Partial | ~88% | macro | Builder-trace macro (Phase 7); positionals/optional flags, all 7 actions, all 4 nargs, type=int\|float\|str + fixed-width ints + Float32 + custom records via `from_arg` (all 4 nargs + append/extend), choices/required/dest/help/metavar, Optional[T]/Optional[list[T]] for absent flags, list-literal defaults, bare parse_args() reads sys.argv[1:], --help/-h auto-generation, add_help=False opt-out, prog=/usage=/epilog= help customization, subparsers (flat-namespace; per-sub fields land as Optional[T] on the top namespace). Missing: mutually-exclusive groups, argument groups, runtime-derived `prog`, terminal-width help wrap, per-sub `--help` auto-emit, BooleanOptionalAction, parents=, allow_abbrev, fromfile_prefix_chars, custom formatter classes, action=<callable> |
|
|
147
|
+
| [`logging`](#logging) | P2 | Missing | 0% | -- | Module-level state + handler architecture |
|
|
148
|
+
| [`configparser`](#configparser) | P2 | Missing | 0% | -- | Depends on `io` |
|
|
149
|
+
| [`urllib.parse`](#urllibparse) | P2 | Missing | 0% | -- | Pure-TPy candidate; no network dependency |
|
|
150
|
+
| [`heapq`](#heapq) | P2 | Partial | ~90% | pure | Pure TPy over `list[T: Comparable]`. All non-variadic ops done; `merge(*iterables)` unblocked by variadic-in-method-call fix, still needs a generator-based iterator-heads heap |
|
|
151
|
+
| [`copy`](#copy) | P2 | Missing | 0% | -- | `copy()` deep semantics need intrinsic support |
|
|
152
|
+
| [`textwrap`](#textwrap) | P2 | Missing | 0% | -- | Pure TPy candidate |
|
|
153
|
+
| [`decimal`](#decimal) | P2 | Missing | 0% | -- | Large surface; candidate for BigInt-based pure impl or native lib |
|
|
154
|
+
| [`fractions`](#fractions) | P3 | Missing | 0% | -- | Pure TPy over BigInt |
|
|
155
|
+
| [`statistics`](#statistics) | P2 | Missing | 0% | -- | Pure TPy candidate |
|
|
156
|
+
| [`pickle`](#pickle) | P2 | Blocked | 0% | -- | Needs dynamic type info + `io` |
|
|
157
|
+
| [`shelve`](#shelve) | P3 | Blocked | 0% | -- | Needs pickle |
|
|
158
|
+
| [`inspect`](#inspect) | P2 | Blocked | 0% | -- | Needs runtime type/func introspection |
|
|
159
|
+
| [`asyncio`](#asyncio) | P1 | Partial | ~35% | pure | v1: `run`/`sleep`/`create_task`/`Task[T]`/`Future[T]`/`Event`/`CancelledError` + thread-local executor with slot table, runnable deque, timer min-heap, cancel-drain at run-end. v1.5 M5+M6: `async with` (cleanup-only), `async for` + `StopAsyncIteration`. v1.5 M8: `wait_for`/`TimeoutError`. v1.5 M9: `gather(*tasks)` (homogeneous variadic-positional) + `gather_list(tasks)` (homogeneous list shape). Missing: CPython-shape *heterogeneous* variadic `gather[*Ts](*coros) -> tuple[*Ts]` (needs variadic generics + async-def `*args` codegen); I/O reactor (v2); multi-thread (v3+) |
|
|
160
|
+
| [`threading`](#threading) | P1 | Blocked | 0% | -- | Needs threading primitives |
|
|
161
|
+
| [`multiprocessing`](#multiprocessing) | P2 | Blocked | 0% | -- | Needs process spawning + IPC |
|
|
162
|
+
| [`subprocess`](#subprocess) | P1 | Blocked | 0% | -- | Needs process spawning |
|
|
163
|
+
| [`socket`](#socket) | P1 | Blocked | 0% | -- | Needs network primitives |
|
|
164
|
+
| [`http.client`](#httpclient) | P2 | Blocked | 0% | -- | Needs socket + regex |
|
|
165
|
+
| [`urllib.request`](#urllibrequest) | P2 | Blocked | 0% | -- | Needs http |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Cross-cutting Language / Runtime Gaps
|
|
170
|
+
|
|
171
|
+
These unlock multiple stdlib modules. Listed with the modules each would
|
|
172
|
+
unblock.
|
|
173
|
+
|
|
174
|
+
| Gap | Unblocks | Rough effort |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `io.IOBase` protocol + text/binary wrappers (StringIO, BytesIO, TextIOWrapper) | io, json (stdlib), csv, configparser, pickle, shelve | M |
|
|
177
|
+
| Regex engine (std::regex phase 1, PCRE2 phase 2, SRE if needed) | re, argparse quality, urllib | M phase 1, L phase 2 |
|
|
178
|
+
| Compile-time conditional compilation / build profiles (F8) | re backend selection, allocator choice, embedded variants, debug/release | M |
|
|
179
|
+
| Filesystem bindings (wrap C++ `<filesystem>`) | os.path, pathlib, os, shutil | M |
|
|
180
|
+
| async/await + event loop | asyncio (v1 shipped), aiohttp, async generators | XL (v1 done) |
|
|
181
|
+
| Threading primitives (Thread, Lock, Event, Queue) | threading, multiprocessing.dummy, concurrent.futures | XL |
|
|
182
|
+
| Process spawning (fork/exec or std::process) | subprocess, multiprocessing | M-L |
|
|
183
|
+
| Socket primitives | socket, http.client, smtplib, ftplib, urllib.request | L |
|
|
184
|
+
| Runtime type info / reflection for `get_type_hints`, `type(x)`, `isinstance` on concrete | typing runtime, inspect, pickle | L |
|
|
185
|
+
| Closure capture for `partial`/`lru_cache` | functools | S-M (may already work via Callable) |
|
|
186
|
+
|
|
187
|
+
### Existing BUGS.md entries that gate pure-TPy stdlib work
|
|
188
|
+
|
|
189
|
+
Under the implementation policy (pure TPy over thin native bindings), several
|
|
190
|
+
known compiler bugs block clean stdlib modules. These were lower-priority as
|
|
191
|
+
isolated issues but become **stdlib prerequisites** when stdlib development
|
|
192
|
+
ramps up.
|
|
193
|
+
|
|
194
|
+
| Tracker entry | Effect on stdlib | Blocks |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| _open verification, no entry yet_: Module-level mutable state across compilation units. Verified working for regular stdlib modules with a reference-type module-level singleton (e.g. `random`'s shared RNG instance); still needs checking for facade-routed or reassignment-based patterns -- file as a bug if a real failure is reproduced. | Single source of truth for per-process state | `logging` (handlers registry), `sys.path`, `warnings` |
|
|
197
|
+
|
|
198
|
+
The original "stdlib enablement" workstream (variable re-exports through
|
|
199
|
+
native_module facades, init chain propagation, `import pkg.sub` + attribute
|
|
200
|
+
access for variables, `from pkg import submod` namespace binding, qualified
|
|
201
|
+
type names in annotations, always-qualify `@native` refs) shipped on branch
|
|
202
|
+
`stdlib-enablement-imports` plus the earlier `D24 Phase 10` commit -- now
|
|
203
|
+
behaviour-tested under `tests/cases/imports/native_facade_*`,
|
|
204
|
+
`from_pkg_import_submod`, and `import_pkg_sub_attr_var`.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Module Detail
|
|
209
|
+
|
|
210
|
+
Item status legend (per-row): **Done** / **Partial** / **Missing** / **Blocked**.
|
|
211
|
+
|
|
212
|
+
### builtins
|
|
213
|
+
|
|
214
|
+
Implicitly imported. Surface lives in `lib/tpy/tpy/_builtins/` (types,
|
|
215
|
+
functions, exceptions, I/O) and is re-exported by `lib/tpy/builtins.py`.
|
|
216
|
+
|
|
217
|
+
**Types**
|
|
218
|
+
|
|
219
|
+
| Item | Status | Notes |
|
|
220
|
+
|---|---|---|
|
|
221
|
+
| `int` | Done | Arbitrary-precision `BigInt` |
|
|
222
|
+
| `float` | Done | IEEE 754 double |
|
|
223
|
+
| `bool` | Done | |
|
|
224
|
+
| `str` | Done | Context-dependent `std::string` / `std::string_view` |
|
|
225
|
+
| `bytes`, `bytearray` | Done | |
|
|
226
|
+
| `list` | Done | `std::vector<T>` |
|
|
227
|
+
| `dict` | Done | Insertion-ordered `tpy::ordered_map<K, V>` |
|
|
228
|
+
| `set` | Done | Insertion-ordered `tpy::ordered_set<T>` |
|
|
229
|
+
| `tuple` | Done | `std::tuple<...>` |
|
|
230
|
+
| `range` | Done | `Range[T]` |
|
|
231
|
+
| `slice`, `basic_slice` | Done | Three-arg and two-arg slices |
|
|
232
|
+
| `frozenset` | Missing | Immutable set; would be `tpy::ordered_set<T>` with mutation-free surface |
|
|
233
|
+
| `complex` | Missing | Not yet planned; niche |
|
|
234
|
+
| `memoryview` | Missing | `BytesView` exists for bytes-like; general memoryview over any buffer is bigger scope |
|
|
235
|
+
| `type` | Partial | `isinstance(x, T)` works; `type(x)` as a runtime value is not yet supported |
|
|
236
|
+
| `object` | Done | Implicit root |
|
|
237
|
+
| `None` | Done | |
|
|
238
|
+
|
|
239
|
+
**Functions -- numeric and conversion**
|
|
240
|
+
|
|
241
|
+
| Item | Status | Notes |
|
|
242
|
+
|---|---|---|
|
|
243
|
+
| `abs`, `min`, `max`, `sum` | Done | |
|
|
244
|
+
| `pow`, `divmod`, `round` | Done | |
|
|
245
|
+
| `bin`, `hex`, `oct` | Done | |
|
|
246
|
+
| `chr`, `ord` | Done | |
|
|
247
|
+
| `len`, `hash` | Done | |
|
|
248
|
+
|
|
249
|
+
**Functions -- iteration**
|
|
250
|
+
|
|
251
|
+
| Item | Status | Notes |
|
|
252
|
+
|---|---|---|
|
|
253
|
+
| `iter`, `next` | Done | |
|
|
254
|
+
| `all`, `any`, `sorted` | Done | |
|
|
255
|
+
| `enumerate`, `filter`, `map`, `reversed`, `zip` | Done | |
|
|
256
|
+
|
|
257
|
+
**Functions -- introspection / attribute access**
|
|
258
|
+
|
|
259
|
+
| Item | Status | Notes |
|
|
260
|
+
|---|---|---|
|
|
261
|
+
| `isinstance` | Done | Some CPython cases missing -- see TODO.md entries on protocol/union isinstance in ternary expressions and `@runtime_checkable` |
|
|
262
|
+
| `repr` | Done | User-type fallback partial; see BUGS.md "repr() and f-string {} don't work on union types" |
|
|
263
|
+
| `issubclass` | Missing | |
|
|
264
|
+
| `callable` | Missing | Compile-time evaluable under static dispatch |
|
|
265
|
+
| `getattr`, `setattr`, `delattr` (literal-name dynamic-fallback) | Done (D16 v1) | 2-arg literal-name form routes to `__getattr__` / `__setattr__` / `__delattr__`. Declared-member names rejected (use direct attribute access). See `docs/DYNAMIC_ATTRS_DESIGN.md` |
|
|
266
|
+
| `hasattr`, 3-arg `getattr` | Done (D16 v1.5) | Phases 7+8 of D16; lambda IIFE wraps the dunder call in try/catch on `AttributeError`. Zero-cost on the happy path under modern table-based EH |
|
|
267
|
+
| Dynamic-name 2-arg builtins | Done (D16 v1.5 phase 9) | Option A: routes unconditionally to the dunder when the name is a runtime expression. CPython divergence documented in `docs/DYNAMIC_ATTRS_DESIGN.md` divergence #8 |
|
|
268
|
+
| `id` | Missing | `tpy.unsafe.unsafe_address_of` exists as an approximation; `id` semantics differ under static compilation |
|
|
269
|
+
| `type(x)` (runtime value) | Missing | See TODO.md "type(); T = type(x); z = T()" |
|
|
270
|
+
| `vars`, `dir` | Missing | Not meaningful without runtime object introspection |
|
|
271
|
+
| `ascii`, `format` | Missing | f-strings cover most `format` uses |
|
|
272
|
+
|
|
273
|
+
**Functions -- I/O**
|
|
274
|
+
|
|
275
|
+
| Item | Status | Notes |
|
|
276
|
+
|---|---|---|
|
|
277
|
+
| `print` | Done | |
|
|
278
|
+
| `open`, `open_text`, `open_binary` | Done | `TextIO` / `BinaryIO` context managers |
|
|
279
|
+
| `input` | Missing | Needs stdin reader |
|
|
280
|
+
|
|
281
|
+
**Descriptors / class utilities**
|
|
282
|
+
|
|
283
|
+
| Item | Status | Notes |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| `@property` | Done | See LANGUAGE_FEATURES Properties |
|
|
286
|
+
| `@staticmethod` | Done | |
|
|
287
|
+
| `@classmethod` | Open | Tracked in FEATURE_ROADMAP Future Extensions (sugar over `type[T]`) |
|
|
288
|
+
| `super()` | Done | See LANGUAGE_FEATURES |
|
|
289
|
+
|
|
290
|
+
**Dynamic / not-meaningful under AOT**
|
|
291
|
+
|
|
292
|
+
| Item | Status | Notes |
|
|
293
|
+
|---|---|---|
|
|
294
|
+
| `eval`, `exec`, `compile` | Missing | Not meaningful without a runtime interpreter |
|
|
295
|
+
| `globals`, `locals` | Missing | Static compilation; no dict-shaped scope |
|
|
296
|
+
| `__import__` | Missing | Imports resolve at compile time |
|
|
297
|
+
| `breakpoint`, `help` | Missing | N/A |
|
|
298
|
+
|
|
299
|
+
**Exceptions**
|
|
300
|
+
|
|
301
|
+
Exception hierarchy support. Most built-in exception types raised by
|
|
302
|
+
runtime checks are now user-catchable TPy types. Sites that remain panics
|
|
303
|
+
are deliberate (internal invariants, OOM, fixed-int arithmetic overflow);
|
|
304
|
+
see `docs/EXCEPTION_DESIGN.md` for the per-type migration table and the
|
|
305
|
+
helper-API surface.
|
|
306
|
+
|
|
307
|
+
| Item | Status | Notes |
|
|
308
|
+
|---|---|---|
|
|
309
|
+
| `BaseException`, `Exception` | Done | Two-tier exception model |
|
|
310
|
+
| `ValueError` | Done | Catchable. Migrated runtime sites: `list.remove`/`list.index`, `bytearray.remove`, `bytes` value/`negative count`, `str.split`/`bytes.split` empty separator, `str.index`/`str.rindex` substring not found, `float()`/`int()`/`Int*()` parse errors, `slice` step==0, extended-slice assignment size mismatch, `range()` arg-3 zero, `time.sleep` negative, fixed-int + BigInt negative shift, `int(float('nan'))`, `open()` invalid mode, `from_range` size mismatch, codegen-side `__len__()` negative + enum `from_value` invalid value |
|
|
311
|
+
| `OSError`, `FileNotFoundError` | Done | `OSError` also raised by `read()`/`write()`/`flush()`/`readline()`/`readlines()` when the file is opened in the wrong mode. CPython's `io.UnsupportedOperation` (diamond OSError+ValueError) is not reproducible since TPy MI doesn't support diamonds; one-sided divergence (`except OSError` works, `except ValueError` doesn't) |
|
|
312
|
+
| `StopIteration` | Done | |
|
|
313
|
+
| `IndexError` | Done | Catchable. List/array/span/string/bytes/bytearray out-of-range indexing, list/bytearray `pop` empty all throw `IndexError`. Messages match CPython ("list index out of range" etc.) |
|
|
314
|
+
| `KeyError` | Partial | Catchable. Dict `__getitem__` / `__delitem__` / `pop`-no-default missing key, set `remove`-missing / `pop`-empty, TypedDict `total=False` field access on absent value, enum `from_name` (`Color["unknown"]`) all throw `KeyError`. CPython's `str(KeyError(k))` reprs the key (`'missing'`); TPy currently returns the catchall message verbatim -- alignment is a v2 follow-up |
|
|
315
|
+
| `TypeError` | Done | Catchable. `ord(s)`/`Char(s)` length-1 violation, `cast(T, any_val)` typeid mismatch (with demangled type names), `hash(any_val)` on an unhashable contained type. User code can `raise TypeError(...)` anywhere |
|
|
316
|
+
| `AttributeError` | Done | Catchable throw-tier type; raised by user `__getattr__` / `__setattr__` / `__delattr__` bodies. `hasattr` and 3-arg `getattr` wrap the dunder call in try/catch |
|
|
317
|
+
| `ArithmeticError` | Done | Base class of `ZeroDivisionError` and `OverflowError`, matching CPython's hierarchy. `except ArithmeticError` catches either subtype |
|
|
318
|
+
| `ZeroDivisionError` | Done | Catchable. Float `/`/`//`/`%`, fixed-int `//`/`%`, BigInt `//`/`%`, `divmod`. Messages match CPython per operation (`float division by zero`, `integer modulo by zero`, etc.) |
|
|
319
|
+
| `OverflowError` | Done (catchable) + fixed-int overflow stays panic | Catchable: `int(float('inf'))`, BigInt `2 ** huge_value`, user `raise OverflowError`. Fixed-int arithmetic overflow (Int8..Int64, UInt8..UInt64 add/sub/mul/neg/shift/pow/cast/divmod/round) routes through `raise_fixedint_overflow` -- currently panics, future build/module/function-scope policy switch (`action=none/panic/throw`) plugs in without rewriting call sites |
|
|
320
|
+
| `AssertionError` | Done | `assert` failure throws `AssertionError(msg)`; catchable via `try/except` |
|
|
321
|
+
| `RuntimeError` | Done (class-only) | Class exposed for `raise RuntimeError(...)` in user code; no runtime panic sites migrated |
|
|
322
|
+
| `NotImplementedError` | Done (class-only) | Class exposed for `raise NotImplementedError(...)` |
|
|
323
|
+
| `MemoryError` | Done (class-only) | Class exposed; the BigInt OOM panic deliberately stays panic (catching MemoryError is fragile) |
|
|
324
|
+
| `FloatingPointError`, `RecursionError` | Missing | Niche; not currently raised by TPy runtime |
|
|
325
|
+
| `LookupError` | Missing | Base of IndexError/KeyError; not yet exposed |
|
|
326
|
+
| `NameError`, `UnboundLocalError` | Not applicable | Compile-time concerns |
|
|
327
|
+
| `ImportError`, `ModuleNotFoundError` | Not applicable | Import failures are compile-time today |
|
|
328
|
+
| `UnicodeError` and subtypes | Missing | TPy has few encoding-panic sites today |
|
|
329
|
+
| `SystemExit`, `KeyboardInterrupt`, `GeneratorExit` | Missing | Control-flow exceptions; need signal/runtime support |
|
|
330
|
+
| `SystemError` | Missing | Internal-interpreter notion not directly applicable |
|
|
331
|
+
| `EOFError`, `PermissionError` | Missing | I/O error hierarchy follow-ups |
|
|
332
|
+
| `TimeoutError` | Done | Built-in re-export of `tpy::TimeoutError` (inherits `Exception`); raised by `asyncio.wait_for` |
|
|
333
|
+
|
|
334
|
+
**Sentinels**
|
|
335
|
+
|
|
336
|
+
| Item | Status | Notes |
|
|
337
|
+
|---|---|---|
|
|
338
|
+
| `True`, `False` | Done | |
|
|
339
|
+
| `None` | Done | |
|
|
340
|
+
| `NotImplemented` | Missing | Used by `__eq__` etc. to signal "try the reflected op"; TPy's overload dispatch handles this differently |
|
|
341
|
+
| `Ellipsis` (`...`) | Partial | Usable in stub bodies (`def f(): ...`); not a first-class runtime value |
|
|
342
|
+
|
|
343
|
+
Tests: `tests/cases/builtins/` has a broad suite covering the working
|
|
344
|
+
surface (enumerate, filter, map, zip, sorted, hash, abs, bin, hex, oct,
|
|
345
|
+
all, any, sum, divmod, range, and many more).
|
|
346
|
+
|
|
347
|
+
### math
|
|
348
|
+
|
|
349
|
+
Current: `lib/tpy/math.py` -- native C++ wrappers. Sufficient for numerics-heavy code.
|
|
350
|
+
|
|
351
|
+
| Item | Status | Notes |
|
|
352
|
+
|---|---|---|
|
|
353
|
+
| `pi`, `tau`, `e`, `inf`, `nan` | Done | Constants as `Final[float]`. `nan` is `Final[float] = float("nan")` -- the `float(str)` literal forms (`"nan"`, `"inf"`, `"-inf"`, plus case/whitespace variants) fold at codegen to constexpr `std::numeric_limits<double>::quiet_NaN()` / `::infinity()`, bypassing the non-constexpr `tpy::float_from_str` runtime |
|
|
354
|
+
| `log`, `log10`, `log2` | Done | `log(x, base)` is pure-TPy overload |
|
|
355
|
+
| `log1p`, `expm1` | Done | Thin `std::log1p` / `std::expm1` |
|
|
356
|
+
| `sqrt`, `cbrt`, `pow`, `exp`, `exp2` | Done | `cbrt` / `exp2` are Python 3.11+ |
|
|
357
|
+
| `floor`, `ceil`, `trunc` | Done | Return `int` (BigInt) / generic `T` |
|
|
358
|
+
| `sin`, `cos`, `tan` | Done | |
|
|
359
|
+
| `asin`, `acos`, `atan`, `atan2` | Done | |
|
|
360
|
+
| `sinh`, `cosh`, `tanh` | Done | |
|
|
361
|
+
| `asinh`, `acosh`, `atanh` | Done | |
|
|
362
|
+
| `fabs` | Done | |
|
|
363
|
+
| `hypot` | Done | Variadic `hypot(*coords)`; internal `_hypot2` native binding to `std::hypot` + overflow-safe hypot-fold |
|
|
364
|
+
| `radians`, `degrees` | Done | Pure-TPy |
|
|
365
|
+
| `isnan`, `isinf`, `isfinite` | Done | Thin `std::isnan` / `std::isinf` / `std::isfinite` |
|
|
366
|
+
| `copysign` | Done | |
|
|
367
|
+
| `fmod`, `remainder` | Done | C fmod semantics (truncation); IEEE remainder (nearest-even) |
|
|
368
|
+
| `nextafter`, `ldexp`, `fma` | Done | Thin natives; `fma` is CPython 3.13+ (cpy test is no_cpython) |
|
|
369
|
+
| `ulp` | Done | `tpy::stdlib::math::ulp` helper matching CPython edge cases for nan/inf/0 |
|
|
370
|
+
| `modf` | Done | `tpy::stdlib::math::modf` wrapper returning `std::tuple<double, double>` |
|
|
371
|
+
| `frexp` | Done | Generic over the exponent type: `frexp[T](x) -> tuple[float, T]`. Default T is `DefaultInt` (Int32 under default config); users can pick `Int64` or `int` (BigInt) for wider ranges |
|
|
372
|
+
| `gcd`, `lcm` | Done | Variadic `gcd(*ints)` / `lcm(*ints)` over BigInt. Internal `_gcd2` binary helper; `lcm` uses `(a // gcd(a,b)) * b` to keep the intermediate bounded by `max(|a|, |b|)`. Generic-over-int-type is a follow-up (see math.py header) |
|
|
373
|
+
| `factorial` | Done | Pure-TPy over BigInt; raises `ValueError` on negative |
|
|
374
|
+
| `isqrt` | Done | Pure-TPy Newton's method over BigInt; initial guess from `bit_length()` for O(log log n) iteration count |
|
|
375
|
+
| `perm`, `comb` | Done | Pure-TPy over BigInt. `perm` has both one-arg (`perm(n) == factorial(n)`) and two-arg overloads via `@overload`. `comb` is binary only |
|
|
376
|
+
| `isclose` | Done | Pure-TPy; `rel_tol` / `abs_tol` are kw-only to match CPython |
|
|
377
|
+
| `prod` | Done | Pure-TPy; `start` is kw-only to match CPython. Three overloads: `Iterable[Int32]` -> Int32 (fast path), `Iterable[int]` -> int (exact BigInt for arbitrary-precision products), `Iterable[float]` -> float. Kwarg disambiguation lets `prod(empty, start=1.0)` / `start=int(1)` / `start=Int32(1)` pick the right family |
|
|
378
|
+
| `fsum` | Done | Pure-TPy Neumaier compensated summation. Takes `Iterable[float]` |
|
|
379
|
+
| `sumprod` | Done | Pure-TPy; raises `ValueError` on length mismatch via iterator lockstep drive (mirrors CPython's `zip(..., strict=True)`). Takes `Iterable[float]` |
|
|
380
|
+
| `dist` | Done | Pure-TPy Euclidean distance via hypot-fold (overflow-safe for coordinates up to `DBL_MAX`). Takes `Iterable[float]` |
|
|
381
|
+
| `gamma`, `lgamma`, `erf`, `erfc` | Done | Thin natives (`std::tgamma` etc.) |
|
|
382
|
+
|
|
383
|
+
Tests: `math_module`, `math_extended`, `math_log_base`, `math_hyperbolic`,
|
|
384
|
+
`math_numeric`, `math_special`, `math_fma`, `math_frexp_generic`,
|
|
385
|
+
`math_variadic`, `math_iterable`, `panic_sumprod_mismatch` in
|
|
386
|
+
`tests/cases/builtins/`; `float_special_values` in `tests/cases/float/`
|
|
387
|
+
(covers `float("nan"/"inf"/"-inf")` fold).
|
|
388
|
+
|
|
389
|
+
**Remaining gaps to reach 100%:**
|
|
390
|
+
- Tuples as `Iterable[T]`. Tuples don't iterate today in TPy regardless of protocol context -- real CPython compat gap but bundled scope (needs coordinated sema + codegen + runtime story, not just a conformance flag flip). See TODO.md.
|
|
391
|
+
|
|
392
|
+
### time
|
|
393
|
+
|
|
394
|
+
Current: `lib/tpy/time.py` -- native_module. Wall-clock + monotonic +
|
|
395
|
+
process-CPU clocks shipped; calendar / formatting surface deferred.
|
|
396
|
+
|
|
397
|
+
| Item | Status | Notes |
|
|
398
|
+
|---|---|---|
|
|
399
|
+
| `time()` | Done | Seconds since epoch as float |
|
|
400
|
+
| `sleep(s)` | Done | |
|
|
401
|
+
| `perf_counter()` / `monotonic()` | Done | `std::chrono::steady_clock`. CPython's `perf_counter` and `monotonic` share the same underlying clock on POSIX; we mirror that |
|
|
402
|
+
| `perf_counter_ns()` / `monotonic_ns()` / `time_ns()` | Done | Return `Int64`; epoch reasonable through year 2262 (INT64_MAX ns) |
|
|
403
|
+
| `process_time()` | Done | `std::clock() / CLOCKS_PER_SEC`. CPU time, ~1us resolution on Linux glibc (CPython uses `clock_gettime(CLOCK_PROCESS_CPUTIME_ID)` for ns precision -- a future tightening) |
|
|
404
|
+
| `sleep_until_steady(deadline)` | Done | TPy extension (no CPython equivalent). Sleeps until the given `monotonic()`-domain deadline; used by `asyncio`'s timer-heap drain |
|
|
405
|
+
| `struct_time` | Missing | Needs named-tuple-like or @dataclass |
|
|
406
|
+
| `gmtime`, `localtime` | Missing | Depends on struct_time |
|
|
407
|
+
| `strftime`, `strptime` | Missing | Formatting strings; depends on struct_time |
|
|
408
|
+
| `mktime` | Missing | Depends on struct_time |
|
|
409
|
+
| `asctime`, `ctime` | Missing | Depends on struct_time |
|
|
410
|
+
| `timezone`, `altzone`, `tzname` | Missing | Module-level constants |
|
|
411
|
+
|
|
412
|
+
Tests: `time_module`, `time_sleep`, `time_import`, `stdlib/time_clocks`
|
|
413
|
+
(invariants on perf_counter / monotonic / process_time / time_ns since
|
|
414
|
+
absolute timing values are non-deterministic).
|
|
415
|
+
|
|
416
|
+
### sys
|
|
417
|
+
|
|
418
|
+
Current: `lib/tpy/sys.py` -- native; `argv`, `stdout`, `stderr`, `exit`.
|
|
419
|
+
|
|
420
|
+
| Item | Status | Notes |
|
|
421
|
+
|---|---|---|
|
|
422
|
+
| `argv` | Done | List populated at runtime init |
|
|
423
|
+
| `stdout`, `stderr` | Done | Backed by `tpy::StdStream` (wraps `std::cout` / `std::cerr`); satisfy the `Writable` protocol so they work as `print(file=...)` targets and expose `write(str) -> Int32` / `flush()` |
|
|
424
|
+
| `stdin` | Missing | Needs read-side protocol; lower priority than write |
|
|
425
|
+
| `exit(code)` | Done | `Int32` arg lowered to `std::exit(int)` via `tpy::sys_exit`; `[[noreturn]]` |
|
|
426
|
+
| `platform` | Missing | Compile-time constant |
|
|
427
|
+
| `version`, `version_info` | Missing | Already in `tpy.version`; could re-export |
|
|
428
|
+
| `path` | Missing | List; relates to import machinery (TPy resolves at compile time, so semantics differ) |
|
|
429
|
+
| `modules` | Missing | Not meaningful under static compilation |
|
|
430
|
+
| `maxsize` | Missing | `PTRDIFF_MAX` constant |
|
|
431
|
+
| `byteorder` | Missing | Compile-time constant |
|
|
432
|
+
| `getsizeof` | Missing | Hard: sizes differ from CPython (inline fields vs boxed) |
|
|
433
|
+
| `executable` | Missing | `argv[0]` / `/proc/self/exe` |
|
|
434
|
+
|
|
435
|
+
Tests: `sys_argv`, `kwargs_print_file_std`.
|
|
436
|
+
|
|
437
|
+
### os
|
|
438
|
+
|
|
439
|
+
**Missing.** Depends on filesystem + process bindings. When attacking, likely in order:
|
|
440
|
+
|
|
441
|
+
| Item | Status | Notes |
|
|
442
|
+
|---|---|---|
|
|
443
|
+
| `getcwd`, `chdir` | Missing | `std::filesystem::current_path` |
|
|
444
|
+
| `listdir`, `scandir` | Missing | `std::filesystem::directory_iterator` |
|
|
445
|
+
| `mkdir`, `makedirs`, `rmdir`, `removedirs` | Missing | `std::filesystem::create_directory` etc. |
|
|
446
|
+
| `remove`, `rename`, `replace` | Missing | `std::filesystem::remove` / `rename` |
|
|
447
|
+
| `stat`, `lstat` | Missing | Needs `stat_result` struct |
|
|
448
|
+
| `environ`, `getenv`, `putenv` | Missing | `std::getenv` |
|
|
449
|
+
| `path` | Missing | See [os.path](#ospath) |
|
|
450
|
+
| `walk` | Missing | Pure TPy over `scandir` |
|
|
451
|
+
| `fork`, `exec*`, `spawnv*`, `system` | Blocked | Process spawning |
|
|
452
|
+
|
|
453
|
+
### os.path
|
|
454
|
+
|
|
455
|
+
**Missing.** Independent of `os` -- pure TPy over `std::filesystem::path`
|
|
456
|
+
would cover most of it.
|
|
457
|
+
|
|
458
|
+
| Item | Status |
|
|
459
|
+
|---|---|
|
|
460
|
+
| `join`, `split`, `splitext`, `basename`, `dirname` | Missing |
|
|
461
|
+
| `exists`, `isfile`, `isdir`, `islink` | Missing |
|
|
462
|
+
| `abspath`, `realpath`, `normpath`, `relpath` | Missing |
|
|
463
|
+
| `expanduser`, `expandvars` | Missing |
|
|
464
|
+
| `getsize`, `getmtime`, `getatime`, `getctime` | Missing |
|
|
465
|
+
|
|
466
|
+
### pathlib
|
|
467
|
+
|
|
468
|
+
**Missing.** Class-based wrapper around `os.path`; depends on filesystem bindings.
|
|
469
|
+
|
|
470
|
+
| Item | Status |
|
|
471
|
+
|---|---|
|
|
472
|
+
| `Path`, `PurePath` | Missing |
|
|
473
|
+
| Path operators (`/`), `.parent`, `.name`, `.suffix`, `.stem` | Missing |
|
|
474
|
+
| `.read_text`, `.write_text`, `.read_bytes`, `.write_bytes` | Missing |
|
|
475
|
+
| `.glob`, `.rglob`, `.iterdir` | Missing |
|
|
476
|
+
| `.exists`, `.is_file`, `.is_dir`, `.stat`, `.unlink`, `.mkdir` | Missing |
|
|
477
|
+
|
|
478
|
+
### io
|
|
479
|
+
|
|
480
|
+
**Partial.** v1 ships `io.StringIO` + `io.BytesIO` plus six IO protocols
|
|
481
|
+
(`Readable`/`Writable`/`BinaryReadable`/`BinaryWritable`/`Seekable`/`Closable`)
|
|
482
|
+
that consumer code parameterizes over (`def f(fp: Writable)`). Both buffer
|
|
483
|
+
types explicitly inherit the relevant protocols (`StringIO(Writable, Readable,
|
|
484
|
+
Seekable, Closable)`), so conformance is documented at the class header.
|
|
485
|
+
|
|
486
|
+
Storage strategy: `list[str]`/`list[bytes]` chunks. The fast path
|
|
487
|
+
(append-at-end, dominant for the "build payload then `getvalue()`" pattern) is
|
|
488
|
+
O(1) per write and never joins; mid-buffer overwrites collapse the chunks to
|
|
489
|
+
a single contiguous buffer first. `getvalue()` joins lazily and does not
|
|
490
|
+
materialize state.
|
|
491
|
+
|
|
492
|
+
Design choice: no `IOBase` ABC hierarchy. TPy's static dispatch makes the
|
|
493
|
+
runtime introspection that `IOBase` exists for in CPython (e.g. `isinstance(fp,
|
|
494
|
+
IOBase)`) unnecessary; the protocols give zero-overhead dispatch and cleanly
|
|
495
|
+
split text/binary + seek/close concerns. Adding the `IOBase` family later as a
|
|
496
|
+
combined protocol layer (`class IOBase(Closable, Seekable, Protocol): ...`)
|
|
497
|
+
remains an option without breaking the existing granular surface.
|
|
498
|
+
|
|
499
|
+
| Item | Status | Notes |
|
|
500
|
+
|---|---|---|
|
|
501
|
+
| `StringIO(initial="")` | Done | Chunked write/read; `read`/`readline`/`readlines`/`seek`/`tell`/`truncate`/`getvalue`/`flush`/`close`/`__iter__`/`__enter__`/`__exit__` |
|
|
502
|
+
| `BytesIO(initial=None)` | Done | Same surface as `StringIO`, returning `bytes` |
|
|
503
|
+
| `Writable` / `Readable` / `BinaryWritable` / `BinaryReadable` | Done | Protocols on `tpy._core._types`, re-exported from `tpy/__init__.py` and from `io` |
|
|
504
|
+
| `Seekable` / `Closable` | Done | Same; `Seekable` is `seek(pos, whence=0)` + `tell()`; `Closable` is `close()` only (the `closed` property is left out of the protocol but available on the concrete classes) |
|
|
505
|
+
| `seek(pos, whence=0)` | Partial | `whence=0/1/2` accepted as integer literals. `io.SEEK_SET/CUR/END` constants not yet exposed -- the names collide with `<cstdio>` macros; needs `#undef` shim from codegen or namespacing |
|
|
506
|
+
| `seek` past end | Diverges | v1 clamps to `_total`; CPython back-fills with NUL/0. Revisit with a real consumer |
|
|
507
|
+
| `__iter__` (line-by-line) | Done | Generator method yielding `readline()` results until empty |
|
|
508
|
+
| `read(size=-1)` | Missing | v1 reads all remaining; CPython supports `size` arg |
|
|
509
|
+
| `IOBase` / `RawIOBase` / `BufferedIOBase` / `TextIOBase` | Missing (deferred) | Not planned -- protocols cover the static-dispatch use case |
|
|
510
|
+
| `TextIOWrapper` | Missing | Encoding + newline translation; defer until a consumer demands it |
|
|
511
|
+
| `encoding=` / `newline=` / `errors=` kwargs | Missing | v1 doesn't translate text |
|
|
512
|
+
| `UnsupportedOperation` | Missing | Defer until a method needs to raise it (e.g. seek on a non-seekable wrapper) |
|
|
513
|
+
| `open()` | Done | In `builtins` (not in `io`); exposes `TextIO`/`BinaryIO` |
|
|
514
|
+
|
|
515
|
+
Tests: `cases/stdlib/io_stringio_basic`, `cases/stdlib/io_bytesio_basic`,
|
|
516
|
+
`cases/stdlib/io_protocols` (consumer functions parameterized over the four
|
|
517
|
+
protocols, both `StringIO`/`BytesIO` and pass-through wiring).
|
|
518
|
+
|
|
519
|
+
### json
|
|
520
|
+
|
|
521
|
+
**Partial.** `lib/tpy/json.py` is a pure-TPy wrapper over `tplib.json`'s
|
|
522
|
+
`JsonReader` / `JsonWriter`. Untyped values are represented by a recursive
|
|
523
|
+
union alias `JsonValue = None | bool | int | float | str | list[JsonValue] |
|
|
524
|
+
dict[str, JsonValue]` -- ints stay BigInt, floats stay double, no precision
|
|
525
|
+
loss. CPython byte-compatible output for the `loads` / `dumps` surface across
|
|
526
|
+
all primitive types and nested containers (verified via the cpy phase in
|
|
527
|
+
`tests/cases/stdlib/json_*`). For typed deserialization into user records,
|
|
528
|
+
`tplib.json` with the `@model` decorator remains faster.
|
|
529
|
+
|
|
530
|
+
| Item | Status | Notes |
|
|
531
|
+
|---|---|---|
|
|
532
|
+
| `loads(s)` | Done | Returns `Own[JsonValue]`. Raises `JSONDecodeError` on malformed input or trailing data. |
|
|
533
|
+
| `dumps(obj, *, indent, sort_keys)` | Done | `indent` and `sort_keys` kwargs supported. CPython byte-compatible for ASCII. |
|
|
534
|
+
| `JSONDecodeError` | Done | Subclasses `ValueError` (matches CPython). Thrown via normal `try`/`except`. Carries `msg`, `doc`, `pos`, `lineno`, `colno`. |
|
|
535
|
+
| `load(fp)`, `dump(obj, fp)` | Missing | Needs `io` |
|
|
536
|
+
| `JSONEncoder`, `JSONDecoder` | Missing | Extension hooks; not yet implemented |
|
|
537
|
+
| `dumps` kwargs `ensure_ascii`, `separators`, `allow_nan`, `default`, `cls`, `skipkeys` | Missing | Current behavior is `ensure_ascii=False` (raw UTF-8) with CPython default separators |
|
|
538
|
+
| `loads` kwargs `object_hook`, `object_pairs_hook`, `parse_float`, `parse_int`, `parse_constant` | Missing | -- |
|
|
539
|
+
|
|
540
|
+
### re
|
|
541
|
+
|
|
542
|
+
**Partial.** Pure-TPy facade in `lib/tpy/re.py` over raw PCRE2 bindings in
|
|
543
|
+
`lib/tpy/_bindings/pcre2.py`. PCRE2 is vendored at
|
|
544
|
+
`runtime/cpp/third_party/pcre2/` (10.44, ~5MB after stripping `doc/`,
|
|
545
|
+
`testdata/`, and autotools build files; reproducible via
|
|
546
|
+
`scripts/vendor_pcre2.py`) and built into the user binary via the existing
|
|
547
|
+
`BuildLayout.build_cpp_commands` infrastructure -- no separate CMake
|
|
548
|
+
required for `tpyc -x` / `-b`. Backend selection: `tpyc --pcre2=bundled`
|
|
549
|
+
(default), `--pcre2=system` (`-lpcre2-8`), or `--pcre2=auto`. The CMake-
|
|
550
|
+
emit path emits a 3-mode selector into `sources.cmake` so users
|
|
551
|
+
integrating into a larger CMake project can flip via `-DTPY_PCRE2=system`.
|
|
552
|
+
|
|
553
|
+
History: F8 was originally listed as a prerequisite to choose between
|
|
554
|
+
`std::regex` and PCRE2 backends. Reframed as a future enhancement -- v1
|
|
555
|
+
ships PCRE2 only (CPython-quality semantics, mature JIT). cppstd backend
|
|
556
|
+
deferred until embedded targets actually need it.
|
|
557
|
+
|
|
558
|
+
Architecture (no C++ wrapper layer, no pcre2.h in TPy-generated TUs):
|
|
559
|
+
|
|
560
|
+
* `runtime/cpp/include/tpy/stdlib/pcre2_h.hpp` -- hand-written facade
|
|
561
|
+
that mirrors the PCRE2 symbols and types we use (opaque struct
|
|
562
|
+
forward-decls, `extern "C"` function declarations, `PCRE2_SPTR8` /
|
|
563
|
+
`PCRE2_UCHAR8` / `PCRE2_SIZE` typedefs). Deliberately does NOT
|
|
564
|
+
`#include <pcre2.h>` -- pcre2.h's `PCRE2_*` macros would otherwise
|
|
565
|
+
collide with TPy module-level constants of the same name. The vendored
|
|
566
|
+
PCRE2 .c files include the real pcre2.h during their separate
|
|
567
|
+
compilation; the linker resolves our extern "C" declarations to those
|
|
568
|
+
symbols.
|
|
569
|
+
* `lib/tpy/_bindings/pcre2.py` -- pure `@native` 1:1 bindings to `pcre2_*_8`
|
|
570
|
+
C primitives. Mirrors upstream constant names exactly
|
|
571
|
+
(`PCRE2_CASELESS`, `PCRE2_SUBSTITUTE_GLOBAL`, etc.). No Python semantics.
|
|
572
|
+
* `lib/tpy/re.py` -- facade. Pattern + Match classes manage
|
|
573
|
+
`pcre2_code*` / `pcre2_match_data*` lifetimes via `__del__`. All flag
|
|
574
|
+
mapping, group accessors, sub/split/findall logic live here in TPy.
|
|
575
|
+
|
|
576
|
+
| Item | Status | Notes |
|
|
577
|
+
|---|---|---|
|
|
578
|
+
| `compile`, `Pattern` | Done | Pure-TPy class wrapping `Ptr[pcre2.Code]`; JIT-compiled on construct |
|
|
579
|
+
| `search`, `match`, `fullmatch` | Done | Return `Optional[Own[Match]]` |
|
|
580
|
+
| `findall` | Done | List of group-0 strings. Doesn't yet return captures-tuples for grouped patterns (CPython divergence) |
|
|
581
|
+
| `finditer` | Partial | Returns `list[Match]` instead of a generator (deferred to v2) |
|
|
582
|
+
| `sub` | Partial | Global replacement only; CPython's `count` arg deferred. Backref syntax is PCRE2-native (`$1`, `${name}`), not CPython's `\1` -- syntax translator deferred |
|
|
583
|
+
| `split`, `split(maxsplit=)` | Done | |
|
|
584
|
+
| `Match.group(int)`, `start`, `end`, `span` | Done | All returning `Int32` offsets and TPy `str` slices |
|
|
585
|
+
| `Match.groups()` | Partial | Returns `list[str]` instead of tuple (varadic-tuple support pending) |
|
|
586
|
+
| `Match.group("name")`, `groupdict` | Missing | Needs PCRE2 nametable walk |
|
|
587
|
+
| Flags (`IGNORECASE`, `MULTILINE`, `DOTALL`, `VERBOSE`, `ASCII`) | Done | CPython bit values; mapped to upstream `PCRE2_*` flags. `re.A` / `re.I` / `re.M` / `re.S` / `re.X` aliases too |
|
|
588
|
+
| `re.error` | Done | Catchable exception subtype |
|
|
589
|
+
| Named groups `(?P<name>...)` | Done (syntax) | PCRE2 accepts Python's `(?P<name>...)` form natively for back-compat |
|
|
590
|
+
| Backreferences `(?P=name)` in pattern | Done | PCRE2-native |
|
|
591
|
+
| Backreferences in `sub` replacement | Partial | PCRE2 `$1` syntax only; CPython's `\1` needs a translator (deferred) |
|
|
592
|
+
| Lookahead, lookbehind, `\p{...}`, etc. | Done | All PCRE2 features available -- patterns just work |
|
|
593
|
+
| `re.compile` cache | Missing | Needs module-level mutable state (see "stdlib enablement workstream" above) |
|
|
594
|
+
| bytes input | Missing | str only for now |
|
|
595
|
+
|
|
596
|
+
Tests:
|
|
597
|
+
* `cases/stdlib/re_basic/` -- CPython-compatible surface. TPy and
|
|
598
|
+
CPython produce byte-identical output for compile/search/match/
|
|
599
|
+
fullmatch/findall/split/sub-without-backref + all 5 flags + pattern
|
|
600
|
+
features (quantifiers, classes, anchors, backrefs in pattern,
|
|
601
|
+
non-capturing groups) + error class catching. cpy phase enabled.
|
|
602
|
+
* `cases/stdlib/re_pcre2_specific/` -- divergent behaviors isolated:
|
|
603
|
+
sub with PCRE2 `$1`/`$2` backref syntax, `Match.groups()` returning
|
|
604
|
+
`list[str]` vs CPython's `tuple[str, ...]`. Marked `no_cpython`.
|
|
605
|
+
Each divergence is tracked as a `TODO(v2)` in `lib/tpy/re.py`; when
|
|
606
|
+
the syntax translator + varadic-tuple support land, this test folds
|
|
607
|
+
into `re_basic/`.
|
|
608
|
+
|
|
609
|
+
### collections
|
|
610
|
+
|
|
611
|
+
**Missing** as a module. Some building blocks already exist.
|
|
612
|
+
|
|
613
|
+
| Item | Status | Notes |
|
|
614
|
+
|---|---|---|
|
|
615
|
+
| `OrderedDict` | Missing | TPy already uses `tpy::ordered_map` for `dict[K,V]`; this would be a thin alias or subclass |
|
|
616
|
+
| `defaultdict` | Missing | Macro-friendly: store factory, synthesize `__getitem__` |
|
|
617
|
+
| `Counter` | Missing | Pure TPy over `dict[T, int]` |
|
|
618
|
+
| `deque` | Missing | Needs C++ backing (std::deque) with Python-like API |
|
|
619
|
+
| `namedtuple` | Missing | Would be a class macro; could desugar to @dataclass(frozen=True) |
|
|
620
|
+
| `ChainMap` | Missing | Pure TPy over list of dicts |
|
|
621
|
+
| `abc.*` (Sequence, Mapping, ...) | Partial | Some in `typing`; deeper introspection absent |
|
|
622
|
+
|
|
623
|
+
### itertools
|
|
624
|
+
|
|
625
|
+
**Missing** as a Python-surface module, but most primitives exist in
|
|
626
|
+
`runtime/cpp/include/tpy/itertools.hpp`: `chain`, `zip_longest`, `islice`,
|
|
627
|
+
`repeat`, `cycle`, `product`, `combinations`, `permutations`, `groupby`.
|
|
628
|
+
|
|
629
|
+
Existing builtins `enumerate`, `zip`, `reversed`, `map`, `filter` live in
|
|
630
|
+
`itertools.hpp` too (as builtins, not `itertools.*`).
|
|
631
|
+
|
|
632
|
+
| Item | Status | Notes |
|
|
633
|
+
|---|---|---|
|
|
634
|
+
| `count`, `cycle`, `repeat` | Missing | Need Python-visible wrappers |
|
|
635
|
+
| `chain`, `chain.from_iterable` | Missing | Wrapper |
|
|
636
|
+
| `compress`, `dropwhile`, `takewhile`, `filterfalse` | Missing | Pure TPy or wrapper |
|
|
637
|
+
| `islice` | Missing | Wrapper |
|
|
638
|
+
| `starmap`, `tee` | Missing | `tee` tricky (needs buffering) |
|
|
639
|
+
| `zip_longest` | Missing | Wrapper |
|
|
640
|
+
| `product`, `permutations`, `combinations`, `combinations_with_replacement` | Missing | Wrapper |
|
|
641
|
+
| `groupby`, `accumulate`, `pairwise`, `batched` | Missing | Wrapper / pure |
|
|
642
|
+
|
|
643
|
+
Key question: whether the module is pure TPy re-exporting C++ generators
|
|
644
|
+
or `@native` thin shims. The @native qualification gap that previously
|
|
645
|
+
blocked the pure-TPy approach has been resolved (codegen always emits
|
|
646
|
+
`::`-qualified names for `@native` refs).
|
|
647
|
+
|
|
648
|
+
### functools
|
|
649
|
+
|
|
650
|
+
Current: `lib/tpy/functools.py` -- pure-TPy `reduce` (both 2-arg and 3-arg
|
|
651
|
+
forms). Landing the rest is gated on specific compiler fixes tracked in
|
|
652
|
+
BUGS.md / TODO.md, not on macro or closure infrastructure:
|
|
653
|
+
|
|
654
|
+
- **`cmp_to_key`** requires either `copy()` to strip readonly through
|
|
655
|
+
generic `T`, arithmetic on `readonly[FixedInt]`, or an opt-out from the
|
|
656
|
+
unconditional readonly deduction on comparison dunders. See BUGS.md
|
|
657
|
+
"Readonly propagation through generic T blocks storing callable cmp/key".
|
|
658
|
+
|
|
659
|
+
| Item | Status | Notes |
|
|
660
|
+
|---|---|---|
|
|
661
|
+
| `reduce(func, a, initial)` | Done | Pure TPy. Takes `Iterable[T]` -- accepts list literals, `range()`, bound list/iter vars |
|
|
662
|
+
| `reduce(func, a)` | Done | Pure TPy. Takes `list[T]` (random-access; raises on empty). Restricted to list rather than `Iterable[T]` because TPy doesn't have CPython's iter/next + StopIteration pattern |
|
|
663
|
+
| `cmp_to_key` | Blocked | Pure TPy; blocked on readonly-through-generics. See BUGS.md |
|
|
664
|
+
| `total_ordering` | Done | Class macro in `lib/tpy/_functools_macros.py`, re-exported via `lib/tpy/functools.py`. Synthesizes the missing comparison ops from any one of `__lt__` / `__le__` / `__gt__` / `__ge__` plus `__eq__`. Defers synthesis via `ClassInfo.defer_until_macros_complete` so it composes with `@dataclass` regardless of decorator order -- dataclass adds `__eq__` (and optionally `__lt__`/`__le__`/`__gt__`/`__ge__`), total_ordering then picks an anchor and fills in the rest |
|
|
665
|
+
| `wraps`, `update_wrapper` | Blocked | CPython's `@wraps(f)` is a decorator factory (`wraps(f)` returns a decorator that takes the wrapper). TPy macro_api has no "decorator factory that's identity" form; would need new infrastructure separate from class/call/builder macros |
|
|
666
|
+
| `partial` | Missing | Full variadic form needs function-macro or `*args` forwarding on user classes |
|
|
667
|
+
| `partialmethod` | Missing | Descriptor-protocol heavy |
|
|
668
|
+
| `lru_cache`, `cache` | Missing | Decorator must wrap + return a new callable with mutable cache dict; needs function-macro (not supported today) |
|
|
669
|
+
| `singledispatch` | Missing | Runtime dispatch; use `@overload` instead |
|
|
670
|
+
| `cached_property` | Missing | Needs descriptor support |
|
|
671
|
+
|
|
672
|
+
### random
|
|
673
|
+
|
|
674
|
+
Current: `lib/tpy/random.py` -- pure-TPy MT19937 engine. `Random` class
|
|
675
|
+
holds the 624-word state; module-level `random()` / `seed()` /
|
|
676
|
+
`getrandbits()` delegate to a module-level `_inst: Random` singleton
|
|
677
|
+
(auto-seeded from OS entropy at module init, like CPython). Byte-
|
|
678
|
+
identical to CPython's `random._inst.getrandbits(32)` for any Int32
|
|
679
|
+
seed (negatives are mapped to `abs()` to match CPython); verified
|
|
680
|
+
against seeds 42, 1, 7, 99, 12345, -42, INT32_MIN; `cases/stdlib/random`
|
|
681
|
+
and `cases/stdlib/random_seq` exercise both TPy and CPython phases.
|
|
682
|
+
|
|
683
|
+
Target under the policy: the Mersenne Twister state machine itself is **pure
|
|
684
|
+
TPy** (same as CPython's `_randommodule.c` logic, but in .py). The only native
|
|
685
|
+
primitive is an OS entropy source for seeding when no explicit seed is given
|
|
686
|
+
(e.g. `os.urandom` via thin syscall binding). Everything else -- `randint`,
|
|
687
|
+
`choice`, `shuffle`, `sample`, `gauss`, etc. -- is pure TPy over the MT core.
|
|
688
|
+
|
|
689
|
+
**Unblocked (engine landed).** Earlier drafts flagged the MT port as gated
|
|
690
|
+
on the `native_module`-facade bugs ("Variable re-exports through native_module
|
|
691
|
+
facades" / "Init chain doesn't propagate transitively through native_module
|
|
692
|
+
facades" in BUGS.md) and "module-level mutable state across compilation
|
|
693
|
+
units." Verified those don't apply here:
|
|
694
|
+
`random.py` is a regular stdlib module, not a facade, so it compiles to one
|
|
695
|
+
TU with a module-level `_inst: Random` whose state is genuinely shared
|
|
696
|
+
across all importers. The one real limitation -- TPy rejects reassigning a
|
|
697
|
+
module-level reference-type variable -- is sidestepped by in-place
|
|
698
|
+
`_inst._seed(s)` mutation, which is how CPython's `random.seed()` works
|
|
699
|
+
anyway.
|
|
700
|
+
|
|
701
|
+
**Thread safety (deferred).** The module-level `_inst` is shared mutable
|
|
702
|
+
state and NOT thread-safe: concurrent callers can corrupt the 624-word
|
|
703
|
+
vector (double-twist, torn index updates). Currently theoretical -- TPy
|
|
704
|
+
has no threading primitives -- but will need a fix when `threading` lands.
|
|
705
|
+
Three candidate models, each with trade-offs: per-thread `_inst` via
|
|
706
|
+
thread-local storage (muddies `seed()` semantics), lock inside `Random`
|
|
707
|
+
(contention under heavy use), or deprecate module-level helpers in favour
|
|
708
|
+
of explicit `Random()` instances (breaks CPython shorthand). CPython itself
|
|
709
|
+
punts to "use per-thread Random()" in docs and added internal locking in
|
|
710
|
+
free-threaded 3.13+. Decision deferred until the TPy threading model is
|
|
711
|
+
chosen.
|
|
712
|
+
|
|
713
|
+
Sketch:
|
|
714
|
+
|
|
715
|
+
class Random:
|
|
716
|
+
_state: Array[UInt32, 624]
|
|
717
|
+
_index: UInt32
|
|
718
|
+
|
|
719
|
+
def __init__(self, seed_value: UInt32 | None = None) -> None: ...
|
|
720
|
+
def _seed(self, s: UInt32) -> None: ... # init_by_array
|
|
721
|
+
def _genrand_uint32(self) -> UInt32: ... # MT step + twist
|
|
722
|
+
def random(self) -> float: ... # genrand_res53
|
|
723
|
+
def choice[T](self, seq: list[T]) -> T: ...
|
|
724
|
+
def shuffle[T](self, seq: list[T]) -> None: ...
|
|
725
|
+
# randint, gauss, ... as methods
|
|
726
|
+
|
|
727
|
+
_inst: Random = Random() # auto-seeds via _os_entropy_uint32()
|
|
728
|
+
|
|
729
|
+
@overload
|
|
730
|
+
def seed() -> None: _inst._seed(_os_entropy_uint32())
|
|
731
|
+
@overload
|
|
732
|
+
def seed(n: Int32) -> None: ... # negatives -> abs()
|
|
733
|
+
# ...
|
|
734
|
+
|
|
735
|
+
OS entropy primitive shipped as `tpy::stdlib::random::os_entropy_uint32`
|
|
736
|
+
in `runtime/cpp/include/tpy/stdlib/random.hpp`, backed by
|
|
737
|
+
`std::random_device`. Used for the no-arg `seed()` and `Random(None)`
|
|
738
|
+
auto-seed paths. `SystemRandom` reuses the same primitive but is
|
|
739
|
+
deferred pending class-hierarchy decisions (subclass `Random` with
|
|
740
|
+
overrides vs standalone class).
|
|
741
|
+
|
|
742
|
+
Soft gaps for full CPython compat (none gate the core MT port):
|
|
743
|
+
- `choices` / `sample` kwarg iterables (`weights=`, `cum_weights=`, `counts=`)
|
|
744
|
+
ship as `list[float]`/`list[int]` rather than `Iterable[T]`. The list-
|
|
745
|
+
literal-vs-protocol conformance gap that blocked this has been closed
|
|
746
|
+
(see functools/math stdlib updates); remaining work is a mechanical
|
|
747
|
+
signature swap plus mixed-positional+kwarg argument wiring.
|
|
748
|
+
|
|
749
|
+
| Item | Status | Notes |
|
|
750
|
+
|---|---|---|
|
|
751
|
+
| `random()` | Done | Uniform [0, 1); MT19937 `genrand_res53`. Byte-identical to CPython |
|
|
752
|
+
| `seed(a)` | Partial | Int32 (negatives mapped to abs()) and `seed()` no-arg auto-seed via OS entropy. CPython also accepts BigInt / `bytes` / `str` (Tier 3 below) |
|
|
753
|
+
| `getrandbits(k)` | Done | Returns `int` (BigInt). k in [1, 32] uses one MT word; k > 32 concatenates ceil(k/32) words little-endian, matching CPython byte-identical |
|
|
754
|
+
| `randint(a, b)` | Done | Inclusive [a, b]; `b - a + 1` must fit Int32 |
|
|
755
|
+
| `randrange(stop)`, `randrange(start, stop)`, `randrange(start, stop, step)` | Done | Three overloads; step can be negative |
|
|
756
|
+
| `randbytes(n)` | Done | Byte-identical to CPython's `getrandbits(n*8).to_bytes(n, 'little')` for any `n` on any host |
|
|
757
|
+
| `choice(seq)` | Done | Pure-TPy generic `choice[T](seq: list[T]) -> T`. Byte-identical to CPython on the same seed |
|
|
758
|
+
| `shuffle(seq)` | Done | Fisher-Yates / Durstenfeld in place; byte-identical to CPython |
|
|
759
|
+
| `uniform(a, b)` | Done | |
|
|
760
|
+
| `triangular(low=0.0, high=1.0, mode=None)` | Done | |
|
|
761
|
+
| `gauss(mu, sigma)` | Done | Box-Muller with cached second value. Reseed clears cache |
|
|
762
|
+
| `normalvariate(mu, sigma)` | Done | Kinderman-Monahan (distinct stream from `gauss`), matches CPython |
|
|
763
|
+
| `lognormvariate(mu, sigma)` | Done | |
|
|
764
|
+
| `expovariate(lambd)` | Done | |
|
|
765
|
+
| `paretovariate(alpha)` | Done | |
|
|
766
|
+
| `weibullvariate(alpha, beta)` | Done | |
|
|
767
|
+
| `gammavariate(alpha, beta)` | Done | Cheng 1977 (alpha>1) + Ahrens-Dieter (0<alpha<1) + exponential (alpha==1) |
|
|
768
|
+
| `betavariate(alpha, beta)` | Done | Composed over `gammavariate` |
|
|
769
|
+
| `vonmisesvariate(mu, kappa)` | Done | Floor-mod workaround for BUGS.md "tpy::fmod uses C semantics" (`%` sign semantics) |
|
|
770
|
+
| `Random` class (per-instance state) | Done | Per-instance 624-word state; `Random(None)` / `Random()` auto-seed from OS entropy |
|
|
771
|
+
| `getstate()`, `setstate(state)` | Missing | Tier 2; CPython tuple shape awkward, `list[UInt32]` variant viable |
|
|
772
|
+
| `choices(pop, weights=, cum_weights=, k=)` | Missing | Tier 3. The `Iterable[T]` conformance gap that previously blocked this is resolved; remaining work is a mechanical signature swap plus weighted-selection wiring |
|
|
773
|
+
| `sample(pop, k, counts=None)` | Missing | Tier 3: same `Iterable[T]` gap + complex algorithm |
|
|
774
|
+
| `binomialvariate(n, p)` | Missing | Tier 3: BTRS state machine; defer until demand |
|
|
775
|
+
| `SystemRandom` class | Missing | Tier 3: OS entropy primitive is wired (`tpy::stdlib::random::os_entropy_uint32`); needs class-hierarchy decisions (subclass `Random` with overrides vs standalone) |
|
|
776
|
+
|
|
777
|
+
Tests: `cases/builtins/random_basic` (existing API smoke test);
|
|
778
|
+
`cases/stdlib/random` (MT engine byte-identity with CPython + per-instance
|
|
779
|
+
Random + singleton isolation, `cpy` phase enabled);
|
|
780
|
+
`cases/stdlib/random_seq` (choice/shuffle/getrandbits k>32/seed(negative)
|
|
781
|
+
byte-identity); `cases/stdlib/random_autoseed` (entropy-driven `seed()` /
|
|
782
|
+
`Random(None)`, `no_cpython` since entropy is non-deterministic).
|
|
783
|
+
|
|
784
|
+
### struct
|
|
785
|
+
|
|
786
|
+
Current: `lib/tpy/struct.py` -- macro module. Format string must be literal.
|
|
787
|
+
|
|
788
|
+
| Item | Status | Notes |
|
|
789
|
+
|---|---|---|
|
|
790
|
+
| `unpack(fmt, data)`, `unpack_from` | Done | Little-endian only; big-endian emits MacroError |
|
|
791
|
+
| `calcsize(fmt)` | Done | Compile-time constant |
|
|
792
|
+
| `pack(fmt, *values)`, `pack_into` | Missing | Needs statement-expr or buffer-builder pattern (see module docstring) |
|
|
793
|
+
| `iter_unpack` | Missing | |
|
|
794
|
+
| `Struct` class | Missing | Would need per-class macro |
|
|
795
|
+
| Big-endian byte order (`>`, `!`) | Missing | Need `std::byteswap` wrappers in unsafe |
|
|
796
|
+
| Format codes `e` (f16), `P` (ptr), `n`/`N` (ssize_t/size_t) | Missing | |
|
|
797
|
+
|
|
798
|
+
Tests: `struct_unpack`.
|
|
799
|
+
|
|
800
|
+
### bisect
|
|
801
|
+
|
|
802
|
+
**Done.** `lib/tpy/bisect.py` is pure TPy generic over `Comparable`.
|
|
803
|
+
|
|
804
|
+
| Item | Status | Notes |
|
|
805
|
+
|---|---|---|
|
|
806
|
+
| `bisect_left`, `bisect_right`, `insort_left`, `insort_right` | Done | |
|
|
807
|
+
| `bisect`, `insort` | Done | Aliases to `bisect_right` / `insort_right` |
|
|
808
|
+
|
|
809
|
+
Tests: `cases/stdlib/bisect`.
|
|
810
|
+
|
|
811
|
+
### enum
|
|
812
|
+
|
|
813
|
+
Current: `lib/tpy/enum.py` -- macro module.
|
|
814
|
+
|
|
815
|
+
| Item | Status | Notes |
|
|
816
|
+
|---|---|---|
|
|
817
|
+
| `Enum` base class | Done | |
|
|
818
|
+
| `IntEnum` | Done | |
|
|
819
|
+
| `auto()` | Done | |
|
|
820
|
+
| `StrEnum` | Missing | Python 3.11+ |
|
|
821
|
+
| `Flag`, `IntFlag` | Missing | Bitwise semantics |
|
|
822
|
+
| Member iteration (`for m in E`) | Partial | Works at runtime; check exhaustiveness |
|
|
823
|
+
| Lookup by value (`E(1)`) | Missing | |
|
|
824
|
+
| Lookup by name (`E["FOO"]`) | Missing | |
|
|
825
|
+
| `.name`, `.value` attributes | Done | |
|
|
826
|
+
| Functional API (`E = Enum("E", "A B C")`) | Missing | Rarely used |
|
|
827
|
+
| `@unique`, `@verify` decorators | Missing | |
|
|
828
|
+
|
|
829
|
+
Tests: integrated in json_model and enum test group.
|
|
830
|
+
|
|
831
|
+
### dataclasses
|
|
832
|
+
|
|
833
|
+
Current: `lib/tpy/dataclasses.py` -- macro module.
|
|
834
|
+
|
|
835
|
+
| Item | Status | Notes |
|
|
836
|
+
|---|---|---|
|
|
837
|
+
| `@dataclass(frozen, order)` | Done | |
|
|
838
|
+
| `field(default, default_factory)` | Done | |
|
|
839
|
+
| `asdict()`, `astuple()` | Done | Recurse into nested dataclasses, lists, dicts, tuples |
|
|
840
|
+
| `@dataclass(slots)` | N/A | All TPy records use inline storage |
|
|
841
|
+
| `@dataclass(eq=False, repr=False, init=False)` | Missing | Opt-outs |
|
|
842
|
+
| `@dataclass(kw_only)` | Missing | |
|
|
843
|
+
| `__post_init__` | Missing | |
|
|
844
|
+
| `InitVar[T]` | Missing | |
|
|
845
|
+
| `replace(obj, **kw)` | Missing | Would be a call macro |
|
|
846
|
+
| `fields(cls)`, `is_dataclass` | Missing | Needs compile-time or runtime reflection |
|
|
847
|
+
| `field(metadata=...)` | Missing | Currently ignored |
|
|
848
|
+
| `MISSING` sentinel | Missing | |
|
|
849
|
+
|
|
850
|
+
Tests: multiple `tplib/json_model_*` cases exercise @dataclass.
|
|
851
|
+
|
|
852
|
+
### typing
|
|
853
|
+
|
|
854
|
+
Current: `lib/tpy/typing.py` -- re-export from `tpy._typing`.
|
|
855
|
+
|
|
856
|
+
| Item | Status | Notes |
|
|
857
|
+
|---|---|---|
|
|
858
|
+
| `Protocol`, `runtime_checkable` | Partial | Protocols via `Protocol`; `@runtime_checkable` N/A |
|
|
859
|
+
| `Self` | Done | |
|
|
860
|
+
| `overload`, `override` | Done | |
|
|
861
|
+
| `Sized`, `Iterable`, `Iterator`, `Sequence`, `MutableSequence` | Done | |
|
|
862
|
+
| `Optional`, `Final` | Done | |
|
|
863
|
+
| `Callable` | Done | |
|
|
864
|
+
| `Literal` | Done | |
|
|
865
|
+
| `TypedDict`, `Unpack` | Done | |
|
|
866
|
+
| `Union`, `Annotated` | Missing | TPy uses `A \| B` syntax |
|
|
867
|
+
| `Any` | Missing | Type-system gap; would need dynamic dispatch |
|
|
868
|
+
| `TypeVar`, `Generic`, `ParamSpec`, `TypeVarTuple` | Missing | TPy uses PEP 695 `[T]` syntax |
|
|
869
|
+
| `ClassVar` | Missing | |
|
|
870
|
+
| `NewType` | Missing | Could be macro |
|
|
871
|
+
| `cast` | Missing | Open question: explicit upcast syntax |
|
|
872
|
+
| `get_type_hints`, `get_origin`, `get_args` | Missing | Runtime reflection |
|
|
873
|
+
|
|
874
|
+
### datetime
|
|
875
|
+
|
|
876
|
+
**Missing.** Class-heavy; natural fit for @dataclass-style TPy records +
|
|
877
|
+
native conversion helpers. Blocked by nothing architectural; medium effort.
|
|
878
|
+
|
|
879
|
+
| Item | Status |
|
|
880
|
+
|---|---|
|
|
881
|
+
| `date`, `time`, `datetime`, `timedelta`, `tzinfo`, `timezone` | Missing |
|
|
882
|
+
| `date.today`, `datetime.now`, `datetime.utcnow` | Missing |
|
|
883
|
+
| `strftime`, `strptime`, `isoformat`, `fromisoformat` | Missing |
|
|
884
|
+
| `timedelta` arithmetic | Missing |
|
|
885
|
+
|
|
886
|
+
### csv
|
|
887
|
+
|
|
888
|
+
**Missing.** Depends on `io` for `reader`/`writer` accepting file-like objects.
|
|
889
|
+
|
|
890
|
+
### base64
|
|
891
|
+
|
|
892
|
+
Current: `lib/tpy/base64.py` -- pure TPy. CPython-compatible defaults:
|
|
893
|
+
`b64decode(validate=False)` silently skips non-alphabet chars (matching
|
|
894
|
+
CPython's lax MIME-mode behavior); `validate=True` makes any non-alphabet
|
|
895
|
+
char raise `ValueError`. Padding errors always raise.
|
|
896
|
+
|
|
897
|
+
| Item | Status | Notes |
|
|
898
|
+
|---|---|---|
|
|
899
|
+
| `b64encode(data, altchars=None)` | Done | Standard `+/` alphabet; `altchars` builds a custom-alphabet view |
|
|
900
|
+
| `b64decode(data, altchars=None, validate=False)` | Done | `validate=False` default matches CPython |
|
|
901
|
+
| `standard_b64encode`, `standard_b64decode` | Done | Aliases over the standard alphabet |
|
|
902
|
+
| `urlsafe_b64encode`, `urlsafe_b64decode` | Done | `-_` alphabet |
|
|
903
|
+
| `b16encode`, `b16decode(data, casefold=False)` | Done | Hex; uppercase output; `casefold=True` accepts lowercase on decode |
|
|
904
|
+
| `b32encode(data)` | Done | RFC 4648 base32; all five padding remainders covered |
|
|
905
|
+
| `b32decode(data, casefold=False, map01=None)` | Done | Strict by default; `casefold=True` accepts lowercase; `map01` maps `'0'`->`'O'` and `'1'`->`'I'` or `'L'` |
|
|
906
|
+
| `encodebytes(data)` / `decodebytes(data)` | Done | MIME-style 76-char line wrap with trailing `\n`; decode passes through `b64decode` (lax) |
|
|
907
|
+
| `bytes` / `bytearray` inputs | Done | Auto-converted via `__span__` (generated signature is `std::span<const uint8_t>`) |
|
|
908
|
+
| `str` input on decoders (`b64decode`, `standard_b64decode`, `urlsafe_b64decode`, `b32decode`, `b16decode`) | Done | `@overload` delegating through `.encode()`; matches CPython which accepts ASCII str on decoders |
|
|
909
|
+
| `BytesView` input | Partial | Works as C++ span but TPy-level coercion not yet tested |
|
|
910
|
+
| `b85encode`/`b85decode`, `a85encode`/`a85decode` | Missing | Rare; separate ~100-LOC algorithms |
|
|
911
|
+
| `memoryview` input | Blocked | Depends on `memoryview` builtin (see `builtins` section) |
|
|
912
|
+
|
|
913
|
+
Tests: `cases/stdlib/base64`.
|
|
914
|
+
|
|
915
|
+
### hashlib
|
|
916
|
+
|
|
917
|
+
Current: `lib/tpy/hashlib.py` -- pure-TPy FIPS 180-4 port. Only SHA-256
|
|
918
|
+
shipped so far; MD5/SHA-1/SHA-512 are mechanical follow-ups using the
|
|
919
|
+
same class pattern (new H0/K constants, swap the round function,
|
|
920
|
+
little-endian for MD5). Needed new shared primitives to get here:
|
|
921
|
+
|
|
922
|
+
- `add_wrap`/`sub_wrap`/`mul_wrap` on all eight fixed-width int types
|
|
923
|
+
(Int8/16/32/64, UInt8/16/32/64) -- wrapping modular arithmetic,
|
|
924
|
+
analogous to Rust's `wrapping_add`. Signed variants route through the
|
|
925
|
+
unsigned type (`static_cast<intN_t>(static_cast<uintN_t>(a) OP ...)`)
|
|
926
|
+
to get defined wrap without signed-overflow UB.
|
|
927
|
+
- `tpy.bits` module (`rotl32`/`rotr32`/`rotl64`/`rotr64`/`byteswap32`/
|
|
928
|
+
`byteswap64`) wrapping C++20 `std::rotl`/`std::rotr` and C++23
|
|
929
|
+
`std::byteswap`. Re-usable for ciphers, RNGs, `struct`, and
|
|
930
|
+
struct-of-ints packing beyond hashlib.
|
|
931
|
+
|
|
932
|
+
| Item | Status | Notes |
|
|
933
|
+
|---|---|---|
|
|
934
|
+
| `sha256(data=None)` + `SHA256` class | Done | FIPS 180-4 algorithm, verified against NIST vectors + CPython |
|
|
935
|
+
| `.update(data)`, `.digest()`, `.hexdigest()`, `.copy()` | Done | On `SHA256` |
|
|
936
|
+
| `.digest_size`, `.block_size`, `.name` | Done | Instance attrs |
|
|
937
|
+
| `md5`, `MD5` | Missing | Next slice; same pattern with little-endian state |
|
|
938
|
+
| `sha1`, `SHA1` | Missing | Same pattern, 20-byte digest |
|
|
939
|
+
| `sha512`, `SHA512` | Missing | Same pattern, 64-bit words -- needs UInt64 rotate + add_wrap (both already present) |
|
|
940
|
+
| `blake2b`, `blake2s` | Missing | Pure TPy; Python has its own impl too |
|
|
941
|
+
| `sha3_*`, `shake_*` | Missing | Later |
|
|
942
|
+
| `new(name)` dispatcher | Missing | Returns a hash object by name; needs `type(x)`-style dispatch or a dict-of-factories |
|
|
943
|
+
| `algorithms_available` / `algorithms_guaranteed` | Missing | Module-level `set[str]` / `frozenset[str]` |
|
|
944
|
+
| Optional OpenSSL backend | Missing | Future; gated by F8 feature flag. Only justified by a perf-critical use case |
|
|
945
|
+
|
|
946
|
+
Design note: `hashlib.sha256(data=None)` takes `bytes | None = None`
|
|
947
|
+
instead of CPython's `b""` default because TPy sema rejects non-literal
|
|
948
|
+
constant defaults (see BUGS.md "Default parameter value `b\"\"` rejected").
|
|
949
|
+
Callers pass either nothing or bytes;
|
|
950
|
+
behavior matches CPython.
|
|
951
|
+
|
|
952
|
+
Tests: `cases/stdlib/hashlib`.
|
|
953
|
+
|
|
954
|
+
### argparse
|
|
955
|
+
|
|
956
|
+
Current: `lib/tpy/argparse.py` -- builder-trace macro (Phase 7 of the
|
|
957
|
+
macro system; see `docs/MACRO_DESIGN.md`). The compiler walks the
|
|
958
|
+
``ArgumentParser`` builder calls at compile time and synthesizes a
|
|
959
|
+
per-call-site record + parse function, so ``args`` is statically typed.
|
|
960
|
+
|
|
961
|
+
| Item | Status | Notes |
|
|
962
|
+
|---|---|---|
|
|
963
|
+
| Positional arguments | Done | Default str type |
|
|
964
|
+
| Optional flags (`-x` / `--foo`) | Done | One or more aliases per add_argument |
|
|
965
|
+
| `type=int\|float\|str` | Done | int maps to BigInt to match CPython |
|
|
966
|
+
| `default=<literal>` | Done | Scalar literals plus list literals for list-typed actions (append/extend or store + nargs=*/+/<int>) |
|
|
967
|
+
| `const=<literal>` | Done | For `store_const` and `store + nargs='?'` |
|
|
968
|
+
| `action=` | Done | `store` / `store_true` / `store_false` / `count` / `append` / `extend` / `store_const` |
|
|
969
|
+
| `nargs=` | Done | `'?'` / `'*'` / `'+'` / positive int. Variable nargs positionals must be last |
|
|
970
|
+
| `choices=(...)` | Done | Macro-time literal sequence |
|
|
971
|
+
| `required=True` | Done | Optional flags only |
|
|
972
|
+
| `dest=` | Done | Override synthesized record field name |
|
|
973
|
+
| `help=` (data) | Done | Stored at macro time |
|
|
974
|
+
| `metavar=` | Done | Per-arg display name override for usage / help |
|
|
975
|
+
| Optional[T] field for absent flag | Done | When no `default=` and not `required=` |
|
|
976
|
+
| ArgumentParser `description=` | Done | |
|
|
977
|
+
| `--help` / `-h` auto-generation | Done | Pre-rendered help printer + argv prelude that exits via `sys.exit(0)` |
|
|
978
|
+
| `add_help=False` opt-out | Done | Suppresses both the auto printer and the `-h` / `--help` reservation, so users can register their own |
|
|
979
|
+
| `prog=` / `usage=` / `epilog=` | Done | Help-text customization. `prog=` substitutes through usage and the `<prog>: error:` parse-error prefix; `usage=` overrides the auto-generated tail; `epilog=` appends after the options block |
|
|
980
|
+
| **TODO**: runtime-derived `prog` default | Missing | CPython uses `os.path.basename(sys.argv[0])` when `prog=` is omitted; we hardcode `"prog"`. Closing this needs a `basename` helper in the TPy stdlib + switching the help printer from a pre-rendered literal to a runtime template. Tracked in MACRO_DESIGN.md's argparse Future Work |
|
|
981
|
+
| **TODO**: terminal-width-aware help wrap | Missing | Help wraps at a hardcoded 80 cols; CPython argparse uses `shutil.get_terminal_size().columns` at runtime. The cpy-phase test pins `COLUMNS=80` so the comparison is deterministic, but a user running our binary in a 200-col terminal still sees help wrapped at 80 while CPython would wrap at 200. Pairs with the runtime-derived `prog` refactor -- both need the help printer to become a runtime template instead of a pre-rendered literal |
|
|
982
|
+
| `type=Int32 / Int64 / UInt8 / ...` | Done | All eight fixed-width ints accepted; field carries the matching primitive |
|
|
983
|
+
| `type=Float32` | Done | Field carries `Float32` (32-bit), parse uses `tpy::float32_from_str` (no Float64 round-trip). Default literals are wrapped in `Float32(...)` so the field type matches |
|
|
984
|
+
| Subparsers | Done | `add_subparsers()` returns a sub-builder via `@builder_returns`; each `add_parser(name)` returns a sub-builder collecting its own arg specs. Top namespace lays per-sub fields out flat (`Optional[T]` per name, mirroring CPython argparse's Namespace shape) so portable test code reads `args.cmd` / `args.<sub-field>` under both backends. Honored kwargs: `dest=` (top field for the chosen subcommand name; default `"cmd"`), `required=`, `help=` (per-sub help text, rendered in --help). Macro-time errors: double `add_subparsers()`, top parser with positional args + subparsers (regex matcher gap), nested `add_subparsers()` in a sub-parser, sub-parser dest collision with a common arg or with `sp.dest`, same per-sub field name with conflicting types across subs. Limitations: typed-union escape hatch (``args._subcommand: A | B`` for `match`/`case`) intentionally not stored -- synth records carry the `__tpy_builder_` private prefix that user code can't reference, so the union would be unreachable for `match`/`case`. The pre-pass-6 builder-trace move (now landed) lifted the sema phasing wall that previously blocked emitting `@property` forwarders over the union; closing the rest needs reachability for the synth records plus per-sub forwarder emission alongside the flat fields. CPython divergence: TPy preemptively populates every per-sub field as `None` on the top namespace; CPython only sets attributes for the chosen sub. Tests using non-active per-sub fields therefore need `getattr(args, ..., None)` (or skip cpy phase) |
|
|
985
|
+
| `add_mutually_exclusive_group()` | Missing | At-most-one constraint across flags |
|
|
986
|
+
| Custom `type=<T>` via `from_arg` | Done | Duck-typed: any record with `@staticmethod from_arg(s: str) -> Self` can be passed as `type=`. Macro emits `T.from_arg(token)`; string defaults route through `from_arg` (mirrors CPython's "string defaults run through type="). All four nargs shapes plus `action=store/append/extend` work; required positional / required-flag fields land as plain `T` (not `Optional[T]`) via an accumulator + post-loop unwrap. Remaining gaps: `choices=` would have to compare unparsed tokens (CPython compares parsed values), and list defaults (`default=["a","b"]`) diverge from CPython too (CPython leaves list-default elements as raw strings) -- both stay rejected |
|
|
987
|
+
| `parents=`, argument groups, `BooleanOptionalAction`, `allow_abbrev`, `fromfile_prefix_chars`, custom formatter classes, `action=<callable>` | Future | Tier-3; full tier table in MACRO_DESIGN.md's argparse Future Work section |
|
|
988
|
+
| Parse-time error wording still differs from CPython | v1 divergence | Stderr+`sys.exit(2)` shape matches; runtime-derived `prog` (`os.path.basename(sys.argv[0])`) and message phrasing parity (e.g. "the following arguments are required") are Tier 2 |
|
|
989
|
+
|
|
990
|
+
Tests: `cases/argparse/{basic,optional_flags,value_free_actions,list_and_const_actions,choices_required_dest,nargs,empty_parser,positional_nargs_optional,qualified_import,two_parsers_same_module,optional_list_absent,no_argv_uses_sys_argv,fixed_width_types,float32_type,custom_type,custom_type_list,custom_type_positional_nargs,help_basic,help_usage_wrap,explicit_sys_import,prog_epilog,usage_override,metavar,add_help_false,list_default,subparsers_basic,subparsers_common_arg,subparsers_optional,subparsers_help,subparsers_sub_positional,subparsers_sub_required_flag,subparsers_required_unify}` plus `error_argparse_*` cases pinning macro-time validation, `error_subparsers_*` cases (`double_call`, `top_positional`, `nested`, `dest_collide`, `no_subs`, `per_sub_collide_common`, `per_sub_type_conflict`) for subparser-specific validation, and `panic_argparse_*` / `panic_subparsers_*` cases for parse-error stderr+`sys.exit(2)` paths. Help-output cases that pass `prog=` explicitly (`prog_epilog`, `usage_override`, `help_usage_wrap`) run under both backends and assert byte-identical output vs CPython's stdlib argparse; the remaining help cases (`help_basic`, `metavar`) carry `no_cpython.txt` because their hardcoded `"prog"` default differs from CPython's `basename(sys.argv[0])`.
|
|
991
|
+
|
|
992
|
+
### logging
|
|
993
|
+
|
|
994
|
+
**Missing.** Needs module-level mutable state + handler/formatter architecture.
|
|
995
|
+
|
|
996
|
+
### configparser
|
|
997
|
+
|
|
998
|
+
**Missing.** Depends on `io`.
|
|
999
|
+
|
|
1000
|
+
### urllib.parse
|
|
1001
|
+
|
|
1002
|
+
**Missing.** Pure-TPy candidate (no network dependency).
|
|
1003
|
+
|
|
1004
|
+
### heapq
|
|
1005
|
+
|
|
1006
|
+
Current: `lib/tpy/heapq.py` -- pure TPy over `list[T: Comparable]`. Mirrors
|
|
1007
|
+
CPython's algorithm (sift-up/sift-down) line-for-line; heap items ordered by
|
|
1008
|
+
`<`.
|
|
1009
|
+
|
|
1010
|
+
| Item | Status | Notes |
|
|
1011
|
+
|---|---|---|
|
|
1012
|
+
| `heappush`, `heappop` | Done | |
|
|
1013
|
+
| `heapify` | Done | O(n) bottom-up construction |
|
|
1014
|
+
| `heappushpop` | Done | Push then pop in one step |
|
|
1015
|
+
| `heapreplace` | Done | Pop then push in one step |
|
|
1016
|
+
| `nsmallest(n, a)` | Done | Heap-based O(n + k log n). Takes `list[T]` (CPython accepts `Iterable[T]`). Body uses `a.copy()` which requires list semantics; a switch to `Iterable[T]` would need `list(a)` materialization plus a story for the empty-input generic-T case (CPython returns `[]`; TPy would error on T inference). Deferred to when empty-iterable generic inference is resolved |
|
|
1017
|
+
| `nlargest(n, a)` | Done | Sort-based O(n log n). Size-k-heap variant (O(n log k)) is a perf follow-up. Same `list[T]` vs `Iterable[T]` gap as `nsmallest` |
|
|
1018
|
+
| `merge(*iterables, key=None, reverse=False)` | Missing | N-way merge; needs a generator-based iterator-heads heap (variadic-in-method-call gap is now fixed) |
|
|
1019
|
+
| `key=` arg on `nlargest`/`nsmallest` | Missing | Needs `Callable[[T], K: Comparable]` threading; straightforward add once prioritized |
|
|
1020
|
+
|
|
1021
|
+
Design note: CPython's `heapq` operates on any mutable sequence; TPy restricts
|
|
1022
|
+
to `list[T]` for now. Ref-type heaps work because (1) `heappush` / `heappushpop`
|
|
1023
|
+
/ `heapreplace` take `item: Own[T]` -- caller transfers ownership of the new
|
|
1024
|
+
element -- and (2) internal `_siftup` / `_siftdown` reads go through `copy()`
|
|
1025
|
+
(mirroring the `bisect.insort_left` pattern); users get explicit ownership
|
|
1026
|
+
and copy semantics for reference types. T must be copyable -- `@nocopy`
|
|
1027
|
+
element types fail at C++ compile time with a deleted-copy-constructor
|
|
1028
|
+
error today (clean sema diagnostic tracked in `BUGS.md`).
|
|
1029
|
+
|
|
1030
|
+
Tests: `cases/stdlib/heapq`, `cases/stdlib/heapq_ref_type`.
|
|
1031
|
+
|
|
1032
|
+
### copy
|
|
1033
|
+
|
|
1034
|
+
**Missing.** `copy`/`deepcopy` need generic copy intrinsic. TPy has `copy()` builtin
|
|
1035
|
+
for value types; dataclass deep-copy would need macro-driven recursion similar to asdict.
|
|
1036
|
+
|
|
1037
|
+
### textwrap
|
|
1038
|
+
|
|
1039
|
+
**Missing.** Pure-TPy candidate.
|
|
1040
|
+
|
|
1041
|
+
### decimal
|
|
1042
|
+
|
|
1043
|
+
**Missing.** Large surface. Either bind `mpdecimal` or build pure TPy atop BigInt.
|
|
1044
|
+
|
|
1045
|
+
### fractions
|
|
1046
|
+
|
|
1047
|
+
**Missing.** Pure TPy over BigInt; small surface.
|
|
1048
|
+
|
|
1049
|
+
### statistics
|
|
1050
|
+
|
|
1051
|
+
**Missing.** Pure TPy (mean/median/mode/stdev/variance).
|
|
1052
|
+
|
|
1053
|
+
### pickle
|
|
1054
|
+
|
|
1055
|
+
**Blocked.** Needs runtime type info + `io`.
|
|
1056
|
+
|
|
1057
|
+
### shelve
|
|
1058
|
+
|
|
1059
|
+
**Blocked.** Needs pickle + dbm.
|
|
1060
|
+
|
|
1061
|
+
### inspect
|
|
1062
|
+
|
|
1063
|
+
**Blocked.** Needs runtime type/function introspection.
|
|
1064
|
+
|
|
1065
|
+
### asyncio
|
|
1066
|
+
|
|
1067
|
+
**Partial (v1).** Single-threaded executor + minimal viable surface. Design in [`docs/ASYNC_DESIGN.md`](ASYNC_DESIGN.md); status in [`docs/ASYNC_PROGRESS.md`](ASYNC_PROGRESS.md).
|
|
1068
|
+
|
|
1069
|
+
Done in v1:
|
|
1070
|
+
|
|
1071
|
+
- `asyncio.run(coro)` -- drives a top-level coroutine to completion; sleeps idle on the executor's timer min-heap; cancel-drains remaining spawned tasks at exit so `finally` runs for fire-and-forget tasks.
|
|
1072
|
+
- `asyncio.sleep(seconds)` -- registers a steady-clock deadline with the running executor; returns `Own[Task[None]]`.
|
|
1073
|
+
- `asyncio.create_task(coro())` -- registers an async-def call with the running executor and returns `Own[Task[T]]` (T inferred from the async def's return type) sharing state with the executor's task slot.
|
|
1074
|
+
- `asyncio.Future[T]` -- single-awaiter manual-completion awaitable (`set_result(value)` / `set_exception(exc)` / `done()`); ownership-transfer API on `set_result` so nocopy types flow through. `Future[None]` is currently unusable (void-payload template substitution issue, BUGS.md); use `Event` for no-payload completion signals.
|
|
1075
|
+
- `asyncio.Event` -- boolean completion signal (`set` / `clear` / `is_set` / directly awaitable). The no-payload analog of `Future[T]`. CPython parity for the API surface; TPy diverges in that `await event` works directly (CPython requires `await event.wait()`) because TPy can't yet define `async def` methods on classes. Single-awaiter v1.
|
|
1076
|
+
- `asyncio.CancelledError` -- raised at the next suspension point of a cancelled task; thread through `try`/`finally`.
|
|
1077
|
+
- `Executor` body (slot table for parked tasks with `(slot_id, generation)` wakers, runnable deque, timer min-heap keyed on steady-clock deadlines) lives in TPy at `lib/tpy/asyncio/_executor.py`. `Executor` inherits the `@dynamic Awaker` protocol; `Waker.wake()` dispatches through that vtable. `runtime/cpp/include/tpy/async.hpp` carries two pieces: `CancelledError` and the `poll_with_cancel` resume-case helper template (M8) -- no FFI dispatch shell.
|
|
1078
|
+
|
|
1079
|
+
v1.5 M3-M6: SHIPPED. Awaits inside arbitrary control flow (`if`/`while`/`for`/`with` sub-bodies + `try`/`except`/`finally` around awaits) via a localized CFG (`tpyc/codegen_cpp/resumable_cfg.py`). `async with` (cleanup-only) and `async for` (with `StopAsyncIteration`) shipped on top of the same machinery. See `docs/ASYNC_PROGRESS.md` for the milestone-by-milestone summary.
|
|
1080
|
+
|
|
1081
|
+
v1.5 M8: SHIPPED. `asyncio.wait_for(coro, timeout) -> T` (`async def` free function) + built-in `TimeoutError`. Pumps inner cleanup through `finally`-with-await on deadline; outer cancellation propagates through to the inner coroutine (the auto-emitted resume-case cancel-check now calls `cancel()` on the in-flight sub-coro before polling, so the inner observes `CancelledError` at its suspension point and can run cleanup before the cancellation surfaces). The async-def codegen path was extended in M8 to handle `Own[Awaitable[T]]`-shaped params via a deduced `T_<pname>` extra template arg with the protocol concept constraint, mirroring the existing non-async pattern. Caller-side sub-coro field declarations use `std::remove_cvref_t<decltype(arg)>` to deduce the concrete coro type without sema knowing it.
|
|
1082
|
+
|
|
1083
|
+
v1.5 M9: SHIPPED. Two homogeneous entrypoints sharing one `_GatherFuture[T]` engine: `asyncio.gather(*tasks: Task[T]) -> list[T]` (variadic-positional, a sync factory returning the awaitable directly) and `asyncio.gather_list(tasks: list[Task[T]]) -> list[T]` (the list-shaped form). Both run N already-spawned tasks concurrently and collect their results in input order; on the first sub-task failure (or outer cancel) propagate `cancel()` to siblings, drain to settlement, then re-raise the first exception observed. The CPython-shape *heterogeneous* form `gather[*Ts](*coros) -> tuple[*Ts]` remains deferred (blocked on variadic generics + the async-def `*args` codegen gap; see TODO.md / BUGS.md). Implementation: hand-written `_GatherFuture[T]` Rc-clones each task handle into an owned list via a new `Task[T].clone()` method.
|
|
1084
|
+
|
|
1085
|
+
Pending (v1.5): CPython-shape *heterogeneous* variadic `gather`, partial / nested try-around-await (see BUGS.md for the specific CFG-build limits).
|
|
1086
|
+
|
|
1087
|
+
v1.5 M1: SHIPPED. Sync `with` upgraded to CPython-shape `__exit__(self, exc_type, exc_val, exc_tb) -> bool | None`; `bool` return suppresses, `None` is cleanup-only. Class-based exception dispatch via `isinstance(exc_val, X)` is deferred to M2 (blocked on `Optional[BaseException]` slicing -- see `BUGS.md`).
|
|
1088
|
+
|
|
1089
|
+
Pending (v2+): I/O reactor (epoll on Linux, kqueue on BSD/macOS, IOCP on Windows), `asyncio.Queue`, async generators, `@error_return` async, `__await__` adaptation, multi-thread executor.
|
|
1090
|
+
|
|
1091
|
+
v1.1 runtime port: SHIPPED. Executor body + `Task` + `Future` moved from `runtime/cpp/include/tpy/async.hpp` to `lib/tpy/asyncio/_executor.py`, dispatched from C++ via a thread-local `ExecutorOps` function-pointer table.
|
|
1092
|
+
|
|
1093
|
+
v1.2: SHIPPED in seven steps. Step 1 added `val_or_ref_t<void>` and ported `asyncio.run` from a C++ template shell to pure TPy. Step 2 added `@cpp_template` auto-move for `Own[T]` args. Step 3 consolidated value-type emission ordering and timer-heap cleanup. Step 4 moved the type-erasure stack (`Task[T]` / `TaskState[T]` / `AnyTask` / `AnyTaskBox`) to TPy via `@dynamic` protocols. Step 5 moved `Poll[T]` itself -- a single `@nocopy class Poll[T]` body covers what the C++ `Poll<T>` + `Poll<void>` + `Poll<T&>` specializations did (storage is `UninitArrayStorage[T, 1] + bool`; void analog rides on `Poll[None]` -> `Poll<std::monostate>`; the reference-T specialization was never instantiated by generated code). Step 6 shrank `async.hpp` by 50 lines (TLS removal + struct/helper moves to TPy) and consolidated `ExecutorHandle` next to `Waker`. Step 7 pivoted dispatch from the C++ `ExecutorOps` function-pointer table to the `@dynamic Awaker` protocol: `Waker` became a pure-TPy `ValueType` holding `Ptr[Awaker]`, `Executor` inherits `Awaker`, and `async.hpp` collapsed to just `CancelledError`. See [`docs/ASYNC_PROGRESS.md`](ASYNC_PROGRESS.md#v1x-milestone-asyncio-runtime-tpy-port-must-precede-v15) for the phase-by-phase history.
|
|
1094
|
+
|
|
1095
|
+
### threading
|
|
1096
|
+
|
|
1097
|
+
**Blocked** on threading primitives.
|
|
1098
|
+
|
|
1099
|
+
### multiprocessing
|
|
1100
|
+
|
|
1101
|
+
**Blocked** on process spawning.
|
|
1102
|
+
|
|
1103
|
+
### subprocess
|
|
1104
|
+
|
|
1105
|
+
**Blocked** on process spawning.
|
|
1106
|
+
|
|
1107
|
+
### socket
|
|
1108
|
+
|
|
1109
|
+
**Partial.** Pure-TPy facade in `lib/tpy/socket.py` over raw `@native`
|
|
1110
|
+
bindings in `lib/tpy/_bindings/posix_socket.py`. One out-of-line C++ helper
|
|
1111
|
+
in `runtime/cpp/src/stdlib/socket_impl.cpp` for DNS resolution
|
|
1112
|
+
(getaddrinfo walks a `struct addrinfo` whose field order isn't portable
|
|
1113
|
+
between Linux and BSD/macOS, so the struct walking lives on the C++ side
|
|
1114
|
+
where `<netdb.h>` is available).
|
|
1115
|
+
|
|
1116
|
+
Phase 1 ships a working IPv4 TCP client/server shape sufficient for the
|
|
1117
|
+
"simple socket client / simple socket server" milestone in the project
|
|
1118
|
+
roadmap. Phase 2 is non-blocking I/O + `selectors`. Phase 3 is TLS/ssl.
|
|
1119
|
+
Phase 4 is `http.client` (pure TPy). Phase 5 is websocket client.
|
|
1120
|
+
|
|
1121
|
+
Architecture (mirrors the re / PCRE2 split):
|
|
1122
|
+
|
|
1123
|
+
* `runtime/cpp/include/tpy/stdlib/socket_h.hpp` -- ABI-glue header.
|
|
1124
|
+
Forward-declares `struct sockaddr_in` (POSIX-stable 16-byte layout)
|
|
1125
|
+
with `static_assert` guards on size + field offsets, plus
|
|
1126
|
+
`extern "C"` decls of libc socket/bind/connect/... and our three
|
|
1127
|
+
runtime helpers. Deliberately does NOT include `<sys/socket.h>` /
|
|
1128
|
+
`<netdb.h>` -- those headers `#define` macros (`AF_INET`, `SOCK_STREAM`,
|
|
1129
|
+
...) that would collide with TPy module-level constants of the same
|
|
1130
|
+
name.
|
|
1131
|
+
* `runtime/cpp/src/stdlib/socket_impl.cpp` -- implements
|
|
1132
|
+
`tpy_resolve_ipv4(const uint8_t*, uint64_t, uint8_t[4])`,
|
|
1133
|
+
`tpy_errno()`, `tpy_last_resolve_error()`. Includes the system
|
|
1134
|
+
headers freely; isolated to its own TU so macros don't escape.
|
|
1135
|
+
First inhabitant of `runtime/cpp/src/` (new runtime-lib convention
|
|
1136
|
+
-- see "C++ helper convention" above).
|
|
1137
|
+
* `lib/tpy/_bindings/posix_socket.py` -- pure `@native` 1:1 bindings over
|
|
1138
|
+
the libc C ABI + the three runtime helpers. `@native(binding="C")
|
|
1139
|
+
class SockaddrIn` mirrors the struct for typed field access.
|
|
1140
|
+
* `lib/tpy/socket.py` -- facade. `Socket` class (`@nocopy`, RAII via
|
|
1141
|
+
`__del__` closing the fd), SocketError, constants, create_connection
|
|
1142
|
+
/ create_server, gethostbyname, with-statement support.
|
|
1143
|
+
|
|
1144
|
+
| Item | Status | Notes |
|
|
1145
|
+
|---|---|---|
|
|
1146
|
+
| `Socket(family, type, proto)` | Done | `@nocopy`, RAII close in `__del__`. Use `Socket(AF_INET, SOCK_STREAM)` or `create_connection` / `create_server`; a CPython-style module-level `socket()` factory is a future addition |
|
|
1147
|
+
| `bind`, `connect`, `listen`, `accept` | Done | `accept() -> tuple[Own[Socket], tuple[str, Int32]]` matches CPython's `(conn, (host, port))` shape |
|
|
1148
|
+
| `send`, `sendall`, `recv` | Done | `send` returns `Int32` (truncated from `ssize_t`); realistic per-call sends are well under 2 GiB. `recv` returns a fresh `bytes` |
|
|
1149
|
+
| `close`, `shutdown`, `fileno` | Done | |
|
|
1150
|
+
| `setsockopt_int` | Done | Int-valued options only; struct options (`SO_RCVTIMEO`, `SO_LINGER`) deferred |
|
|
1151
|
+
| `getsockname`, `getpeername` | Done | Return `tuple[str, Int32]` |
|
|
1152
|
+
| `gethostbyname` | Done | Resolves via `getaddrinfo` behind `tpy_resolve_ipv4`; returns the first A record only |
|
|
1153
|
+
| `create_connection`, `create_server` | Done | TCP client/server convenience factories; `create_server` bundles SO_REUSEADDR + bind + listen |
|
|
1154
|
+
| `with socket(...) as s:` | Done | `__enter__` returns self, `__exit__` closes |
|
|
1155
|
+
| `SocketError` | Done | Wraps errno + strerror. `except socket.SocketError` blocked by qualified-except-clause gap; use `from socket import SocketError` |
|
|
1156
|
+
| Constants (`AF_INET`, `SOCK_STREAM`, `SOL_SOCKET`, ...) | Done | Linux glibc values hardcoded. macOS/BSD values differ -- deferred |
|
|
1157
|
+
| `socketpair()` | Done | Defaults to `AF_UNIX` + `SOCK_STREAM`; returns `tuple[Own[Socket], Own[Socket]]` |
|
|
1158
|
+
| IPv6 / `AF_INET6` | Missing | Needs `SockaddrIn6` binding |
|
|
1159
|
+
| `AF_UNIX` | Missing | Needs `SockaddrUn` binding |
|
|
1160
|
+
| `sendto`, `recvfrom`, `recv_into` | Missing | UDP out-addr + recv-into-caller-buffer variants |
|
|
1161
|
+
| `setblocking`, `settimeout` | Missing | Non-blocking I/O belongs with Phase 2 `selectors` |
|
|
1162
|
+
| `getaddrinfo` (full API) | Missing | Flat `tpy_resolve_ipv4` only today; multi-result walk needs typed records |
|
|
1163
|
+
| `makefile()` | Missing | Needs io module to grow "adopt this fd" |
|
|
1164
|
+
| Windows (Winsock2) | Missing | `SOCKET` unsigned, `WSAStartup`, `closesocket`, `WSAGetLastError` -- all in `#ifdef _WIN32` block inside socket_impl.cpp once we have Windows CI |
|
|
1165
|
+
| TLS (`ssl` module) | Missing | Phase 3; needs mbedTLS vendored |
|
|
1166
|
+
|
|
1167
|
+
Tests:
|
|
1168
|
+
* No integration test cases under `tests/cases/` -- running real client/
|
|
1169
|
+
server end-to-end needs threading or fork (not in Phase 1), and
|
|
1170
|
+
fingerprint-based skip logic doesn't play well with network ports.
|
|
1171
|
+
* Examples under `examples/net/` (`tcp_client.py` + `tcp_server.py`)
|
|
1172
|
+
serve as manual smoke tests: run the server in one terminal, the
|
|
1173
|
+
client in another, verify the echo round-trip.
|
|
1174
|
+
|
|
1175
|
+
### http.client
|
|
1176
|
+
|
|
1177
|
+
**Blocked** on TLS (phase 3) for HTTPS; HTTP-over-socket would build on
|
|
1178
|
+
Phase 1's `socket` today but makes limited sense without TLS. Slated
|
|
1179
|
+
for Phase 4 per the network roadmap discussion.
|
|
1180
|
+
|
|
1181
|
+
### urllib.request
|
|
1182
|
+
|
|
1183
|
+
**Blocked** on http.
|
|
1184
|
+
|
|
1185
|
+
---
|
|
1186
|
+
|
|
1187
|
+
## Adjacent: NumPy / Numeric Computing (parked)
|
|
1188
|
+
|
|
1189
|
+
Not CPython stdlib, but tracked here to keep it in sight. Not on the near-term
|
|
1190
|
+
plan. A separate design doc (`NUMERIC_COMPUTING.md`) will spin off when this
|
|
1191
|
+
becomes active.
|
|
1192
|
+
|
|
1193
|
+
**Key realization**: numpy itself is not wrappable. The Python object layer
|
|
1194
|
+
(`np.array`, `arr.shape`, methods) and the C API (`PyArray_*`) are hard-coupled
|
|
1195
|
+
to CPython's refcounting + GIL + PyObject machinery. A natively-compiled TPy
|
|
1196
|
+
runtime can't host that.
|
|
1197
|
+
|
|
1198
|
+
**However, the low-level pieces people assume are "numpy" mostly aren't**:
|
|
1199
|
+
|
|
1200
|
+
| Layer | Wrappable from TPy? | What it actually is |
|
|
1201
|
+
|---|---|---|
|
|
1202
|
+
| Python object layer | No | Numpy's own, CPython-coupled |
|
|
1203
|
+
| C API (`PyArray_*`) | No | Numpy's own, CPython-coupled |
|
|
1204
|
+
| Ufunc dispatch / `NpyIter` broadcasting | Theoretically; not packaged as a standalone lib | Numpy's own |
|
|
1205
|
+
| Element-wise kernel loops | Theoretically; template-generated, not shipped as reusable | Numpy's own |
|
|
1206
|
+
| **BLAS / LAPACK / OpenBLAS / MKL** (linear algebra) | **Yes** | Upstream Fortran/C libs; ABI-stable; numpy just wraps them |
|
|
1207
|
+
| **pocketfft** (FFT) | **Yes** | ~2k LOC C++, MIT, header-only, upstream repo exists; numpy vendors it |
|
|
1208
|
+
| **sleef** (SIMD transcendentals) | **Yes** | Upstream C lib |
|
|
1209
|
+
|
|
1210
|
+
So the "wrap numpy's low-level building blocks" idea collapses into **wrap the
|
|
1211
|
+
things numpy itself wraps** -- which are CPython-independent upstream libs we
|
|
1212
|
+
can bind directly via the thin-binding pattern. Gets us the raw compute for
|
|
1213
|
+
free.
|
|
1214
|
+
|
|
1215
|
+
**The container + broadcasting layer is ours to build** regardless. NumPy's
|
|
1216
|
+
view/refcount model doesn't fit TPy's ownership/borrow model; a
|
|
1217
|
+
reimplementation is mandatory.
|
|
1218
|
+
|
|
1219
|
+
### Candidate backends for the ndarray container
|
|
1220
|
+
|
|
1221
|
+
| Option | What it is | License | Fit |
|
|
1222
|
+
|---|---|---|---|
|
|
1223
|
+
| **xtensor** | Header-only C++ "numpy for C++, Python-free". `xt::xarray<T>`, broadcasting, lazy expressions, ufuncs, BLAS/LAPACK bindings | BSD-3 | **Strongest candidate** -- explicitly designed for this use case; matches numpy's API style; years of optimization work already done |
|
|
1224
|
+
| **Eigen** | Header-only C++ linear algebra (matrices, decompositions) | MPL2 | Good for linalg specifically; less numpy-shaped than xtensor |
|
|
1225
|
+
| **ArrayFire** | GPU-capable numeric library | BSD-3 | Relevant if GPU is ever in scope |
|
|
1226
|
+
| **blitz++** | Older C++ numeric library with expression templates | Artistic/LGPL | Historical; less active than xtensor |
|
|
1227
|
+
| **Pure TPy + `Span[T]` / `Array[T, N]`** | Build our own | -- | Maximum control; maximum work. Pressure-tests language features (multi-dim arrays, broadcasting via operator overloads, SIMD intrinsics, bounds-check elision) |
|
|
1228
|
+
|
|
1229
|
+
Most likely path: prototype **xtensor-backed** and **pure-TPy** options side-
|
|
1230
|
+
by-side for the same small API subset (1D/2D dense, element-wise, reductions,
|
|
1231
|
+
slicing), benchmark, pick.
|
|
1232
|
+
|
|
1233
|
+
### Strategy sketch
|
|
1234
|
+
|
|
1235
|
+
- **Phase 0 (now)**: parked. Keep this section up to date as thinking
|
|
1236
|
+
evolves; don't build.
|
|
1237
|
+
- **Phase 1**: `tplib.ndarray` -- numpy-*style*, not numpy-compat. 1D/2D
|
|
1238
|
+
dense arrays, element-wise ops, reductions, basic slicing. Backend: TBD
|
|
1239
|
+
(xtensor vs own). Bind BLAS for linalg separately.
|
|
1240
|
+
- **Phase 2**: N-D, broadcasting, fancy indexing, dtype system, FFT
|
|
1241
|
+
(pocketfft binding).
|
|
1242
|
+
- **Phase 3 (maybe never)**: a `numpy`-named CPython-compat shim over
|
|
1243
|
+
`tplib.ndarray`. Document gaps honestly. Only justified if the shim gets
|
|
1244
|
+
close enough to real numpy that users can flip a switch.
|
|
1245
|
+
|
|
1246
|
+
### Other angles worth remembering
|
|
1247
|
+
|
|
1248
|
+
- **At compile time, macros run under CPython** -- so a macro like
|
|
1249
|
+
`@np_table` can freely use real numpy to compute lookup tables, validate
|
|
1250
|
+
shapes, or generate specialized kernels that get embedded in the binary.
|
|
1251
|
+
Narrow but real use case, and it's available today without any runtime
|
|
1252
|
+
numpy support.
|
|
1253
|
+
- **Nobody has pulled off full numpy compat in a compiled Python.** Codon
|
|
1254
|
+
ships a partial numpy-like lib under its own namespace. PyPy's
|
|
1255
|
+
`micronumpy` is partial and most users fall back to CPython numpy via
|
|
1256
|
+
`cpyext`. Cython/Nuitka/mypyc don't reimplement -- they use real numpy
|
|
1257
|
+
because they run on CPython. The honest precedent is "numpy-style
|
|
1258
|
+
subset", not "numpy-compat".
|