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,221 @@
|
|
|
1
|
+
"""Unit tests for cycle_detection module."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from .typesys import NominalType, RecordInfo, FieldInfo, PtrType, TypeParamRef, make_list
|
|
5
|
+
from .type_def_registry import (
|
|
6
|
+
attach_dynamic_type_def, clear_dynamic_type_defs, TypeCategory,
|
|
7
|
+
)
|
|
8
|
+
from .cycle_detection import detect_type_cycles, TypeCycle
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _named(name: str) -> NominalType:
|
|
12
|
+
return NominalType(name)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _box(inner: NominalType) -> NominalType:
|
|
16
|
+
# `_module_qname` mirrors what type resolution sets on real records;
|
|
17
|
+
# it's what feeds `record_info_of(typ)` through the TypeDef registry.
|
|
18
|
+
return NominalType("Box", type_args=(inner,), _module_qname="Box")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _list(inner) -> NominalType:
|
|
22
|
+
return make_list(element_type=inner)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def _box_recordinfo():
|
|
27
|
+
"""Register a minimal RecordInfo for Box mirroring tplib.Box's
|
|
28
|
+
`_ptr: Ptr[T]` storage. The cycle walker now reads indirection from
|
|
29
|
+
record fields (with type-param substitution) rather than a hardcoded
|
|
30
|
+
name list, so tests using _box() must register the record."""
|
|
31
|
+
info = RecordInfo(
|
|
32
|
+
name="Box",
|
|
33
|
+
fields=[FieldInfo(name="_ptr", type=PtrType(TypeParamRef("T")))],
|
|
34
|
+
type_params=["T"],
|
|
35
|
+
)
|
|
36
|
+
attach_dynamic_type_def("Box", TypeCategory.RECORD, record=info)
|
|
37
|
+
try:
|
|
38
|
+
yield info
|
|
39
|
+
finally:
|
|
40
|
+
clear_dynamic_type_defs()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture
|
|
44
|
+
def _list_indirecting():
|
|
45
|
+
"""Flip is_indirecting=True on the static `builtins.list` TypeDef for
|
|
46
|
+
the duration of one test. In production the `@builtin_type("builtins.list",
|
|
47
|
+
indirecting=True)` stub registration handles this; the unit tests skip
|
|
48
|
+
stub compilation, so we toggle the flag directly."""
|
|
49
|
+
from .type_def_registry import get_type_def
|
|
50
|
+
td = get_type_def("builtins.list")
|
|
51
|
+
assert td is not None
|
|
52
|
+
prev = td.is_indirecting
|
|
53
|
+
td.is_indirecting = True
|
|
54
|
+
try:
|
|
55
|
+
yield
|
|
56
|
+
finally:
|
|
57
|
+
td.is_indirecting = prev
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestCycleDetection:
|
|
61
|
+
"""Tests for detect_type_cycles."""
|
|
62
|
+
|
|
63
|
+
def test_no_cycle(self) -> None:
|
|
64
|
+
"""A -> B with no back-reference: no cycle."""
|
|
65
|
+
cycles = detect_type_cycles(
|
|
66
|
+
record_fields={"A": [("b", _named("B"))], "B": [("x", _named("int"))]},
|
|
67
|
+
union_aliases={},
|
|
68
|
+
)
|
|
69
|
+
assert cycles == []
|
|
70
|
+
|
|
71
|
+
def test_record_record_cycle_no_indirection(self) -> None:
|
|
72
|
+
"""A has B, B has A -- infinite size cycle."""
|
|
73
|
+
cycles = detect_type_cycles(
|
|
74
|
+
record_fields={
|
|
75
|
+
"A": [("b", _named("B"))],
|
|
76
|
+
"B": [("a", _named("A"))],
|
|
77
|
+
},
|
|
78
|
+
union_aliases={},
|
|
79
|
+
)
|
|
80
|
+
assert len(cycles) == 1
|
|
81
|
+
assert cycles[0].is_fully_unindirected()
|
|
82
|
+
assert set(cycles[0].path) == {"A", "B"}
|
|
83
|
+
|
|
84
|
+
def test_record_record_cycle_with_box(self, _box_recordinfo) -> None:
|
|
85
|
+
"""A has Box[B], B has A -- cycle broken by Box on one edge."""
|
|
86
|
+
cycles = detect_type_cycles(
|
|
87
|
+
record_fields={
|
|
88
|
+
"A": [("b", _box(_named("B")))],
|
|
89
|
+
"B": [("a", _named("A"))],
|
|
90
|
+
},
|
|
91
|
+
union_aliases={},
|
|
92
|
+
)
|
|
93
|
+
assert len(cycles) == 1
|
|
94
|
+
# One edge has indirection (Box), so cycle is valid
|
|
95
|
+
assert cycles[0].has_any_indirection()
|
|
96
|
+
assert not cycles[0].is_fully_unindirected()
|
|
97
|
+
|
|
98
|
+
def test_mutual_recursion_union_records(self, _box_recordinfo) -> None:
|
|
99
|
+
"""type Expr = Lit | BinOp, where BinOp has Box[Expr] -- the AST pattern."""
|
|
100
|
+
cycles = detect_type_cycles(
|
|
101
|
+
record_fields={
|
|
102
|
+
"Lit": [("value", _named("int"))],
|
|
103
|
+
"BinOp": [("left", _box(_named("Expr"))), ("right", _box(_named("Expr")))],
|
|
104
|
+
},
|
|
105
|
+
union_aliases={
|
|
106
|
+
"Expr": (_named("Lit"), _named("BinOp")),
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
assert len(cycles) >= 1
|
|
110
|
+
# At least one cycle should involve Expr
|
|
111
|
+
expr_cycles = [c for c in cycles if "Expr" in c.path]
|
|
112
|
+
assert len(expr_cycles) >= 1
|
|
113
|
+
# The cycle has indirection (Box on one edge), so it's valid
|
|
114
|
+
for c in expr_cycles:
|
|
115
|
+
assert c.has_any_indirection()
|
|
116
|
+
assert not c.is_fully_unindirected()
|
|
117
|
+
# Expr should be identified as an alias in the cycle
|
|
118
|
+
for c in expr_cycles:
|
|
119
|
+
assert "Expr" in c.alias_names
|
|
120
|
+
|
|
121
|
+
def test_mutual_recursion_no_box_error(self) -> None:
|
|
122
|
+
"""type Expr = Lit | BinOp, where BinOp has bare Expr -- infinite size."""
|
|
123
|
+
cycles = detect_type_cycles(
|
|
124
|
+
record_fields={
|
|
125
|
+
"Lit": [("value", _named("int"))],
|
|
126
|
+
"BinOp": [("left", _named("Expr")), ("right", _named("Expr"))],
|
|
127
|
+
},
|
|
128
|
+
union_aliases={
|
|
129
|
+
"Expr": (_named("Lit"), _named("BinOp")),
|
|
130
|
+
},
|
|
131
|
+
)
|
|
132
|
+
assert len(cycles) >= 1
|
|
133
|
+
expr_cycles = [c for c in cycles if "Expr" in c.path]
|
|
134
|
+
assert len(expr_cycles) >= 1
|
|
135
|
+
# All edges lack indirection -- infinite size
|
|
136
|
+
assert any(c.is_fully_unindirected() for c in expr_cycles)
|
|
137
|
+
|
|
138
|
+
def test_list_provides_indirection(self, _list_indirecting) -> None:
|
|
139
|
+
"""type Expr = Lit | Call, where Call has list[Expr] -- OK."""
|
|
140
|
+
cycles = detect_type_cycles(
|
|
141
|
+
record_fields={
|
|
142
|
+
"Lit": [("value", _named("int"))],
|
|
143
|
+
"Call": [("args", _list(_named("Expr")))],
|
|
144
|
+
},
|
|
145
|
+
union_aliases={
|
|
146
|
+
"Expr": (_named("Lit"), _named("Call")),
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
expr_cycles = [c for c in cycles if "Expr" in c.path]
|
|
150
|
+
assert len(expr_cycles) >= 1
|
|
151
|
+
for c in expr_cycles:
|
|
152
|
+
assert c.has_any_indirection()
|
|
153
|
+
assert not c.is_fully_unindirected()
|
|
154
|
+
|
|
155
|
+
def test_deep_cycle(self, _box_recordinfo) -> None:
|
|
156
|
+
"""A -> B -> C -> A through records, depth > 1."""
|
|
157
|
+
cycles = detect_type_cycles(
|
|
158
|
+
record_fields={
|
|
159
|
+
"A": [("b", _box(_named("B")))],
|
|
160
|
+
"B": [("c", _named("C"))],
|
|
161
|
+
"C": [("a", _named("A"))],
|
|
162
|
+
},
|
|
163
|
+
union_aliases={},
|
|
164
|
+
)
|
|
165
|
+
assert len(cycles) >= 1
|
|
166
|
+
deep = [c for c in cycles if len(c.path) == 3]
|
|
167
|
+
assert len(deep) >= 1
|
|
168
|
+
assert set(deep[0].path) == {"A", "B", "C"}
|
|
169
|
+
|
|
170
|
+
def test_no_false_positives_for_self_reference(self, _box_recordinfo) -> None:
|
|
171
|
+
"""Self-references are skipped (handled by D19)."""
|
|
172
|
+
cycles = detect_type_cycles(
|
|
173
|
+
record_fields={"Node": [("child", _box(_named("Node")))]},
|
|
174
|
+
union_aliases={},
|
|
175
|
+
)
|
|
176
|
+
assert cycles == []
|
|
177
|
+
|
|
178
|
+
def test_empty_input(self) -> None:
|
|
179
|
+
cycles = detect_type_cycles({}, {})
|
|
180
|
+
assert cycles == []
|
|
181
|
+
|
|
182
|
+
def test_imported_record_self_loop_terminates(self) -> None:
|
|
183
|
+
"""The structural field walk would loop forever on an imported,
|
|
184
|
+
non-indirecting record that references itself in its own fields
|
|
185
|
+
(e.g. `class Wrapper[T]: inner: Wrapper[T]` imported from another
|
|
186
|
+
module, where Wrapper is NOT in the current module's target_names).
|
|
187
|
+
The walker's `expanding` recursion guard must terminate.
|
|
188
|
+
"""
|
|
189
|
+
# Register Wrapper with a self-referential field. No Ptr, no
|
|
190
|
+
# is_indirecting -- so the walker takes the structural-fields
|
|
191
|
+
# path and would recurse indefinitely without the guard.
|
|
192
|
+
wrapper_info = RecordInfo(
|
|
193
|
+
name="Wrapper",
|
|
194
|
+
fields=[FieldInfo(name="inner", type=NominalType(
|
|
195
|
+
"Wrapper", type_args=(TypeParamRef("T"),), _module_qname="Wrapper",
|
|
196
|
+
))],
|
|
197
|
+
type_params=["T"],
|
|
198
|
+
)
|
|
199
|
+
attach_dynamic_type_def("Wrapper", TypeCategory.RECORD, record=wrapper_info)
|
|
200
|
+
try:
|
|
201
|
+
# The cycle check should terminate (recursion guard fires) and
|
|
202
|
+
# report no cycle: Foo's field type is Wrapper[A], which the
|
|
203
|
+
# walker expands once into `inner: Wrapper[T->A]` -- the second
|
|
204
|
+
# entry into Wrapper hits the guard and returns without finding
|
|
205
|
+
# `A` in target_names along that path.
|
|
206
|
+
cycles = detect_type_cycles(
|
|
207
|
+
record_fields={
|
|
208
|
+
"Foo": [("w", NominalType(
|
|
209
|
+
"Wrapper",
|
|
210
|
+
type_args=(_named("A"),),
|
|
211
|
+
_module_qname="Wrapper",
|
|
212
|
+
))],
|
|
213
|
+
"A": [("x", _named("int"))],
|
|
214
|
+
},
|
|
215
|
+
union_aliases={},
|
|
216
|
+
)
|
|
217
|
+
# The important assertion is termination; no cycle exists in
|
|
218
|
+
# this graph (Foo -> A via Wrapper's substituted field, A -> int).
|
|
219
|
+
assert cycles == []
|
|
220
|
+
finally:
|
|
221
|
+
clear_dynamic_type_defs()
|
tpyc/test_dump_types.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Tests for builtin type dump output (walks lib/tpy/ .py stubs)."""
|
|
2
|
+
|
|
3
|
+
import ast as pyast
|
|
4
|
+
import io
|
|
5
|
+
import re
|
|
6
|
+
from contextlib import redirect_stdout
|
|
7
|
+
|
|
8
|
+
from .dump_types import (
|
|
9
|
+
_extract_all_whitelist,
|
|
10
|
+
_format_function_signature,
|
|
11
|
+
_get_docstring,
|
|
12
|
+
_gfm_anchor,
|
|
13
|
+
_indent,
|
|
14
|
+
_is_public,
|
|
15
|
+
_path_to_module,
|
|
16
|
+
_render_module,
|
|
17
|
+
dump_builtin_types,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _parse(src: str) -> pyast.Module:
|
|
22
|
+
return pyast.parse(src)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_path_to_module():
|
|
26
|
+
assert _path_to_module("tpy/__init__.py") == "tpy"
|
|
27
|
+
assert _path_to_module("tpy/_core/_types.py") == "tpy._core._types"
|
|
28
|
+
assert _path_to_module("typing.py") == "typing"
|
|
29
|
+
assert _path_to_module("tplib/json/parser.py") == "tplib.json.parser"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_is_public():
|
|
33
|
+
assert _is_public("Foo", None)
|
|
34
|
+
assert not _is_public("_hidden", None)
|
|
35
|
+
# Dunder names are public-by-convention module metadata.
|
|
36
|
+
assert _is_public("__version__", None)
|
|
37
|
+
assert _is_public("__all__", None)
|
|
38
|
+
# With an __all__ whitelist, only listed names are public.
|
|
39
|
+
allow = frozenset({"Foo"})
|
|
40
|
+
assert _is_public("Foo", allow)
|
|
41
|
+
assert not _is_public("Bar", allow)
|
|
42
|
+
assert not _is_public("_hidden", allow)
|
|
43
|
+
# __all__ whitelist takes precedence even for dunders.
|
|
44
|
+
assert not _is_public("__version__", allow)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_extract_all_whitelist():
|
|
48
|
+
tree = _parse('__all__ = ["A", "B"]\nclass C: ...')
|
|
49
|
+
assert _extract_all_whitelist(tree) == {"A", "B"}
|
|
50
|
+
|
|
51
|
+
tree_no_all = _parse("class C: ...")
|
|
52
|
+
assert _extract_all_whitelist(tree_no_all) is None
|
|
53
|
+
|
|
54
|
+
tree_tuple = _parse('__all__ = ("A", "B")')
|
|
55
|
+
assert _extract_all_whitelist(tree_tuple) == {"A", "B"}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_format_function_signature_basic():
|
|
59
|
+
tree = _parse("def f(x: int, y: str) -> bool: ...")
|
|
60
|
+
fn = tree.body[0]
|
|
61
|
+
assert _format_function_signature("f", fn) == "f(x: int, y: str) -> bool"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_format_function_signature_skips_self_for_constructors():
|
|
65
|
+
tree = _parse("class C:\n def __init__(self, x: int) -> None: ...")
|
|
66
|
+
init = tree.body[0].body[0]
|
|
67
|
+
sig = _format_function_signature("C", init, is_method=True, skip_self=True)
|
|
68
|
+
assert sig == "C(x: int) -> None"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_format_function_signature_keeps_self_for_methods():
|
|
72
|
+
tree = _parse("class C:\n def f(self, x: int) -> int: ...")
|
|
73
|
+
method = tree.body[0].body[0]
|
|
74
|
+
sig = _format_function_signature("f", method, is_method=True)
|
|
75
|
+
assert sig == "f(self, x: int) -> int"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_format_function_signature_varargs_and_kwonly():
|
|
79
|
+
tree = _parse("def f(*args: int, flag: bool = True, **kw: str) -> None: ...")
|
|
80
|
+
fn = tree.body[0]
|
|
81
|
+
sig = _format_function_signature("f", fn)
|
|
82
|
+
assert "*args: int" in sig
|
|
83
|
+
assert "flag: bool" in sig
|
|
84
|
+
assert "**kw: str" in sig
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_format_function_signature_bare_kwonly_separator():
|
|
88
|
+
"""A bare `*` separator must be emitted when kwonlyargs exist without *args."""
|
|
89
|
+
tree = _parse("def f(x: int, *, flag: bool) -> None: ...")
|
|
90
|
+
fn = tree.body[0]
|
|
91
|
+
sig = _format_function_signature("f", fn)
|
|
92
|
+
assert sig == "f(x: int, *, flag: bool) -> None"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_get_docstring_module_class_function():
|
|
96
|
+
tree = _parse('"""Module doc."""\n'
|
|
97
|
+
'class C:\n'
|
|
98
|
+
' """Class doc."""\n'
|
|
99
|
+
' def m(self):\n'
|
|
100
|
+
' """Method doc."""\n'
|
|
101
|
+
' ...\n'
|
|
102
|
+
'def f():\n'
|
|
103
|
+
' """Function doc."""\n'
|
|
104
|
+
' ...\n')
|
|
105
|
+
assert _get_docstring(tree) == "Module doc."
|
|
106
|
+
cls = tree.body[1]
|
|
107
|
+
assert _get_docstring(cls) == "Class doc."
|
|
108
|
+
# cls.body[0] is the docstring expression; cls.body[1] is the method.
|
|
109
|
+
method = cls.body[1]
|
|
110
|
+
assert _get_docstring(method) == "Method doc."
|
|
111
|
+
fn = tree.body[2]
|
|
112
|
+
assert _get_docstring(fn) == "Function doc."
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_get_docstring_absent_returns_none():
|
|
116
|
+
tree = _parse("class C: ...\ndef f(): ...\n")
|
|
117
|
+
assert _get_docstring(tree) is None
|
|
118
|
+
assert _get_docstring(tree.body[0]) is None
|
|
119
|
+
assert _get_docstring(tree.body[1]) is None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_get_docstring_dedents_and_strips():
|
|
123
|
+
tree = _parse('def f():\n """First line.\n\n Second line.\n """\n ...\n')
|
|
124
|
+
# pyast.get_docstring(clean=True) already handles dedent, but our
|
|
125
|
+
# normalisation strips trailing whitespace too.
|
|
126
|
+
doc = _get_docstring(tree.body[0])
|
|
127
|
+
assert doc == "First line.\n\nSecond line."
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_indent_preserves_blank_lines():
|
|
131
|
+
assert _indent("a\n\nb", " ") == " a\n\n b"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_render_module_end_to_end(tmp_path):
|
|
135
|
+
"""End-to-end: synthetic stub rendered through _render_module should
|
|
136
|
+
include class header, constructor overloads, methods, docstring, and a
|
|
137
|
+
trailing blank line after docstrings so the next item isn't swallowed."""
|
|
138
|
+
lib = tmp_path / "lib"
|
|
139
|
+
pkg = lib / "pkg"
|
|
140
|
+
pkg.mkdir(parents=True)
|
|
141
|
+
(pkg / "mod.py").write_text('"""Module doc."""\n'
|
|
142
|
+
"class Foo:\n"
|
|
143
|
+
' """Class doc."""\n'
|
|
144
|
+
" def __init__(self, x: int) -> None: ...\n"
|
|
145
|
+
" def __init__(self, x: str) -> None: ...\n"
|
|
146
|
+
" def method(self, y: int) -> int:\n"
|
|
147
|
+
' """Method doc."""\n'
|
|
148
|
+
" ...\n"
|
|
149
|
+
" def another(self) -> None: ...\n"
|
|
150
|
+
"VERSION: str = '1.0'\n")
|
|
151
|
+
|
|
152
|
+
result = _render_module(pkg / "mod.py", lib)
|
|
153
|
+
assert result is not None
|
|
154
|
+
module_name, body = result
|
|
155
|
+
assert module_name == "pkg.mod"
|
|
156
|
+
|
|
157
|
+
# Module docstring appears.
|
|
158
|
+
assert "Module doc." in body
|
|
159
|
+
# Class header + class docstring.
|
|
160
|
+
assert "### Foo" in body
|
|
161
|
+
assert "Class doc." in body
|
|
162
|
+
# Both __init__ overloads are under Constructor, not Methods.
|
|
163
|
+
assert "**Constructor:**" in body
|
|
164
|
+
assert body.count("Foo(x: int)") == 1
|
|
165
|
+
assert body.count("Foo(x: str)") == 1
|
|
166
|
+
# Methods section exists and contains visible methods.
|
|
167
|
+
assert "**Methods:**" in body
|
|
168
|
+
assert "method(self, y: int) -> int" in body
|
|
169
|
+
assert "another(self) -> None" in body
|
|
170
|
+
# Method docstring appears.
|
|
171
|
+
assert "Method doc." in body
|
|
172
|
+
# Module-level typed constant rendered.
|
|
173
|
+
assert "`VERSION: str`" in body
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_render_module_skips_when_nothing_to_document(tmp_path):
|
|
177
|
+
"""A module with only imports/private helpers produces no section."""
|
|
178
|
+
lib = tmp_path / "lib"
|
|
179
|
+
lib.mkdir()
|
|
180
|
+
(lib / "empty.py").write_text("from os import path\n"
|
|
181
|
+
"_private = 1\n"
|
|
182
|
+
"def _helper(): ...\n")
|
|
183
|
+
assert _render_module(lib / "empty.py", lib) is None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def test_render_module_handles_syntax_error(tmp_path):
|
|
187
|
+
lib = tmp_path / "lib"
|
|
188
|
+
lib.mkdir()
|
|
189
|
+
(lib / "broken.py").write_text("this is not ::: valid python\n")
|
|
190
|
+
assert _render_module(lib / "broken.py", lib) is None
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_dump_builtin_types_emits_toc_and_modules():
|
|
194
|
+
"""Smoke test: run the real dump against lib/tpy/ and verify structure."""
|
|
195
|
+
buf = io.StringIO()
|
|
196
|
+
with redirect_stdout(buf):
|
|
197
|
+
dump_builtin_types()
|
|
198
|
+
out = buf.getvalue()
|
|
199
|
+
# Header + TOC markers.
|
|
200
|
+
assert out.startswith("# TurboPython API Reference")
|
|
201
|
+
assert "## Modules" in out
|
|
202
|
+
# A few well-known modules appear both in the TOC and as sections.
|
|
203
|
+
for mod in ("math", "dataclasses", "tpy.version"):
|
|
204
|
+
assert f"[`{mod}`]" in out, f"missing TOC entry for {mod}"
|
|
205
|
+
assert f"## `{mod}`" in out, f"missing section for {mod}"
|
|
206
|
+
# tpy.version surfaces its typed constants (AnnAssign handling).
|
|
207
|
+
assert "`__version__: Final[str]`" in out
|
|
208
|
+
assert "`is_compiled: Final[bool]`" in out
|
|
209
|
+
# Internal _bindings.* layers are filtered out (no TOC entry, no section
|
|
210
|
+
# header). Their names may still appear in body text of other modules
|
|
211
|
+
# that reference them (e.g. `re` docstring), so check only structural
|
|
212
|
+
# positions, not raw substrings. Regex catches any future _bindings.*
|
|
213
|
+
# module without needing per-name updates.
|
|
214
|
+
assert not re.search(r"\[`_bindings\.[^`]+`\]", out)
|
|
215
|
+
assert not re.search(r"^## `_bindings\.[^`]+`", out, re.MULTILINE)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_gfm_anchor():
|
|
219
|
+
# Backticks and dots stripped, underscores preserved, lowercased.
|
|
220
|
+
assert _gfm_anchor("`bisect`") == "bisect"
|
|
221
|
+
assert _gfm_anchor("`tpy._core._types`") == "tpy_core_types"
|
|
222
|
+
assert _gfm_anchor("`tplib.json.parser`") == "tplibjsonparser"
|
|
223
|
+
assert _gfm_anchor("`tplib.array_list`") == "tplibarray_list"
|
|
224
|
+
# Spaces become hyphens.
|
|
225
|
+
assert _gfm_anchor("Hello World") == "hello-world"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Smoke tests for install_docs."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .install_docs import install_agent_docs, agents_md_snippet
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_install_creates_both_files(tmp_path: Path) -> None:
|
|
9
|
+
target = tmp_path / "mydocs"
|
|
10
|
+
written = install_agent_docs(target)
|
|
11
|
+
|
|
12
|
+
names = {p.name for p in written}
|
|
13
|
+
assert names == {
|
|
14
|
+
"TPY_FOR_AGENTS.md",
|
|
15
|
+
"TPY_LANGUAGE_FEATURES.md",
|
|
16
|
+
"TPY_STDLIB_ROADMAP.md",
|
|
17
|
+
"TPY_API_REFERENCE.md",
|
|
18
|
+
}
|
|
19
|
+
for p in written:
|
|
20
|
+
assert p.is_file()
|
|
21
|
+
assert p.read_text().startswith("#")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_install_creates_missing_dir(tmp_path: Path) -> None:
|
|
25
|
+
target = tmp_path / "a" / "b" / "c"
|
|
26
|
+
install_agent_docs(target)
|
|
27
|
+
assert target.is_dir()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_install_overwrites(tmp_path: Path) -> None:
|
|
31
|
+
target = tmp_path / "docs"
|
|
32
|
+
target.mkdir()
|
|
33
|
+
stale = target / "TPY_FOR_AGENTS.md"
|
|
34
|
+
stale.write_text("old content")
|
|
35
|
+
|
|
36
|
+
install_agent_docs(target)
|
|
37
|
+
|
|
38
|
+
assert stale.read_text() != "old content"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_install_rejects_existing_file(tmp_path: Path) -> None:
|
|
42
|
+
target = tmp_path / "not_a_dir"
|
|
43
|
+
target.write_text("I am a regular file")
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
install_agent_docs(target)
|
|
47
|
+
except NotADirectoryError as e:
|
|
48
|
+
assert "not a directory" in str(e)
|
|
49
|
+
else:
|
|
50
|
+
raise AssertionError("expected NotADirectoryError")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_snippet_uses_provided_path() -> None:
|
|
54
|
+
snippet = agents_md_snippet(Path("mydocs"))
|
|
55
|
+
assert "mydocs/TPY_FOR_AGENTS.md" in snippet
|
|
56
|
+
assert "mydocs/TPY_LANGUAGE_FEATURES.md" in snippet
|
|
57
|
+
assert "mydocs/TPY_STDLIB_ROADMAP.md" in snippet
|
|
58
|
+
assert "mydocs/TPY_API_REFERENCE.md" in snippet
|
|
59
|
+
assert "tpyc --install-agent-docs mydocs" in snippet
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_snippet_strips_trailing_slash() -> None:
|
|
63
|
+
snippet = agents_md_snippet(Path("docs/"))
|
|
64
|
+
assert "docs/TPY_FOR_AGENTS.md" in snippet
|
|
65
|
+
assert "docs//TPY_FOR_AGENTS.md" not in snippet
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Tests for `CodeGenContext.local_cpp_form` and the lift-helper predicates.
|
|
2
|
+
|
|
3
|
+
The classifier reads several parallel `set[str]` fields plus
|
|
4
|
+
`current_func_params`. These tests stub those attributes directly to
|
|
5
|
+
exercise priority order and helper-method dispatch without spinning up
|
|
6
|
+
a full analyzer.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from types import SimpleNamespace
|
|
10
|
+
|
|
11
|
+
from .codegen_cpp.context import (
|
|
12
|
+
CodeGenContext,
|
|
13
|
+
LocalCppForm,
|
|
14
|
+
)
|
|
15
|
+
from .typesys import (
|
|
16
|
+
OwnType,
|
|
17
|
+
UnionType,
|
|
18
|
+
NominalType,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _make_ctx(**overrides) -> SimpleNamespace:
|
|
23
|
+
"""Build a stub with just the attrs/methods `local_cpp_form` reads."""
|
|
24
|
+
base = SimpleNamespace(
|
|
25
|
+
pointer_locals=set(),
|
|
26
|
+
optional_locals=set(),
|
|
27
|
+
ptr_variant_locals=set(),
|
|
28
|
+
storage_form_tuple_locals=set(),
|
|
29
|
+
storage_form_optional_locals=set(),
|
|
30
|
+
current_func_params={},
|
|
31
|
+
)
|
|
32
|
+
for k, v in overrides.items():
|
|
33
|
+
setattr(base, k, v)
|
|
34
|
+
# Bind the real classifier methods to the stub so the lift helpers
|
|
35
|
+
# (which call `self.local_cpp_form(...)` etc.) work.
|
|
36
|
+
base.is_own_ptr_variant_param = (
|
|
37
|
+
lambda name: CodeGenContext.is_own_ptr_variant_param(base, name)
|
|
38
|
+
)
|
|
39
|
+
base.is_ptr_variant_union = (
|
|
40
|
+
lambda u: CodeGenContext.is_ptr_variant_union(base, u)
|
|
41
|
+
)
|
|
42
|
+
base.local_cpp_form = (
|
|
43
|
+
lambda name: CodeGenContext.local_cpp_form(base, name)
|
|
44
|
+
)
|
|
45
|
+
return base
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _form(ctx, name: str) -> LocalCppForm:
|
|
49
|
+
return CodeGenContext.local_cpp_form(ctx, name)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TestLocalCppFormClassification:
|
|
53
|
+
def test_default_is_value(self):
|
|
54
|
+
ctx = _make_ctx()
|
|
55
|
+
assert _form(ctx, "x") is LocalCppForm.VALUE
|
|
56
|
+
|
|
57
|
+
def test_pointer_only(self):
|
|
58
|
+
ctx = _make_ctx(pointer_locals={"p"})
|
|
59
|
+
assert _form(ctx, "p") is LocalCppForm.POINTER
|
|
60
|
+
|
|
61
|
+
def test_ptr_variant(self):
|
|
62
|
+
ctx = _make_ctx(ptr_variant_locals={"u"})
|
|
63
|
+
assert _form(ctx, "u") is LocalCppForm.PTR_VARIANT
|
|
64
|
+
|
|
65
|
+
def test_storage_tuple(self):
|
|
66
|
+
ctx = _make_ctx(storage_form_tuple_locals={"t"})
|
|
67
|
+
assert _form(ctx, "t") is LocalCppForm.STORAGE_TUPLE
|
|
68
|
+
|
|
69
|
+
def test_storage_optional(self):
|
|
70
|
+
# Loop var iterating `list[P|None]` / `dict[K, P|None]`, or
|
|
71
|
+
# comp/genexpr unpack var bound from a storage-form tuple slot.
|
|
72
|
+
ctx = _make_ctx(storage_form_optional_locals={"it"})
|
|
73
|
+
assert _form(ctx, "it") is LocalCppForm.STORAGE_OPTIONAL
|
|
74
|
+
|
|
75
|
+
def test_optional_storage_priority_over_storage_optional(self):
|
|
76
|
+
# The two sets are producer-disjoint today, but a same-name
|
|
77
|
+
# shadow (Own[Opt[T_ref]] param + a loop var that reuses the
|
|
78
|
+
# name) could put both populations on one name. Both lift via
|
|
79
|
+
# `optional_to_ptr`; the classifier resolves to OPTIONAL_STORAGE
|
|
80
|
+
# so the existing OPTIONAL_STORAGE consumer paths fire.
|
|
81
|
+
ctx = _make_ctx(
|
|
82
|
+
pointer_locals={"p"},
|
|
83
|
+
optional_locals={"p"},
|
|
84
|
+
storage_form_optional_locals={"p"},
|
|
85
|
+
)
|
|
86
|
+
assert _form(ctx, "p") is LocalCppForm.OPTIONAL_STORAGE
|
|
87
|
+
|
|
88
|
+
def test_optional_storage_in_both_sets(self):
|
|
89
|
+
# Own[Opt[T_ref]] params land in BOTH pointer_locals and
|
|
90
|
+
# optional_locals; the classifier must return OPTIONAL_STORAGE,
|
|
91
|
+
# not POINTER.
|
|
92
|
+
ctx = _make_ctx(pointer_locals={"p"}, optional_locals={"p"})
|
|
93
|
+
assert _form(ctx, "p") is LocalCppForm.OPTIONAL_STORAGE
|
|
94
|
+
|
|
95
|
+
def test_value_variant_via_own_union_param(self):
|
|
96
|
+
# Own[Union(Dog, Cat)] with both non-value records -> ptr-variant
|
|
97
|
+
# union -> classifier returns VALUE_VARIANT for the param name.
|
|
98
|
+
union = UnionType((NominalType("Dog"), NominalType("Cat")))
|
|
99
|
+
own = OwnType(union)
|
|
100
|
+
ctx = _make_ctx(current_func_params={"animal": own})
|
|
101
|
+
# Force the union to be ptr-variant: stub `is_ptr_variant_union`
|
|
102
|
+
# to return True. (The real predicate consults the record
|
|
103
|
+
# registry; we bypass it here.)
|
|
104
|
+
ctx.is_ptr_variant_union = lambda u: u is union
|
|
105
|
+
assert _form(ctx, "animal") is LocalCppForm.VALUE_VARIANT
|
|
106
|
+
|
|
107
|
+
def test_value_variant_takes_priority_over_ptr_variant_locals(self):
|
|
108
|
+
# If a single name somehow appears in both populations, the param
|
|
109
|
+
# ABI form (VALUE_VARIANT) wins -- it reflects the storage shape
|
|
110
|
+
# the caller produced, not the local-decl shape.
|
|
111
|
+
union = UnionType((NominalType("Dog"), NominalType("Cat")))
|
|
112
|
+
own = OwnType(union)
|
|
113
|
+
ctx = _make_ctx(
|
|
114
|
+
ptr_variant_locals={"animal"},
|
|
115
|
+
current_func_params={"animal": own},
|
|
116
|
+
)
|
|
117
|
+
ctx.is_ptr_variant_union = lambda u: u is union
|
|
118
|
+
assert _form(ctx, "animal") is LocalCppForm.VALUE_VARIANT
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class TestLiftHelpers:
|
|
122
|
+
def test_needs_optional_to_ptr_lift(self):
|
|
123
|
+
ctx = _make_ctx(pointer_locals={"p"}, optional_locals={"p"})
|
|
124
|
+
assert CodeGenContext.needs_optional_to_ptr_lift(ctx, "p") is True
|
|
125
|
+
# Plain POINTER name -- pointer_locals only, no optional_locals.
|
|
126
|
+
ctx2 = _make_ctx(pointer_locals={"q"})
|
|
127
|
+
assert CodeGenContext.needs_optional_to_ptr_lift(ctx2, "q") is False
|
|
128
|
+
assert CodeGenContext.needs_optional_to_ptr_lift(ctx, "missing") is False
|
|
129
|
+
|
|
130
|
+
def test_needs_to_ptr_variant_lift(self):
|
|
131
|
+
union = UnionType((NominalType("Dog"), NominalType("Cat")))
|
|
132
|
+
ctx = _make_ctx(current_func_params={"animal": OwnType(union)})
|
|
133
|
+
ctx.is_ptr_variant_union = lambda u: u is union
|
|
134
|
+
assert CodeGenContext.needs_to_ptr_variant_lift(ctx, "animal") is True
|
|
135
|
+
assert CodeGenContext.needs_to_ptr_variant_lift(ctx, "x") is False
|