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,181 @@
1
+ """
2
+ ValueRange -- tracks provable integer value constraints.
3
+
4
+ Represents a [lo, hi] interval with optional non_zero flag and symbolic
5
+ upper bound (hi_len_of). Used for bounds check elision and division-by-zero
6
+ check elision.
7
+
8
+ Frozen dataclass so instances can be stored in FlowFacts frozensets.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+
15
+ # INT32 bounds (loop indices and len() return Int32)
16
+ _INT32_MAX = 2**31 - 1
17
+ _INT32_MIN = -(2**31)
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class ValueRange:
22
+ """Proven integer value constraints for a variable.
23
+
24
+ lo/hi: concrete bounds (None = unbounded in that direction).
25
+ non_zero: True if the value is provably != 0.
26
+ hi_len_of: when set, hi is symbolically len(var_name) - 1.
27
+ """
28
+
29
+ lo: int | None = None
30
+ hi: int | None = None
31
+ non_zero: bool = False
32
+ hi_len_of: str | None = None
33
+
34
+ # -- Factories --
35
+
36
+ @staticmethod
37
+ def from_literal(value: int) -> ValueRange:
38
+ return ValueRange(lo=value, hi=value, non_zero=(value != 0))
39
+
40
+ @staticmethod
41
+ def from_len() -> ValueRange:
42
+ """Result of len() -- always non-negative."""
43
+ return ValueRange(lo=0, hi=_INT32_MAX)
44
+
45
+ @staticmethod
46
+ def non_negative() -> ValueRange:
47
+ return ValueRange(lo=0)
48
+
49
+ @staticmethod
50
+ def only_non_zero() -> ValueRange:
51
+ return ValueRange(non_zero=True)
52
+
53
+ @staticmethod
54
+ def for_range_index(stop_len_of: str | None = None,
55
+ stop_literal: int | None = None) -> ValueRange:
56
+ """Range fact for a for-loop variable iterating over range().
57
+
58
+ stop_len_of: container name when stop is len(container).
59
+ stop_literal: concrete stop value when known.
60
+ """
61
+ if stop_len_of is not None:
62
+ return ValueRange(lo=0, hi_len_of=stop_len_of)
63
+ if stop_literal is not None:
64
+ if stop_literal <= 0:
65
+ # Empty range -- loop body never executes, but if it did
66
+ # the variable would have no valid value. Use [0, -1].
67
+ return ValueRange(lo=0, hi=-1)
68
+ return ValueRange(lo=0, hi=stop_literal - 1)
69
+ return ValueRange(lo=0)
70
+
71
+ # -- Queries --
72
+
73
+ def is_non_negative(self) -> bool:
74
+ return self.lo is not None and self.lo >= 0
75
+
76
+ def is_bounded_by_len(self, container: str) -> bool:
77
+ """True if hi is symbolically <= len(container) - 1."""
78
+ return self.hi_len_of is not None and self.hi_len_of == container
79
+
80
+ # -- Combinators --
81
+
82
+ @staticmethod
83
+ def merge(a: ValueRange, b: ValueRange) -> ValueRange:
84
+ """Branch join (union): widens to cover both possibilities."""
85
+ if a.lo is None or b.lo is None:
86
+ lo = None
87
+ else:
88
+ lo = min(a.lo, b.lo)
89
+
90
+ # Symbolic bounds only preserved when both agree
91
+ if a.hi_len_of is not None and a.hi_len_of == b.hi_len_of:
92
+ return ValueRange(
93
+ lo=lo,
94
+ hi_len_of=a.hi_len_of,
95
+ non_zero=a.non_zero and b.non_zero,
96
+ )
97
+
98
+ if a.hi is None or b.hi is None:
99
+ hi = None
100
+ else:
101
+ hi = max(a.hi, b.hi)
102
+
103
+ return ValueRange(
104
+ lo=lo,
105
+ hi=hi,
106
+ non_zero=a.non_zero and b.non_zero,
107
+ )
108
+
109
+ @staticmethod
110
+ def intersect(a: ValueRange, b: ValueRange) -> ValueRange:
111
+ """And-composition: tightens to what both prove."""
112
+ if a.lo is None:
113
+ lo = b.lo
114
+ elif b.lo is None:
115
+ lo = a.lo
116
+ else:
117
+ lo = max(a.lo, b.lo)
118
+
119
+ # Keep symbolic bound only when both sides agree (or one is absent)
120
+ if a.hi_len_of is not None and b.hi_len_of is not None:
121
+ hi_len_of = a.hi_len_of if a.hi_len_of == b.hi_len_of else None
122
+ else:
123
+ hi_len_of = a.hi_len_of or b.hi_len_of
124
+
125
+ if hi_len_of is not None:
126
+ return ValueRange(
127
+ lo=lo,
128
+ hi_len_of=hi_len_of,
129
+ non_zero=a.non_zero or b.non_zero,
130
+ )
131
+
132
+ if a.hi is None:
133
+ hi = b.hi
134
+ elif b.hi is None:
135
+ hi = a.hi
136
+ else:
137
+ hi = min(a.hi, b.hi)
138
+
139
+ return ValueRange(
140
+ lo=lo,
141
+ hi=hi,
142
+ non_zero=a.non_zero or b.non_zero,
143
+ )
144
+
145
+ def with_non_zero(self, nz: bool = True) -> ValueRange:
146
+ """Return a copy with non_zero set."""
147
+ if self.non_zero == nz:
148
+ return self
149
+ return ValueRange(
150
+ lo=self.lo, hi=self.hi,
151
+ non_zero=nz, hi_len_of=self.hi_len_of,
152
+ )
153
+
154
+ def with_lo(self, lo: int) -> ValueRange:
155
+ """Return a copy with lo tightened (only if stricter)."""
156
+ current_lo = self.lo
157
+ if current_lo is not None and current_lo >= lo:
158
+ return self
159
+ nz = self.non_zero or lo > 0
160
+ return ValueRange(
161
+ lo=lo, hi=self.hi,
162
+ non_zero=nz, hi_len_of=self.hi_len_of,
163
+ )
164
+
165
+ def with_hi(self, hi: int) -> ValueRange:
166
+ """Return a copy with hi tightened (only if stricter)."""
167
+ current_hi = self.hi
168
+ if current_hi is not None and current_hi <= hi:
169
+ return self
170
+ nz = self.non_zero or (self.lo is not None and self.lo > 0)
171
+ return ValueRange(
172
+ lo=self.lo, hi=hi,
173
+ non_zero=nz, hi_len_of=None,
174
+ )
175
+
176
+ def with_hi_len_of(self, container: str) -> ValueRange:
177
+ """Return a copy with symbolic upper bound."""
178
+ return ValueRange(
179
+ lo=self.lo, hi=None,
180
+ non_zero=self.non_zero, hi_len_of=container,
181
+ )
tpyc/symbol_binding.py ADDED
@@ -0,0 +1,259 @@
1
+ """Per-module attribute table.
2
+
3
+ A single `dict[str, BindingCell]` per module, indexed by short local
4
+ name. Each cell holds a `SymbolBinding` describing what the name
5
+ refers to: kind (function / record / protocol / ...), the canonical
6
+ Info object, and the (defining_module, canonical_name) pair the
7
+ codegen `qualify_imported()` helper turns into a C++ qname.
8
+
9
+ Cells are mutable indirections so peer references captured during
10
+ pre-population stay live as the binding's payload Info object is
11
+ filled in by the registration paths. The binding itself is frozen --
12
+ identity refinement, when needed, happens by rebinding the cell's
13
+ `.binding` slot.
14
+
15
+ See docs/MUTUAL_IMPORTS_DESIGN.md "Universal re-export via per-module
16
+ attribute table" for the full design.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from dataclasses import dataclass
22
+ from enum import Enum
23
+
24
+
25
+ class SymbolKind(Enum):
26
+ FUNCTION = "function"
27
+ RECORD = "record"
28
+ PROTOCOL_STATIC = "protocol_static"
29
+ PROTOCOL_DYNAMIC = "protocol_dynamic"
30
+ ENUM = "enum"
31
+ VARIABLE = "variable"
32
+ TYPE_ALIAS = "type_alias"
33
+ MODULE = "module"
34
+ SUBMODULE = "submodule"
35
+ CLASS_MACRO = "class_macro"
36
+ CALL_MACRO = "call_macro"
37
+ BUILDER_MACRO = "builder_macro"
38
+ PARSER_KEYWORD = "parser_keyword"
39
+ OPAQUE = "opaque"
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class SymbolBinding:
44
+ """One name in a module's attribute table.
45
+
46
+ `info` holds the canonical Info object for the binding's kind:
47
+ FUNCTION -> list[FunctionInfo]
48
+ RECORD -> RecordInfo
49
+ PROTOCOL_* -> ProtocolInfo
50
+ ENUM -> NominalType (enum-kind)
51
+ VARIABLE -> TpyType (var's declared type)
52
+ TYPE_ALIAS -> TpyType (alias body)
53
+ MODULE / SUBMODULE -> ModuleInfo (or None for special modules)
54
+ CLASS_MACRO / ... -> macro registration object
55
+ PARSER_KEYWORD -> None
56
+ OPAQUE -> None
57
+
58
+ `defining_module` is None when the symbol is locally defined in the
59
+ module that owns this binding. For imports/re-exports it's the
60
+ *ultimate* defining module (chain-flattened): records use
61
+ `record_info.defining_module`, enums use `EnumInfo.module_name`,
62
+ functions use `FunctionInfo.originating_module`, variables use
63
+ the second pre-pop pass's chain-walk.
64
+
65
+ `canonical_name` is the symbol's name in the defining module
66
+ (== local_name when no aliasing).
67
+ """
68
+ local_name: str
69
+ kind: SymbolKind
70
+ info: object
71
+ defining_module: str | None
72
+ canonical_name: str
73
+
74
+
75
+ @dataclass
76
+ class BindingCell:
77
+ """Mutable indirection wrapping a `SymbolBinding`.
78
+
79
+ Cells are the stable references peers capture during
80
+ `bind_imports`; the cell's `.binding` slot may be rebound when
81
+ the same name is re-installed (e.g. a local definition shadowing
82
+ an earlier import in source order, or a registration path
83
+ refining a pre-pop'd skeleton). The binding objects themselves
84
+ stay frozen.
85
+ """
86
+ binding: SymbolBinding
87
+
88
+
89
+ def make_binding(
90
+ local_name: str,
91
+ kind: SymbolKind,
92
+ info: object,
93
+ *,
94
+ defining_module: str | None = None,
95
+ canonical_name: str | None = None,
96
+ ) -> SymbolBinding:
97
+ return SymbolBinding(
98
+ local_name=local_name,
99
+ kind=kind,
100
+ info=info,
101
+ defining_module=defining_module,
102
+ canonical_name=canonical_name if canonical_name is not None else local_name,
103
+ )
104
+
105
+
106
+ def install_binding(
107
+ table: 'dict[str, BindingCell] | None',
108
+ local_name: str,
109
+ kind: SymbolKind,
110
+ info: object,
111
+ *,
112
+ defining_module: str | None = None,
113
+ canonical_name: str | None = None,
114
+ ) -> None:
115
+ """Install or replace an entry in the attribute table.
116
+
117
+ No-op when `table` is None (some analyzer constructions don't have
118
+ one -- e.g. ad-hoc tests / REPL).
119
+
120
+ Cells are mutated in place when they already exist, preserving the
121
+ cell identity peer modules may have captured during `bind_imports`.
122
+ """
123
+ if table is None:
124
+ return
125
+ binding = make_binding(
126
+ local_name, kind, info,
127
+ defining_module=defining_module,
128
+ canonical_name=canonical_name,
129
+ )
130
+ cell = table.get(local_name)
131
+ if cell is None:
132
+ table[local_name] = BindingCell(binding=binding)
133
+ else:
134
+ cell.binding = binding
135
+
136
+
137
+ def protocol_kind_for(is_dynamic: bool) -> SymbolKind:
138
+ return SymbolKind.PROTOCOL_DYNAMIC if is_dynamic else SymbolKind.PROTOCOL_STATIC
139
+
140
+
141
+ def qualify_imported(binding: SymbolBinding, current_module: str) -> tuple[str, str]:
142
+ """Return the (module, canonical_name) pair to use when emitting a
143
+ use-site reference to `binding` from inside `current_module`.
144
+
145
+ The result is the input to `qualified_cpp_name(...)`; callers that
146
+ want the rendered C++ string take that extra step so this helper
147
+ stays free of the codegen-context import.
148
+ """
149
+ if binding.defining_module is None:
150
+ return (current_module, binding.canonical_name)
151
+ return (binding.defining_module, binding.canonical_name)
152
+
153
+
154
+ def lookup_qualified(
155
+ table: 'dict[str, BindingCell] | None', name: str, current_module: str,
156
+ ) -> 'tuple[str, str] | None':
157
+ """Convenience for the common codegen pattern: fetch the cell from
158
+ `table` and pass its binding through `qualify_imported`. Returns
159
+ None when the table is missing or the name isn't bound, leaving
160
+ the caller's fallback path in charge.
161
+ """
162
+ if table is None:
163
+ return None
164
+ cell = table.get(name)
165
+ if cell is None:
166
+ return None
167
+ return qualify_imported(cell.binding, current_module)
168
+
169
+
170
+ def lookup_imported(
171
+ table: 'dict[str, BindingCell] | None', name: str, *kinds: SymbolKind,
172
+ ) -> 'tuple[str, str] | None':
173
+ """Return `(defining_module, canonical_name)` when `name` is bound as
174
+ an *imported* symbol of one of `kinds` -- i.e. the cell exists, kind
175
+ matches, and the binding has a non-None `defining_module`. Returns
176
+ None for local definitions, kind mismatches, missing entries, and a
177
+ None table.
178
+ """
179
+ if table is None:
180
+ return None
181
+ cell = table.get(name)
182
+ if cell is None:
183
+ return None
184
+ bd = cell.binding
185
+ if bd.defining_module is None:
186
+ return None
187
+ if kinds and bd.kind not in kinds:
188
+ return None
189
+ return (bd.defining_module, bd.canonical_name)
190
+
191
+
192
+ def resolve_definer(
193
+ registry, module_name: str, name: str, *kinds: SymbolKind,
194
+ ) -> tuple[str, str]:
195
+ """Return the (definer_module, definer_name) for `name` as bound in
196
+ `module_name`'s attribute table, or `(module_name, name)` if the
197
+ cell is missing / locally-defined / kind-mismatched.
198
+
199
+ `registry` only needs `get_module(name) -> object | None`; the
200
+ returned object only needs a `module_attributes` attribute that's
201
+ a `dict | None`. Works equally with the sema `TypeRegistry` and the
202
+ `Compiler`'s `modules` dict via a thin adapter.
203
+ """
204
+ mi = registry.get_module(module_name)
205
+ if mi is None:
206
+ return (module_name, name)
207
+ table = getattr(mi, "module_attributes", None)
208
+ imp = lookup_imported(table, name, *kinds)
209
+ if imp is None:
210
+ return (module_name, name)
211
+ return imp
212
+
213
+
214
+ def walk_attribute_chain(
215
+ registry, module_name: str, name: str,
216
+ accept, # Callable[[SymbolBinding], bool]
217
+ ) -> 'tuple[str, str, SymbolBinding] | None':
218
+ """Walk the `module_attributes` chain across `registry`'s modules
219
+ until `accept(binding)` returns True or the chain dead-ends.
220
+
221
+ Returns `(defining_module_or_current, canonical_name, binding)` for
222
+ the accepted cell, or None when no hop matches. `registry` only
223
+ needs `get_module(name) -> ModuleInfo | None` -- works equally with
224
+ `TypeRegistry` (sema) and `Compiler` (with a wrapper).
225
+ """
226
+ visited: set[tuple[str, str]] = set()
227
+ cur_mod, cur_name = module_name, name
228
+ while True:
229
+ key = (cur_mod, cur_name)
230
+ if key in visited:
231
+ return None
232
+ visited.add(key)
233
+ mi = registry.get_module(cur_mod)
234
+ if mi is None or mi.module_attributes is None:
235
+ return None
236
+ cell = mi.module_attributes.get(cur_name)
237
+ if cell is None:
238
+ return None
239
+ bd = cell.binding
240
+ if accept(bd):
241
+ return (bd.defining_module or cur_mod, bd.canonical_name, bd)
242
+ if bd.defining_module is None:
243
+ return None
244
+ cur_mod, cur_name = bd.defining_module, bd.canonical_name
245
+
246
+
247
+ _MACRO_KINDS = frozenset({
248
+ SymbolKind.CLASS_MACRO, SymbolKind.CALL_MACRO, SymbolKind.BUILDER_MACRO,
249
+ })
250
+
251
+
252
+ def is_macro_kind(binding: SymbolBinding) -> bool:
253
+ return binding.kind in _MACRO_KINDS
254
+
255
+
256
+ def is_kind(*kinds: SymbolKind):
257
+ """Return an `accept` predicate matching any of `kinds`."""
258
+ s = frozenset(kinds)
259
+ return lambda bd: bd.kind in s
tpyc/test_c3_mro.py ADDED
@@ -0,0 +1,208 @@
1
+ """C3 linearization and diamond detection invariants (D22 prep).
2
+
3
+ The multi-base path is still gated in sema, so these tests exercise
4
+ `c3_linearize`, `TypeRegistry.compute_mro_ancestors`, and
5
+ `TypeRegistry.detect_diamond` directly. Once D22 lifts the gate, the
6
+ algorithms these tests pin down start running in production.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import pytest
11
+
12
+ from tpyc.typesys import (
13
+ NominalType, RecordInfo, TypeRegistry,
14
+ c3_linearize, C3LinearizationError,
15
+ same_nominal_symbol_loose,
16
+ )
17
+ from tpyc.type_def_registry import (
18
+ attach_dynamic_type_def, TypeCategory, clear_dynamic_type_defs,
19
+ )
20
+
21
+
22
+ @pytest.fixture(autouse=True)
23
+ def _cleanup():
24
+ yield
25
+ clear_dynamic_type_defs()
26
+
27
+
28
+ def _nom(name: str) -> NominalType:
29
+ """NominalType with qname set so is_user_record resolves via the attached TypeDef."""
30
+ return NominalType(name, _module_qname=f"m.{name}")
31
+
32
+
33
+ def _record(name: str, parents: list[NominalType], mro_ancestors: list[NominalType]) -> RecordInfo:
34
+ info = RecordInfo(
35
+ name=name, fields=[], module="m",
36
+ parents=list(parents),
37
+ mro_ancestors=list(mro_ancestors),
38
+ )
39
+ attach_dynamic_type_def(
40
+ info.qualified_name(),
41
+ TypeCategory.RECORD,
42
+ record=info,
43
+ is_value_type=False,
44
+ )
45
+ return info
46
+
47
+
48
+ def _type_names(types: list) -> list[str]:
49
+ return [t.name for t in types]
50
+
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # c3_linearize: pure algorithm
54
+ # ---------------------------------------------------------------------------
55
+
56
+ class TestC3Linearize:
57
+ def test_empty_bases(self) -> None:
58
+ assert c3_linearize([]) == []
59
+
60
+ def test_single_base_no_ancestors(self) -> None:
61
+ """class Child(Root): -> [Root]."""
62
+ root = _nom("Root")
63
+ assert _type_names(c3_linearize([(root, [])])) == ["Root"]
64
+
65
+ def test_single_base_with_chain(self) -> None:
66
+ """A <- B <- C. class D(C): -> [C, B, A]."""
67
+ a, b, c = _nom("A"), _nom("B"), _nom("C")
68
+ # C's own ancestry: [B, A]
69
+ result = c3_linearize([(c, [b, a])])
70
+ assert _type_names(result) == ["C", "B", "A"]
71
+
72
+ def test_non_diamond_two_parents(self) -> None:
73
+ """class D(B, C): with B(A), C: -> [B, A, C]."""
74
+ a, b, c = _nom("A"), _nom("B"), _nom("C")
75
+ result = c3_linearize([(b, [a]), (c, [])])
76
+ assert _type_names(result) == ["B", "A", "C"]
77
+
78
+ def test_canonical_diamond_c3(self) -> None:
79
+ """Canonical C3: O <- A, O <- B, A <- X(A, B), result should include A then B.
80
+
81
+ This is a 'diamond-free' compose since A and B don't share an ancestor in
82
+ this reduced form. Here: class D(B, C) where B(A), C(A) -- A is shared.
83
+ C3 linearizes this as [B, C, A], but our detect_diamond rejects it.
84
+ The linearization algorithm itself is permissive.
85
+ """
86
+ a, b, c = _nom("A"), _nom("B"), _nom("C")
87
+ # B.mro_ancestors = [A], C.mro_ancestors = [A]
88
+ result = c3_linearize([(b, [a]), (c, [a])])
89
+ assert _type_names(result) == ["B", "C", "A"]
90
+
91
+ def test_inconsistent_hierarchy_raises(self) -> None:
92
+ """class X(A, B) where A's MRO says [A, P, Q] and B's says [B, Q, P] -- no valid linearization."""
93
+ a, b, p, q = _nom("A"), _nom("B"), _nom("P"), _nom("Q")
94
+ with pytest.raises(C3LinearizationError):
95
+ c3_linearize([(a, [p, q]), (b, [q, p])])
96
+
97
+ def test_duplicate_direct_bases_raises(self) -> None:
98
+ """class D(B, B) -- same base listed twice is inconsistent."""
99
+ b = _nom("B")
100
+ with pytest.raises(C3LinearizationError):
101
+ c3_linearize([(b, []), (b, [])])
102
+
103
+
104
+ # ---------------------------------------------------------------------------
105
+ # TypeRegistry.compute_mro_ancestors + detect_diamond
106
+ # ---------------------------------------------------------------------------
107
+
108
+ class TestRegistryMRO:
109
+ def test_leaf_record_mro_empty(self) -> None:
110
+ reg = TypeRegistry()
111
+ a = _record("A", parents=[], mro_ancestors=[])
112
+ reg.register_record(a)
113
+ assert reg.compute_mro_ancestors(a) == []
114
+
115
+ def test_single_parent_linear(self) -> None:
116
+ reg = TypeRegistry()
117
+ a = _record("A", parents=[], mro_ancestors=[])
118
+ reg.register_record(a)
119
+ a_ref = _nom("A")
120
+ b = _record("B", parents=[a_ref], mro_ancestors=[a_ref])
121
+ reg.register_record(b)
122
+ b_ref = _nom("B")
123
+ c = _record("C", parents=[b_ref], mro_ancestors=[])
124
+ reg.register_record(c)
125
+ result = reg.compute_mro_ancestors(c)
126
+ assert _type_names(result) == ["B", "A"]
127
+
128
+ def test_multi_parent_no_diamond(self) -> None:
129
+ reg = TypeRegistry()
130
+ a = _record("A", parents=[], mro_ancestors=[])
131
+ reg.register_record(a)
132
+ b = _record("B", parents=[], mro_ancestors=[])
133
+ reg.register_record(b)
134
+ a_ref, b_ref = _nom("A"), _nom("B")
135
+ d = _record("D", parents=[a_ref, b_ref], mro_ancestors=[])
136
+ reg.register_record(d)
137
+ result = reg.compute_mro_ancestors(d)
138
+ assert _type_names(result) == ["A", "B"]
139
+
140
+
141
+ class TestDiamondDetection:
142
+ def test_single_parent_no_diamond(self) -> None:
143
+ reg = TypeRegistry()
144
+ a = _record("A", parents=[], mro_ancestors=[])
145
+ reg.register_record(a)
146
+ a_ref = _nom("A")
147
+ b = _record("B", parents=[a_ref], mro_ancestors=[a_ref])
148
+ reg.register_record(b)
149
+ assert reg.detect_diamond(b) is None
150
+
151
+ def test_unrelated_multi_parent_no_diamond(self) -> None:
152
+ reg = TypeRegistry()
153
+ a = _record("A", parents=[], mro_ancestors=[])
154
+ b = _record("B", parents=[], mro_ancestors=[])
155
+ reg.register_record(a)
156
+ reg.register_record(b)
157
+ d = _record("D", parents=[_nom("A"), _nom("B")], mro_ancestors=[])
158
+ reg.register_record(d)
159
+ assert reg.detect_diamond(d) is None
160
+
161
+ def test_classic_diamond_detected(self) -> None:
162
+ """A at top; B(A), C(A); D(B, C) -- A is reachable via both B and C."""
163
+ reg = TypeRegistry()
164
+ a = _record("A", parents=[], mro_ancestors=[])
165
+ reg.register_record(a)
166
+ a_ref = _nom("A")
167
+ b = _record("B", parents=[a_ref], mro_ancestors=[a_ref])
168
+ c = _record("C", parents=[a_ref], mro_ancestors=[a_ref])
169
+ reg.register_record(b)
170
+ reg.register_record(c)
171
+ d = _record("D", parents=[_nom("B"), _nom("C")], mro_ancestors=[])
172
+ reg.register_record(d)
173
+
174
+ diamond = reg.detect_diamond(d)
175
+ assert diamond is not None
176
+ anc, p1, p2 = diamond
177
+ assert anc == "A"
178
+ assert {p1, p2} == {"B", "C"}
179
+
180
+ def test_direct_and_indirect_parent_is_diamond(self) -> None:
181
+ """class C(A, B) where B(A) -- A is both a direct base and an ancestor of B."""
182
+ reg = TypeRegistry()
183
+ a = _record("A", parents=[], mro_ancestors=[])
184
+ reg.register_record(a)
185
+ a_ref = _nom("A")
186
+ b = _record("B", parents=[a_ref], mro_ancestors=[a_ref])
187
+ reg.register_record(b)
188
+ c = _record("C", parents=[a_ref, _nom("B")], mro_ancestors=[])
189
+ reg.register_record(c)
190
+
191
+ diamond = reg.detect_diamond(c)
192
+ assert diamond is not None
193
+ anc, p1, p2 = diamond
194
+ assert anc == "A"
195
+ assert {p1, p2} == {"A", "B"}
196
+
197
+ def test_duplicate_direct_parent_is_diamond(self) -> None:
198
+ """class D(B, B) -- same parent listed twice."""
199
+ reg = TypeRegistry()
200
+ b = _record("B", parents=[], mro_ancestors=[])
201
+ reg.register_record(b)
202
+ b_ref = _nom("B")
203
+ d = _record("D", parents=[b_ref, b_ref], mro_ancestors=[])
204
+ reg.register_record(d)
205
+
206
+ diamond = reg.detect_diamond(d)
207
+ assert diamond is not None
208
+ assert diamond[0] == "B"
tpyc/test_cli_argv.py ADDED
@@ -0,0 +1,52 @@
1
+ """Unit tests for tpyc argv pre-split helper."""
2
+
3
+ import pytest
4
+
5
+ from .cli import _split_tpyc_argv
6
+
7
+
8
+ def test_no_separator_returns_empty_script_args():
9
+ assert _split_tpyc_argv(["foo.py", "-o", "out/"]) == (["foo.py", "-o", "out/"], [])
10
+
11
+
12
+ def test_empty_argv():
13
+ assert _split_tpyc_argv([]) == ([], [])
14
+
15
+
16
+ def test_separator_at_end_yields_empty_script_args():
17
+ assert _split_tpyc_argv(["foo.py", "-x", "--"]) == (["foo.py", "-x"], [])
18
+
19
+
20
+ def test_separator_splits_compiler_and_program_args():
21
+ before, after = _split_tpyc_argv(["foo.py", "-x", "--", "a", "b"])
22
+ assert before == ["foo.py", "-x"]
23
+ assert after == ["a", "b"]
24
+
25
+
26
+ def test_tpyc_flags_preserved_before_separator():
27
+ before, after = _split_tpyc_argv(["foo.py", "-o", "out/", "--pcre2=none", "-x", "--", "arg"])
28
+ assert before == ["foo.py", "-o", "out/", "--pcre2=none", "-x"]
29
+ assert after == ["arg"]
30
+
31
+
32
+ def test_multiple_dash_dash_only_first_is_separator():
33
+ before, after = _split_tpyc_argv(["foo.py", "-x", "--", "a", "--", "b"])
34
+ assert before == ["foo.py", "-x"]
35
+ assert after == ["a", "--", "b"]
36
+
37
+
38
+ def test_leading_separator_with_trailing_tokens_rejected():
39
+ with pytest.raises(ValueError, match="input file must appear before"):
40
+ _split_tpyc_argv(["--", "foo.py"])
41
+
42
+
43
+ def test_only_separator_passes_through():
44
+ # `tpyc --` alone: no trailing tokens, so nothing to reject. argparse will
45
+ # then fail with "required: input" which is the right error for this shape.
46
+ assert _split_tpyc_argv(["--"]) == ([], [])
47
+
48
+
49
+ def test_flag_like_program_args_preserved():
50
+ before, after = _split_tpyc_argv(["foo.py", "-x", "--", "-O", "--help", "-"])
51
+ assert before == ["foo.py", "-x"]
52
+ assert after == ["-O", "--help", "-"]