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