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.
Files changed (333) hide show
  1. tpy_lang-0.3.0.dev0.dist-info/METADATA +151 -0
  2. tpy_lang-0.3.0.dev0.dist-info/RECORD +333 -0
  3. tpy_lang-0.3.0.dev0.dist-info/WHEEL +4 -0
  4. tpy_lang-0.3.0.dev0.dist-info/entry_points.txt +3 -0
  5. tpyc/__init__.py +104 -0
  6. tpyc/__main__.py +6 -0
  7. tpyc/_buildinfo.py +1 -0
  8. tpyc/_data/docs/LANGUAGE_FEATURES.md +6278 -0
  9. tpyc/_data/docs/STDLIB_ROADMAP.md +1258 -0
  10. tpyc/_data/docs/TPY_FOR_AGENTS.md +556 -0
  11. tpyc/_data/lib/tpy/_bindings/__init__.py +6 -0
  12. tpyc/_data/lib/tpy/_bindings/pcre2.py +173 -0
  13. tpyc/_data/lib/tpy/_bindings/posix_socket.py +161 -0
  14. tpyc/_data/lib/tpy/_functools_macros.py +80 -0
  15. tpyc/_data/lib/tpy/_macro_helpers.py +161 -0
  16. tpyc/_data/lib/tpy/argparse.py +2062 -0
  17. tpyc/_data/lib/tpy/asyncio/__init__.py +744 -0
  18. tpyc/_data/lib/tpy/asyncio/_executor.py +515 -0
  19. tpyc/_data/lib/tpy/base64.py +410 -0
  20. tpyc/_data/lib/tpy/bisect.py +39 -0
  21. tpyc/_data/lib/tpy/builtins.py +38 -0
  22. tpyc/_data/lib/tpy/dataclasses.py +354 -0
  23. tpyc/_data/lib/tpy/enum.py +23 -0
  24. tpyc/_data/lib/tpy/functools.py +33 -0
  25. tpyc/_data/lib/tpy/hashlib.py +206 -0
  26. tpyc/_data/lib/tpy/heapq.py +118 -0
  27. tpyc/_data/lib/tpy/io.py +395 -0
  28. tpyc/_data/lib/tpy/json.py +221 -0
  29. tpyc/_data/lib/tpy/math.py +406 -0
  30. tpyc/_data/lib/tpy/random.py +597 -0
  31. tpyc/_data/lib/tpy/re.py +467 -0
  32. tpyc/_data/lib/tpy/socket.py +379 -0
  33. tpyc/_data/lib/tpy/struct.py +178 -0
  34. tpyc/_data/lib/tpy/sys.py +40 -0
  35. tpyc/_data/lib/tpy/time.py +39 -0
  36. tpyc/_data/lib/tpy/tpy/__init__.py +78 -0
  37. tpyc/_data/lib/tpy/tpy/_bootstrap/__init__.py +10 -0
  38. tpyc/_data/lib/tpy/tpy/_bootstrap/_decorators.py +37 -0
  39. tpyc/_data/lib/tpy/tpy/_bootstrap/_extern.py +64 -0
  40. tpyc/_data/lib/tpy/tpy/_builtins/__init__.py +11 -0
  41. tpyc/_data/lib/tpy/tpy/_builtins/_bytes.py +378 -0
  42. tpyc/_data/lib/tpy/tpy/_builtins/_dict.py +151 -0
  43. tpyc/_data/lib/tpy/tpy/_builtins/_exceptions.py +125 -0
  44. tpyc/_data/lib/tpy/tpy/_builtins/_funcs.py +681 -0
  45. tpyc/_data/lib/tpy/tpy/_builtins/_io.py +97 -0
  46. tpyc/_data/lib/tpy/tpy/_builtins/_list.py +127 -0
  47. tpyc/_data/lib/tpy/tpy/_builtins/_range.py +52 -0
  48. tpyc/_data/lib/tpy/tpy/_builtins/_set.py +139 -0
  49. tpyc/_data/lib/tpy/tpy/_builtins/_super.py +11 -0
  50. tpyc/_data/lib/tpy/tpy/_builtins/_types.py +661 -0
  51. tpyc/_data/lib/tpy/tpy/_core/__init__.py +23 -0
  52. tpyc/_data/lib/tpy/tpy/_core/_bytes_view.py +129 -0
  53. tpyc/_data/lib/tpy/tpy/_core/_containers.py +137 -0
  54. tpyc/_data/lib/tpy/tpy/_core/_functions.py +40 -0
  55. tpyc/_data/lib/tpy/tpy/_core/_types.py +2061 -0
  56. tpyc/_data/lib/tpy/tpy/_typing/__init__.py +77 -0
  57. tpyc/_data/lib/tpy/tpy/_version.py +29 -0
  58. tpyc/_data/lib/tpy/tpy/bits.py +28 -0
  59. tpyc/_data/lib/tpy/tpy/coro/__init__.py +127 -0
  60. tpyc/_data/lib/tpy/tpy/extern.py +8 -0
  61. tpyc/_data/lib/tpy/tpy/mem.py +49 -0
  62. tpyc/_data/lib/tpy/tpy/unsafe.py +195 -0
  63. tpyc/_data/lib/tpy/tpy/version.py +21 -0
  64. tpyc/_data/lib/tpy/typing.py +13 -0
  65. tpyc/_data/runtime/cpp/include/tpy/any.hpp +461 -0
  66. tpyc/_data/runtime/cpp/include/tpy/as_ostream.hpp +117 -0
  67. tpyc/_data/runtime/cpp/include/tpy/async.hpp +76 -0
  68. tpyc/_data/runtime/cpp/include/tpy/bigint.hpp +1343 -0
  69. tpyc/_data/runtime/cpp/include/tpy/builtins.hpp +400 -0
  70. tpyc/_data/runtime/cpp/include/tpy/bytes_ops.hpp +469 -0
  71. tpyc/_data/runtime/cpp/include/tpy/container_ops.hpp +487 -0
  72. tpyc/_data/runtime/cpp/include/tpy/copy_iter.hpp +82 -0
  73. tpyc/_data/runtime/cpp/include/tpy/core.hpp +558 -0
  74. tpyc/_data/runtime/cpp/include/tpy/dict_ops.hpp +289 -0
  75. tpyc/_data/runtime/cpp/include/tpy/dunder.hpp +750 -0
  76. tpyc/_data/runtime/cpp/include/tpy/dynamic.hpp +44 -0
  77. tpyc/_data/runtime/cpp/include/tpy/enum.hpp +40 -0
  78. tpyc/_data/runtime/cpp/include/tpy/file.hpp +245 -0
  79. tpyc/_data/runtime/cpp/include/tpy/fixed_int.hpp +317 -0
  80. tpyc/_data/runtime/cpp/include/tpy/format.hpp +954 -0
  81. tpyc/_data/runtime/cpp/include/tpy/frame_slot.hpp +120 -0
  82. tpyc/_data/runtime/cpp/include/tpy/generator.hpp +47 -0
  83. tpyc/_data/runtime/cpp/include/tpy/iterable_ops.hpp +122 -0
  84. tpyc/_data/runtime/cpp/include/tpy/itertools.hpp +749 -0
  85. tpyc/_data/runtime/cpp/include/tpy/next_iter.hpp +82 -0
  86. tpyc/_data/runtime/cpp/include/tpy/ordered_map.hpp +518 -0
  87. tpyc/_data/runtime/cpp/include/tpy/ordered_set.hpp +337 -0
  88. tpyc/_data/runtime/cpp/include/tpy/own_iter.hpp +54 -0
  89. tpyc/_data/runtime/cpp/include/tpy/pascal_graph_sdl.hpp +192 -0
  90. tpyc/_data/runtime/cpp/include/tpy/printing.hpp +302 -0
  91. tpyc/_data/runtime/cpp/include/tpy/protocols.hpp +61 -0
  92. tpyc/_data/runtime/cpp/include/tpy/range.hpp +115 -0
  93. tpyc/_data/runtime/cpp/include/tpy/ranges.hpp +212 -0
  94. tpyc/_data/runtime/cpp/include/tpy/set_ops.hpp +265 -0
  95. tpyc/_data/runtime/cpp/include/tpy/slice.hpp +47 -0
  96. tpyc/_data/runtime/cpp/include/tpy/span_iter.hpp +42 -0
  97. tpyc/_data/runtime/cpp/include/tpy/stdlib/math.hpp +41 -0
  98. tpyc/_data/runtime/cpp/include/tpy/stdlib/pcre2_h.hpp +96 -0
  99. tpyc/_data/runtime/cpp/include/tpy/stdlib/random.hpp +25 -0
  100. tpyc/_data/runtime/cpp/include/tpy/stdlib/socket_h.hpp +145 -0
  101. tpyc/_data/runtime/cpp/include/tpy/stdlib/time.hpp +62 -0
  102. tpyc/_data/runtime/cpp/include/tpy/system.hpp +121 -0
  103. tpyc/_data/runtime/cpp/include/tpy/throwable.hpp +55 -0
  104. tpyc/_data/runtime/cpp/include/tpy/tpy.hpp +156 -0
  105. tpyc/_data/runtime/cpp/include/tpy/type_name.hpp +77 -0
  106. tpyc/_data/runtime/cpp/include/tpy/type_traits.hpp +240 -0
  107. tpyc/_data/runtime/cpp/include/tpy/uninit_array_storage.hpp +250 -0
  108. tpyc/_data/runtime/cpp/include/tpy/uninit_heap_storage.hpp +277 -0
  109. tpyc/_data/runtime/cpp/include/tpy/varargs.hpp +174 -0
  110. tpyc/_data/runtime/cpp/include/tpy/variant_ref.hpp +118 -0
  111. tpyc/_data/runtime/cpp/src/stdlib/socket_impl.cpp +104 -0
  112. tpyc/_data/runtime/cpp/third_party/README.md +58 -0
  113. tpyc/_data/runtime/cpp/third_party/pcre2/AUTHORS +36 -0
  114. tpyc/_data/runtime/cpp/third_party/pcre2/CMakeLists.txt +1233 -0
  115. tpyc/_data/runtime/cpp/third_party/pcre2/COPYING +5 -0
  116. tpyc/_data/runtime/cpp/third_party/pcre2/ChangeLog +3097 -0
  117. tpyc/_data/runtime/cpp/third_party/pcre2/HACKING +853 -0
  118. tpyc/_data/runtime/cpp/third_party/pcre2/INSTALL +368 -0
  119. tpyc/_data/runtime/cpp/third_party/pcre2/LICENCE +94 -0
  120. tpyc/_data/runtime/cpp/third_party/pcre2/NEWS +492 -0
  121. tpyc/_data/runtime/cpp/third_party/pcre2/NON-AUTOTOOLS-BUILD +430 -0
  122. tpyc/_data/runtime/cpp/third_party/pcre2/README +956 -0
  123. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/COPYING-CMAKE-SCRIPTS +22 -0
  124. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindEditline.cmake +16 -0
  125. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindPackageHandleStandardArgs.cmake +58 -0
  126. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindReadline.cmake +29 -0
  127. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config-version.cmake.in +15 -0
  128. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config.cmake.in +148 -0
  129. tpyc/_data/runtime/cpp/third_party/pcre2/config-cmake.h.in +56 -0
  130. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-16.pc.in +13 -0
  131. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-32.pc.in +13 -0
  132. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-8.pc.in +13 -0
  133. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-posix.pc.in +13 -0
  134. tpyc/_data/runtime/cpp/third_party/pcre2/pcre2-config.in +121 -0
  135. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h +483 -0
  136. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.generic +483 -0
  137. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.in +460 -0
  138. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h +1010 -0
  139. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.generic +1010 -0
  140. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.in +1010 -0
  141. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_auto_possess.c +1371 -0
  142. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c +196 -0
  143. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c.dist +196 -0
  144. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chkdint.c +96 -0
  145. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_compile.c +11001 -0
  146. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_config.c +252 -0
  147. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_context.c +510 -0
  148. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_convert.c +1189 -0
  149. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dfa_match.c +4119 -0
  150. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dftables.c +297 -0
  151. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_error.c +345 -0
  152. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_extuni.c +162 -0
  153. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_find_bracket.c +219 -0
  154. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_fuzzsupport.c +792 -0
  155. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_internal.h +2084 -0
  156. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_intmodedep.h +940 -0
  157. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_compile.c +14972 -0
  158. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_match.c +200 -0
  159. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_misc.c +234 -0
  160. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_neon_inc.h +354 -0
  161. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_simd_inc.h +2355 -0
  162. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_test.c +2528 -0
  163. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_maketables.c +165 -0
  164. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match.c +7777 -0
  165. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match_data.c +185 -0
  166. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_newline.c +243 -0
  167. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ord2utf.c +120 -0
  168. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_pattern_info.c +432 -0
  169. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_printint.c +886 -0
  170. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_script_run.c +344 -0
  171. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_serialize.c +286 -0
  172. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_string_utils.c +237 -0
  173. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_study.c +1915 -0
  174. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substitute.c +1009 -0
  175. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substring.c +550 -0
  176. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_tables.c +234 -0
  177. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucd.c +5460 -0
  178. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucp.h +396 -0
  179. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucptables.c +1533 -0
  180. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_valid_utf.c +398 -0
  181. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_xclass.c +308 -0
  182. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2demo.c +497 -0
  183. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2grep.c +4606 -0
  184. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.c +425 -0
  185. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.h +187 -0
  186. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix_test.c +209 -0
  187. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2test.c +9708 -0
  188. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorApple.c +137 -0
  189. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorCore.c +327 -0
  190. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorFreeBSD.c +89 -0
  191. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorPosix.c +62 -0
  192. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorWindows.c +40 -0
  193. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorNetBSD.c +72 -0
  194. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorPosix.c +172 -0
  195. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorPosix.c +141 -0
  196. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorWindows.c +102 -0
  197. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfig.h +142 -0
  198. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigCPU.h +188 -0
  199. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigInternal.h +907 -0
  200. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.c +3561 -0
  201. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.h +2466 -0
  202. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_32.c +4636 -0
  203. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_64.c +3491 -0
  204. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_T2_32.c +4302 -0
  205. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeLOONGARCH_64.c +3765 -0
  206. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_32.c +472 -0
  207. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_64.c +387 -0
  208. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_common.c +4259 -0
  209. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_32.c +485 -0
  210. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_64.c +719 -0
  211. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_common.c +3161 -0
  212. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_32.c +142 -0
  213. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_64.c +222 -0
  214. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_common.c +3121 -0
  215. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeS390X.c +4526 -0
  216. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_32.c +1685 -0
  217. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_64.c +1398 -0
  218. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_common.c +5001 -0
  219. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitSerialize.c +516 -0
  220. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitUtils.c +344 -0
  221. tpyc/_data/runtime/cpp/third_party/pcre2.sources.txt +54 -0
  222. tpyc/_data/runtime/cpp/third_party/pcre2.vendor.json +7 -0
  223. tpyc/build/__init__.py +7 -0
  224. tpyc/build/pcre2.py +122 -0
  225. tpyc/build/third_party.py +413 -0
  226. tpyc/cli.py +822 -0
  227. tpyc/codegen_cpp/__init__.py +18 -0
  228. tpyc/codegen_cpp/builtins.py +484 -0
  229. tpyc/codegen_cpp/context.py +2064 -0
  230. tpyc/codegen_cpp/expressions.py +5940 -0
  231. tpyc/codegen_cpp/functions.py +1913 -0
  232. tpyc/codegen_cpp/gen_async.py +3258 -0
  233. tpyc/codegen_cpp/gen_generators.py +657 -0
  234. tpyc/codegen_cpp/generator.py +2258 -0
  235. tpyc/codegen_cpp/match.py +1997 -0
  236. tpyc/codegen_cpp/param_const.py +172 -0
  237. tpyc/codegen_cpp/protocols.py +907 -0
  238. tpyc/codegen_cpp/records.py +1654 -0
  239. tpyc/codegen_cpp/resumable_cfg.py +1651 -0
  240. tpyc/codegen_cpp/statements.py +4963 -0
  241. tpyc/codegen_cpp/string_dispatch.py +76 -0
  242. tpyc/codegen_cpp/test_context.py +46 -0
  243. tpyc/codegen_cpp/test_param_const.py +113 -0
  244. tpyc/codegen_cpp/test_resumable_cfg.py +182 -0
  245. tpyc/codegen_cpp/type_resolution.py +53 -0
  246. tpyc/codegen_cpp/types.py +436 -0
  247. tpyc/codegen_cpp/variant_access.py +135 -0
  248. tpyc/coercions.py +749 -0
  249. tpyc/compilation_context.py +57 -0
  250. tpyc/compiler.py +3945 -0
  251. tpyc/cycle_detection.py +358 -0
  252. tpyc/diagnostics.py +135 -0
  253. tpyc/dump_types.py +353 -0
  254. tpyc/frontend_diagnostics.py +47 -0
  255. tpyc/frontend_ir/__init__.py +140 -0
  256. tpyc/frontend_ir/lower.py +1098 -0
  257. tpyc/frontend_ir/nodes.py +718 -0
  258. tpyc/frontend_ir/resolver_adapter.py +151 -0
  259. tpyc/frontend_plugin.py +209 -0
  260. tpyc/install_docs.py +81 -0
  261. tpyc/liveness.py +756 -0
  262. tpyc/macro_api.py +1724 -0
  263. tpyc/macro_loader.py +497 -0
  264. tpyc/module_names.py +64 -0
  265. tpyc/modules/__init__.py +31 -0
  266. tpyc/modules/defs.py +89 -0
  267. tpyc/modules/registry.py +36 -0
  268. tpyc/modules/resolver.py +192 -0
  269. tpyc/modules/type_resolution.py +629 -0
  270. tpyc/namespace.py +172 -0
  271. tpyc/parse/__init__.py +84 -0
  272. tpyc/parse/imports.py +490 -0
  273. tpyc/parse/nodes.py +1732 -0
  274. tpyc/parse/parser.py +4043 -0
  275. tpyc/parse/resolve_refs.py +466 -0
  276. tpyc/parse/type_resolver.py +1060 -0
  277. tpyc/prescan.py +254 -0
  278. tpyc/qnames.py +149 -0
  279. tpyc/repl.py +529 -0
  280. tpyc/repl_backends.py +848 -0
  281. tpyc/sema/__init__.py +21 -0
  282. tpyc/sema/analyzer.py +3625 -0
  283. tpyc/sema/bound_check.py +72 -0
  284. tpyc/sema/builder_trace.py +684 -0
  285. tpyc/sema/calls.py +5406 -0
  286. tpyc/sema/compatibility.py +2107 -0
  287. tpyc/sema/context.py +1243 -0
  288. tpyc/sema/expressions.py +3737 -0
  289. tpyc/sema/flow_facts.py +199 -0
  290. tpyc/sema/init_tracker.py +150 -0
  291. tpyc/sema/list_literals.py +69 -0
  292. tpyc/sema/literal_utils.py +27 -0
  293. tpyc/sema/local_deduction.py +1088 -0
  294. tpyc/sema/macros.py +179 -0
  295. tpyc/sema/match.py +1177 -0
  296. tpyc/sema/method_expansion.py +347 -0
  297. tpyc/sema/methods.py +2197 -0
  298. tpyc/sema/mutation_propagation.py +268 -0
  299. tpyc/sema/narrowing.py +857 -0
  300. tpyc/sema/numeric_lattice.py +160 -0
  301. tpyc/sema/operators.py +402 -0
  302. tpyc/sema/overloads.py +841 -0
  303. tpyc/sema/protocols.py +1209 -0
  304. tpyc/sema/reach_analysis.py +202 -0
  305. tpyc/sema/registration.py +3156 -0
  306. tpyc/sema/scope_tracker.py +193 -0
  307. tpyc/sema/statements.py +4426 -0
  308. tpyc/sema/type_ops.py +1879 -0
  309. tpyc/sema/value_range.py +181 -0
  310. tpyc/symbol_binding.py +259 -0
  311. tpyc/test_c3_mro.py +208 -0
  312. tpyc/test_cli_argv.py +52 -0
  313. tpyc/test_compiler.py +559 -0
  314. tpyc/test_contains_type_param.py +101 -0
  315. tpyc/test_cycle_detection.py +221 -0
  316. tpyc/test_dump_types.py +225 -0
  317. tpyc/test_install_docs.py +65 -0
  318. tpyc/test_local_cpp_form.py +135 -0
  319. tpyc/test_macro_loader.py +76 -0
  320. tpyc/test_method_expansion.py +254 -0
  321. tpyc/test_nominal_identity.py +182 -0
  322. tpyc/test_overloads.py +410 -0
  323. tpyc/test_parse.py +303 -0
  324. tpyc/test_parse_type_ref.py +506 -0
  325. tpyc/test_parse_version_info.py +58 -0
  326. tpyc/test_reach_analysis.py +72 -0
  327. tpyc/test_ref_type.py +216 -0
  328. tpyc/test_send_sync_substitution.py +276 -0
  329. tpyc/test_tuple_mutation_propagation.py +206 -0
  330. tpyc/test_type_def_registry.py +1729 -0
  331. tpyc/test_union_types.py +195 -0
  332. tpyc/type_def_registry.py +975 -0
  333. 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()
@@ -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