code-towel 1.0.4__tar.gz → 1.414__tar.gz

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 (411) hide show
  1. code_towel-1.414/.flake8 +9 -0
  2. code_towel-1.414/.pre-commit-config.yaml +70 -0
  3. code_towel-1.414/.templates/README.md +406 -0
  4. code_towel-1.414/.templates/binding_constructs_comprehensive.py +137 -0
  5. code_towel-1.414/.templates/bindings_comprehensions.py +61 -0
  6. code_towel-1.414/.templates/bindings_for_loops.py +61 -0
  7. code_towel-1.414/.templates/closure_adversarial.py +118 -0
  8. code_towel-1.414/.templates/complex_expressions.py +80 -0
  9. code_towel-1.414/.templates/control_flow_adversarial.py +158 -0
  10. code_towel-1.414/.templates/edge_cases_stress_test.py +328 -0
  11. code_towel-1.414/.templates/example1_simple.py +56 -0
  12. code_towel-1.414/.templates/example2_classes.py +65 -0
  13. code_towel-1.414/.templates/example3_file1.py +26 -0
  14. code_towel-1.414/.templates/example3_file2.py +26 -0
  15. code_towel-1.414/.templates/example4_complex.py +56 -0
  16. code_towel-1.414/.templates/exception_adversarial.py +176 -0
  17. code_towel-1.414/.templates/fstrings_constants.py +84 -0
  18. code_towel-1.414/.templates/functional_patterns.py +288 -0
  19. code_towel-1.414/.templates/global_nonlocal_examples.py +129 -0
  20. code_towel-1.414/.templates/hygienic_naming.py +152 -0
  21. code_towel-1.414/.templates/method_chains.py +240 -0
  22. code_towel-1.414/.templates/nested_structures.py +206 -0
  23. code_towel-1.414/.templates/real_world_patterns.py +312 -0
  24. code_towel-1.414/.templates/referential_transparency.py +258 -0
  25. code_towel-1.414/.templates/return_values.py +94 -0
  26. code_towel-1.414/.templates/scoping_edge_cases.py +117 -0
  27. code_towel-1.414/.templates/side_effects_adversarial.py +119 -0
  28. code_towel-1.414/.templates/syntactic_coverage_comprehensive.py +735 -0
  29. code_towel-1.414/.templates/tricky_edge_cases_adversarial.py +216 -0
  30. code_towel-1.414/CHANGELOG.md +48 -0
  31. code_towel-1.414/CODE_OF_CONDUCT.md +131 -0
  32. code_towel-1.414/CONTRIBUTING.md +175 -0
  33. code_towel-1.414/MANIFEST.in +15 -0
  34. code_towel-1.414/PKG-INFO +175 -0
  35. code_towel-1.414/README.md +141 -0
  36. code_towel-1.414/SECURITY.md +13 -0
  37. code_towel-1.414/docs/ADVERSARIAL_REVIEW.md +113 -0
  38. code_towel-1.414/docs/ARCHITECTURE.md +347 -0
  39. code_towel-1.414/docs/BUG_HANDOFF_2025-12-03.md +278 -0
  40. code_towel-1.414/docs/CROSS_FILE_REFACTORING.md +233 -0
  41. code_towel-1.414/docs/JUSTFILE_REFERENCE.md +361 -0
  42. code_towel-1.414/docs/KNOWN_LIMITATIONS.md +224 -0
  43. code_towel-1.414/docs/OPEN_SOURCE_AUDIT.md +68 -0
  44. code_towel-1.414/docs/PRODUCTION_READINESS.md +295 -0
  45. code_towel-1.414/docs/README_DIRECTORY_USAGE.md +265 -0
  46. code_towel-1.414/docs/RELEASE_LOG.md +277 -0
  47. code_towel-1.414/docs/RELEASING.md +36 -0
  48. code_towel-1.414/docs/TESTING_AUDIT.md +281 -0
  49. code_towel-1.414/docs/TROUBLESHOOTING.md +284 -0
  50. code_towel-1.414/docs/UNIFICATION_IMPLEMENTATION.md +257 -0
  51. code_towel-1.414/docs/USAGE_GUIDE.md +388 -0
  52. code_towel-1.414/justfile +155 -0
  53. {code_towel-1.0.4 → code_towel-1.414}/pyproject.toml +26 -46
  54. code_towel-1.414/scripts/add_copyright_headers.py +67 -0
  55. code_towel-1.414/scripts/bench_refactor.py +118 -0
  56. code_towel-1.414/scripts/debug_pair.py +119 -0
  57. code_towel-1.414/scripts/dry +7 -0
  58. code_towel-1.414/scripts/ecosystem/manifest.toml +720 -0
  59. code_towel-1.414/scripts/ecosystem_check.py +499 -0
  60. code_towel-1.414/scripts/preview +162 -0
  61. code_towel-1.414/scripts/promote_dry_helpers.py +611 -0
  62. code_towel-1.414/scripts/report_net_benefit_diff.py +116 -0
  63. code_towel-1.414/scripts/run_crossfile_coverage.py +27 -0
  64. code_towel-1.414/scripts/set_version.py +33 -0
  65. code_towel-1.414/scripts/trace_proposals.py +99 -0
  66. code_towel-1.414/scripts/verify-examples +111 -0
  67. code_towel-1.414/src/code_towel.egg-info/PKG-INFO +175 -0
  68. code_towel-1.414/src/code_towel.egg-info/SOURCES.txt +404 -0
  69. {code_towel-1.0.4 → code_towel-1.414}/src/code_towel.egg-info/requires.txt +5 -1
  70. code_towel-1.414/src/towel/changes.py +257 -0
  71. {code_towel-1.0.4 → code_towel-1.414}/src/towel/cli.py +267 -133
  72. code_towel-1.414/src/towel/filesystem.py +41 -0
  73. code_towel-1.414/src/towel/py.typed +0 -0
  74. code_towel-1.414/src/towel/renaming.py +877 -0
  75. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/assignment_analyzer.py +83 -11
  76. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/ast_normalizer.py +26 -12
  77. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/binding_detector.py +5 -2
  78. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/block_signature.py +22 -12
  79. code_towel-1.414/src/towel/unification/definite_assignment.py +231 -0
  80. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/extractor.py +34 -40
  81. code_towel-1.414/src/towel/unification/instantiation.py +337 -0
  82. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/models.py +5 -0
  83. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/orphan_detector.py +14 -13
  84. code_towel-1.414/src/towel/unification/pipeline.py +473 -0
  85. code_towel-1.414/src/towel/unification/project_layout.py +496 -0
  86. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/refactor_engine.py +1748 -559
  87. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/scope_analyzer.py +196 -49
  88. code_towel-1.414/src/towel/unification/semantic_safety.py +741 -0
  89. code_towel-1.414/src/towel/unification/structural_memo.py +137 -0
  90. code_towel-1.414/src/towel/unification/thunk_inlining.py +273 -0
  91. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/unifier.py +70 -8
  92. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/visitors.py +30 -8
  93. code_towel-1.414/test_examples/README.md +406 -0
  94. code_towel-1.414/test_examples/binding_constructs_comprehensive.py +137 -0
  95. code_towel-1.414/test_examples/bindings_comprehensions.py +61 -0
  96. code_towel-1.414/test_examples/bindings_for_loops.py +61 -0
  97. code_towel-1.414/test_examples/closure_adversarial.py +118 -0
  98. code_towel-1.414/test_examples/complex_expressions.py +80 -0
  99. code_towel-1.414/test_examples/control_flow_adversarial.py +158 -0
  100. code_towel-1.414/test_examples/edge_cases_stress_test.py +328 -0
  101. code_towel-1.414/test_examples/example1_simple.py +56 -0
  102. code_towel-1.414/test_examples/example2_classes.py +65 -0
  103. code_towel-1.414/test_examples/example3_file1.py +26 -0
  104. code_towel-1.414/test_examples/example3_file2.py +26 -0
  105. code_towel-1.414/test_examples/example4_complex.py +56 -0
  106. code_towel-1.414/test_examples/exception_adversarial.py +176 -0
  107. code_towel-1.414/test_examples/fstrings_constants.py +84 -0
  108. code_towel-1.414/test_examples/functional_patterns.py +288 -0
  109. code_towel-1.414/test_examples/global_nonlocal_examples.py +129 -0
  110. code_towel-1.414/test_examples/hygienic_naming.py +152 -0
  111. code_towel-1.414/test_examples/method_chains.py +240 -0
  112. code_towel-1.414/test_examples/nested_structures.py +206 -0
  113. code_towel-1.414/test_examples/real_world_patterns.py +312 -0
  114. code_towel-1.414/test_examples/referential_transparency.py +258 -0
  115. code_towel-1.414/test_examples/return_values.py +94 -0
  116. code_towel-1.414/test_examples/scoping_edge_cases.py +117 -0
  117. code_towel-1.414/test_examples/side_effects_adversarial.py +119 -0
  118. code_towel-1.414/test_examples/syntactic_coverage_comprehensive.py +735 -0
  119. code_towel-1.414/test_examples/tricky_edge_cases_adversarial.py +216 -0
  120. code_towel-1.414/test_examples_crossfile/README.md +227 -0
  121. code_towel-1.414/test_examples_crossfile/multi_level/api/checkout.py +27 -0
  122. code_towel-1.414/test_examples_crossfile/multi_level/core/services/payment.py +27 -0
  123. code_towel-1.414/test_examples_crossfile/multi_level/utils/validators.py +26 -0
  124. code_towel-1.414/test_examples_crossfile/nested_structure/src/data_processor.py +23 -0
  125. code_towel-1.414/test_examples_crossfile/simple_crossfile/admin_service.py +25 -0
  126. code_towel-1.414/test_examples_crossfile/simple_crossfile/user_service.py +25 -0
  127. code_towel-1.414/test_examples_crossfile_expected_output/multi_level/api/checkout.py +35 -0
  128. code_towel-1.414/test_examples_crossfile_expected_output/multi_level/core/services/payment.py +18 -0
  129. code_towel-1.414/test_examples_crossfile_expected_output/multi_level/utils/validators.py +17 -0
  130. code_towel-1.414/test_examples_crossfile_expected_output/nested_structure/src/data_processor.py +23 -0
  131. code_towel-1.414/test_examples_crossfile_expected_output/simple_crossfile/admin_service.py +29 -0
  132. code_towel-1.414/test_examples_crossfile_expected_output/simple_crossfile/user_service.py +17 -0
  133. code_towel-1.414/test_examples_expected_output/binding_constructs_comprehensive.py +133 -0
  134. code_towel-1.414/test_examples_expected_output/bindings_comprehensions.py +61 -0
  135. code_towel-1.414/test_examples_expected_output/bindings_for_loops.py +60 -0
  136. code_towel-1.414/test_examples_expected_output/closure_adversarial.py +119 -0
  137. code_towel-1.414/test_examples_expected_output/complex_expressions.py +79 -0
  138. code_towel-1.414/test_examples_expected_output/control_flow_adversarial.py +145 -0
  139. code_towel-1.414/test_examples_expected_output/edge_cases_stress_test.py +181 -0
  140. code_towel-1.414/test_examples_expected_output/example1_simple.py +50 -0
  141. code_towel-1.414/test_examples_expected_output/example2_classes.py +59 -0
  142. code_towel-1.414/test_examples_expected_output/example3_file1.py +26 -0
  143. code_towel-1.414/test_examples_expected_output/example3_file2.py +26 -0
  144. code_towel-1.414/test_examples_expected_output/example4_complex.py +37 -0
  145. code_towel-1.414/test_examples_expected_output/exception_adversarial.py +156 -0
  146. code_towel-1.414/test_examples_expected_output/fstrings_constants.py +88 -0
  147. code_towel-1.414/test_examples_expected_output/functional_patterns.py +222 -0
  148. code_towel-1.414/test_examples_expected_output/global_nonlocal_examples.py +131 -0
  149. code_towel-1.414/test_examples_expected_output/hygienic_naming.py +150 -0
  150. code_towel-1.414/test_examples_expected_output/method_chains.py +171 -0
  151. code_towel-1.414/test_examples_expected_output/nested_structures.py +166 -0
  152. code_towel-1.414/test_examples_expected_output/real_world_patterns.py +186 -0
  153. code_towel-1.414/test_examples_expected_output/referential_transparency.py +245 -0
  154. code_towel-1.414/test_examples_expected_output/return_values.py +96 -0
  155. code_towel-1.414/test_examples_expected_output/scoping_edge_cases.py +113 -0
  156. code_towel-1.414/test_examples_expected_output/side_effects_adversarial.py +122 -0
  157. code_towel-1.414/test_examples_expected_output/syntactic_coverage_comprehensive.py +709 -0
  158. code_towel-1.414/test_examples_expected_output/tricky_edge_cases_adversarial.py +217 -0
  159. code_towel-1.414/tests/OBSERVATIONAL_EQUIVALENCE.md +336 -0
  160. code_towel-1.414/tests/README.md +87 -0
  161. code_towel-1.414/tests/__init__.py +3 -0
  162. code_towel-1.414/tests/automatic_equivalence_tester.py +631 -0
  163. code_towel-1.414/tests/conftest.py +26 -0
  164. code_towel-1.414/tests/crossfile_equivalence_tester.py +358 -0
  165. code_towel-1.414/tests/edge_case_values.py +361 -0
  166. code_towel-1.414/tests/equivalence_targets.py +137 -0
  167. code_towel-1.414/tests/generate_baseline.py +179 -0
  168. code_towel-1.414/tests/hostile_cases/h01_side_effect_order.py +23 -0
  169. code_towel-1.414/tests/hostile_cases/h02_conditional_eval.py +16 -0
  170. code_towel-1.414/tests/hostile_cases/h03_loop_reeval.py +19 -0
  171. code_towel-1.414/tests/hostile_cases/h04_closure_freevar.py +16 -0
  172. code_towel-1.414/tests/hostile_cases/h05_cond_return_plus_retvar.py +20 -0
  173. code_towel-1.414/tests/hostile_cases/h06_del_in_block.py +20 -0
  174. code_towel-1.414/tests/hostile_cases/h07_except_unbind.py +24 -0
  175. code_towel-1.414/tests/hostile_cases/h08_late_binding_closure.py +16 -0
  176. code_towel-1.414/tests/hostile_cases/h10_short_circuit.py +14 -0
  177. code_towel-1.414/tests/hostile_cases/h13_dunder_file.py +13 -0
  178. code_towel-1.414/tests/hostile_cases/h26_match_capture.py +18 -0
  179. code_towel-1.414/tests/hostile_cases/h38_closure_after_block.py +18 -0
  180. code_towel-1.414/tests/hostile_cases/h39_augassign_after_block.py +16 -0
  181. code_towel-1.414/tests/hostile_cases/h51_semicolons.py +14 -0
  182. code_towel-1.414/tests/hostile_cases/h52_del_after_block.py +16 -0
  183. code_towel-1.414/tests/hostile_cases/h53_nested_func_reads_retvar.py +14 -0
  184. code_towel-1.414/tests/hostile_cases/h54_attr_param_property.py +21 -0
  185. code_towel-1.414/tests/hostile_cases/h55_walrus_used_after.py +17 -0
  186. code_towel-1.414/tests/hostile_cases/h56_global_write.py +15 -0
  187. code_towel-1.414/tests/hostile_cases/h57_exception_in_param.py +18 -0
  188. code_towel-1.414/tests/hostile_cases/h58_nested_def_freevar_reassigned.py +14 -0
  189. code_towel-1.414/tests/hostile_cases/h59_retvar_conditional_unbound.py +20 -0
  190. code_towel-1.414/tests/hostile_cases/h60_finally_return.py +18 -0
  191. code_towel-1.414/tests/hostile_cases/h61_str_method_param.py +16 -0
  192. code_towel-1.414/tests/hostile_cases/r01_property_thunk_order.py +19 -0
  193. code_towel-1.414/tests/hostile_cases/r02_cluster_attrs.py +25 -0
  194. code_towel-1.414/tests/hostile_cases/r07_attr_two_positions.py +16 -0
  195. code_towel-1.414/tests/hostile_cases/r100_clustered_site_with_live_binding.py +22 -0
  196. code_towel-1.414/tests/hostile_cases/r101_same_named_method_forces_module_helper.py +43 -0
  197. code_towel-1.414/tests/hostile_cases/r11_same_function_pair.py +14 -0
  198. code_towel-1.414/tests/hostile_cases/r14_starred_param.py +15 -0
  199. code_towel-1.414/tests/hostile_cases/r22_fstring_attr.py +19 -0
  200. code_towel-1.414/tests/hostile_cases/r26_attr_store_param.py +14 -0
  201. code_towel-1.414/tests/hostile_cases/r27_del_subscript.py +12 -0
  202. code_towel-1.414/tests/hostile_cases/r32_method_callee.py +18 -0
  203. code_towel-1.414/tests/hostile_cases/r38_name_two_positions.py +14 -0
  204. code_towel-1.414/tests/hostile_cases/r39_cluster_alpha.py +17 -0
  205. code_towel-1.414/tests/hostile_cases/r40_class_hierarchy.py +21 -0
  206. code_towel-1.414/tests/hostile_cases/r41_conditional_call_param.py +17 -0
  207. code_towel-1.414/tests/hostile_cases/r42_loop_call_param.py +16 -0
  208. code_towel-1.414/tests/hostile_cases/r43_opaque_decorator_receiver.py +17 -0
  209. code_towel-1.414/tests/hostile_cases/r44_cluster_nested_overlap.py +23 -0
  210. code_towel-1.414/tests/hostile_cases/r45_async_no_await.py +17 -0
  211. code_towel-1.414/tests/hostile_cases/r46_nonlocal_counter.py +22 -0
  212. code_towel-1.414/tests/hostile_cases/r47_lru_cache_method.py +19 -0
  213. code_towel-1.414/tests/hostile_cases/r50_helper_name_collision.py +18 -0
  214. code_towel-1.414/tests/hostile_cases/r53_try_else_binding.py +28 -0
  215. code_towel-1.414/tests/hostile_cases/r54_with_binding_used_after.py +15 -0
  216. code_towel-1.414/tests/hostile_cases/r55_chained_assign.py +14 -0
  217. code_towel-1.414/tests/hostile_cases/r57_star_unpack.py +12 -0
  218. code_towel-1.414/tests/hostile_cases/r58_escaping_closure.py +16 -0
  219. code_towel-1.414/tests/hostile_cases/r60_local_import.py +14 -0
  220. code_towel-1.414/tests/hostile_cases/r61_same_function_clean.py +13 -0
  221. code_towel-1.414/tests/hostile_cases/r62_recursion.py +14 -0
  222. code_towel-1.414/tests/hostile_cases/r63_except_var_used_in_handler.py +22 -0
  223. code_towel-1.414/tests/hostile_cases/r66_conditional_callee.py +17 -0
  224. code_towel-1.414/tests/hostile_cases/r69_annassign.py +14 -0
  225. code_towel-1.414/tests/hostile_cases/r72_global_promoted.py +17 -0
  226. code_towel-1.414/tests/hostile_cases/r73_slice_vs_index.py +12 -0
  227. code_towel-1.414/tests/hostile_cases/r74_starred_vs_plain.py +15 -0
  228. code_towel-1.414/tests/hostile_cases/r75_prior_helper_param_names.py +14 -0
  229. code_towel-1.414/tests/hostile_cases/r76_return_order.py +16 -0
  230. code_towel-1.414/tests/hostile_cases/r77_mixed_return_and_variables.py +17 -0
  231. code_towel-1.414/tests/hostile_cases/r78_walrus_in_parameter.py +17 -0
  232. code_towel-1.414/tests/hostile_cases/r79_list_display_in_loop.py +23 -0
  233. code_towel-1.414/tests/hostile_cases/r80_inlined_leading_thunk.py +19 -0
  234. code_towel-1.414/tests/hostile_cases/r81_conditionally_bound_free_variable.py +21 -0
  235. code_towel-1.414/tests/hostile_cases/r82_local_classes_same_name.py +20 -0
  236. code_towel-1.414/tests/hostile_cases/r83_tab_indented_class.py +20 -0
  237. code_towel-1.414/tests/hostile_cases/r84_warn_stacklevel.py +18 -0
  238. code_towel-1.414/tests/hostile_cases/r85_conditionally_bound_parameter.py +25 -0
  239. code_towel-1.414/tests/hostile_cases/r86_annotated_assignment_live.py +26 -0
  240. code_towel-1.414/tests/hostile_cases/r87_nested_function_in_method.py +22 -0
  241. code_towel-1.414/tests/hostile_cases/r88_elif_branch.py +25 -0
  242. code_towel-1.414/tests/hostile_cases/r89_conditionally_bound_return.py +28 -0
  243. code_towel-1.414/tests/hostile_cases/r90_cluster_across_classes.py +28 -0
  244. code_towel-1.414/tests/hostile_cases/r91_class_body_helper.py +23 -0
  245. code_towel-1.414/tests/hostile_cases/r92_parameterless_class_body_helper.py +25 -0
  246. code_towel-1.414/tests/hostile_cases/r93_import_name_differs.py +39 -0
  247. code_towel-1.414/tests/hostile_cases/r94_import_binds_live_name.py +12 -0
  248. code_towel-1.414/tests/hostile_cases/r95_type_checking_annotation.py +18 -0
  249. code_towel-1.414/tests/hostile_cases/r96_unpacked_targets_live.py +19 -0
  250. code_towel-1.414/tests/hostile_cases/r97_helper_in_function_with_outside_site.py +21 -0
  251. code_towel-1.414/tests/hostile_cases/r98_same_named_methods_nested_helpers.py +37 -0
  252. code_towel-1.414/tests/hostile_cases/r99_partial_return_branches.py +25 -0
  253. code_towel-1.414/tests/hostile_crossfile/xf3_same_named_local_function/pkg/__init__.py +0 -0
  254. code_towel-1.414/tests/hostile_crossfile/xf3_same_named_local_function/pkg/a.py +8 -0
  255. code_towel-1.414/tests/hostile_crossfile/xf3_same_named_local_function/pkg/b.py +8 -0
  256. code_towel-1.414/tests/hostile_crossfile/xf3_same_named_local_function/run.py +3 -0
  257. code_towel-1.414/tests/hostile_crossfile/xf4_same_named_class/pkg/__init__.py +0 -0
  258. code_towel-1.414/tests/hostile_crossfile/xf4_same_named_class/pkg/a.py +9 -0
  259. code_towel-1.414/tests/hostile_crossfile/xf4_same_named_class/pkg/b.py +9 -0
  260. code_towel-1.414/tests/hostile_crossfile/xf4_same_named_class/run.py +3 -0
  261. code_towel-1.414/tests/hostile_crossfile/xf5_dunder_file/pkg/__init__.py +0 -0
  262. code_towel-1.414/tests/hostile_crossfile/xf5_dunder_file/pkg/a.py +7 -0
  263. code_towel-1.414/tests/hostile_crossfile/xf5_dunder_file/pkg/b.py +7 -0
  264. code_towel-1.414/tests/hostile_crossfile/xf5_dunder_file/run.py +3 -0
  265. code_towel-1.414/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/__init__.py +0 -0
  266. code_towel-1.414/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/a.py +7 -0
  267. code_towel-1.414/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/b.py +7 -0
  268. code_towel-1.414/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/util1.py +1 -0
  269. code_towel-1.414/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/util2.py +1 -0
  270. code_towel-1.414/tests/hostile_crossfile/xf6_same_alias_different_import/run.py +3 -0
  271. code_towel-1.414/tests/hostile_crossfile/xf7_docstring_import/pkg/__init__.py +0 -0
  272. code_towel-1.414/tests/hostile_crossfile/xf7_docstring_import/pkg/a.py +14 -0
  273. code_towel-1.414/tests/hostile_crossfile/xf7_docstring_import/pkg/b.py +13 -0
  274. code_towel-1.414/tests/hostile_crossfile/xf7_docstring_import/run.py +3 -0
  275. code_towel-1.414/tests/hostile_crossfile/xf8_helper_name_collision/pkg/__init__.py +0 -0
  276. code_towel-1.414/tests/hostile_crossfile/xf8_helper_name_collision/pkg/a.py +6 -0
  277. code_towel-1.414/tests/hostile_crossfile/xf8_helper_name_collision/pkg/b.py +6 -0
  278. code_towel-1.414/tests/hostile_crossfile/xf8_helper_name_collision/pkg/c.py +6 -0
  279. code_towel-1.414/tests/hostile_crossfile/xf8_helper_name_collision/pkg/d.py +6 -0
  280. code_towel-1.414/tests/hostile_crossfile/xf8_helper_name_collision/pkg/e.py +12 -0
  281. code_towel-1.414/tests/hostile_crossfile/xf8_helper_name_collision/run.py +6 -0
  282. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/__init__.py +0 -0
  283. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/__init__.py +0 -0
  284. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/base.py +3 -0
  285. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/introspect.py +10 -0
  286. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/revoke.py +10 -0
  287. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/__init__.py +0 -0
  288. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/access.py +10 -0
  289. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/base.py +3 -0
  290. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/request.py +10 -0
  291. code_towel-1.414/tests/hostile_crossfile/xf9_same_named_base_class/run.py +8 -0
  292. code_towel-1.414/tests/run_tests.py +31 -0
  293. code_towel-1.414/tests/test_adversarial_renaming.py +242 -0
  294. code_towel-1.414/tests/test_adversarial_semantics.py +308 -0
  295. code_towel-1.414/tests/test_analysis_sessions.py +200 -0
  296. {code_towel-1.0.4 → code_towel-1.414}/tests/test_ancestor_insertion.py +30 -16
  297. code_towel-1.414/tests/test_backend_layouts.py +321 -0
  298. {code_towel-1.0.4 → code_towel-1.414}/tests/test_binding_detector_edge_cases.py +5 -0
  299. code_towel-1.414/tests/test_candidate_index.py +187 -0
  300. code_towel-1.414/tests/test_change_transactions.py +251 -0
  301. code_towel-1.414/tests/test_cli_integration.py +257 -0
  302. code_towel-1.414/tests/test_cli_output_safety.py +56 -0
  303. {code_towel-1.0.4 → code_towel-1.414}/tests/test_comprehensive_coverage.py +14 -13
  304. code_towel-1.414/tests/test_copy_preservation.py +38 -0
  305. {code_towel-1.0.4 → code_towel-1.414}/tests/test_crossfile_integration.py +0 -1
  306. {code_towel-1.0.4 → code_towel-1.414}/tests/test_crossfile_observational_equivalence.py +3 -2
  307. code_towel-1.414/tests/test_definite_assignment.py +127 -0
  308. {code_towel-1.0.4 → code_towel-1.414}/tests/test_engine_adversarial.py +83 -95
  309. code_towel-1.414/tests/test_engine_failure_visibility.py +34 -0
  310. code_towel-1.414/tests/test_equivalence_harness_regressions.py +155 -0
  311. code_towel-1.414/tests/test_equivalence_target_selection.py +150 -0
  312. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_edge_cases.py +18 -24
  313. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_final_lines.py +40 -0
  314. code_towel-1.414/tests/test_frame_sensitivity_scan.py +61 -0
  315. code_towel-1.414/tests/test_helper_inventory.py +117 -0
  316. code_towel-1.414/tests/test_hostile_battery.py +105 -0
  317. code_towel-1.414/tests/test_hostile_crossfile_battery.py +52 -0
  318. code_towel-1.414/tests/test_import_bindings.py +102 -0
  319. code_towel-1.414/tests/test_import_resolution.py +65 -0
  320. code_towel-1.414/tests/test_instantiation_check.py +222 -0
  321. code_towel-1.414/tests/test_legacy_normalizers.py +69 -0
  322. {code_towel-1.0.4 → code_towel-1.414}/tests/test_method_level_and_attributes.py +2 -2
  323. code_towel-1.414/tests/test_method_receiver_arguments.py +39 -0
  324. {code_towel-1.0.4 → code_towel-1.414}/tests/test_nominal_unifier.py +4 -8
  325. {code_towel-1.0.4 → code_towel-1.414}/tests/test_observational_equivalence.py +65 -47
  326. code_towel-1.414/tests/test_parent_watchdog.py +72 -0
  327. {code_towel-1.0.4 → code_towel-1.414}/tests/test_pipeline_api.py +0 -1
  328. {code_towel-1.0.4 → code_towel-1.414}/tests/test_pipeline_phases.py +0 -1
  329. {code_towel-1.0.4 → code_towel-1.414}/tests/test_project_layout_behavior.py +128 -25
  330. {code_towel-1.0.4 → code_towel-1.414}/tests/test_promotion_and_mangling.py +8 -4
  331. {code_towel-1.0.4 → code_towel-1.414}/tests/test_refactor_engine_comprehensive.py +8 -16
  332. {code_towel-1.0.4 → code_towel-1.414}/tests/test_refactor_engine_edge_cases.py +20 -24
  333. {code_towel-1.0.4 → code_towel-1.414}/tests/test_refactor_engine_targeted_branches.py +18 -29
  334. {code_towel-1.0.4 → code_towel-1.414}/tests/test_refactoring_engine.py +3 -1
  335. {code_towel-1.0.4 → code_towel-1.414}/tests/test_regression.py +83 -64
  336. {code_towel-1.0.4 → code_towel-1.414}/tests/test_regressions_broader.py +8 -24
  337. code_towel-1.414/tests/test_release_regressions.py +125 -0
  338. code_towel-1.414/tests/test_rename_parameters_and_methods.py +130 -0
  339. code_towel-1.414/tests/test_semantic_guards_extended.py +187 -0
  340. code_towel-1.414/tests/test_semantic_safety_regressions.py +370 -0
  341. {code_towel-1.0.4 → code_towel-1.414}/tests/test_signature_prefilter.py +8 -16
  342. code_towel-1.414/tests/test_structural_memo.py +94 -0
  343. code_towel-1.414/tests/test_thunk_inlining.py +135 -0
  344. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_additional_branches.py +8 -16
  345. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_batch2.py +4 -8
  346. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_batch3.py +24 -48
  347. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_batch4.py +12 -24
  348. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_batch5.py +4 -8
  349. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_comprehensions.py +0 -1
  350. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_core.py +43 -64
  351. code_towel-1.414/uv.lock +926 -0
  352. code_towel-1.0.4/PKG-INFO +0 -732
  353. code_towel-1.0.4/README.md +0 -501
  354. code_towel-1.0.4/src/code_towel.egg-info/PKG-INFO +0 -732
  355. code_towel-1.0.4/src/code_towel.egg-info/SOURCES.txt +0 -102
  356. code_towel-1.0.4/src/towel/unification/pipeline.py +0 -524
  357. code_towel-1.0.4/src/towel/unification/project_layout.py +0 -201
  358. {code_towel-1.0.4 → code_towel-1.414}/LICENSE +0 -0
  359. {code_towel-1.0.4 → code_towel-1.414}/setup.cfg +0 -0
  360. {code_towel-1.0.4 → code_towel-1.414}/src/code_towel.egg-info/dependency_links.txt +0 -0
  361. {code_towel-1.0.4 → code_towel-1.414}/src/code_towel.egg-info/entry_points.txt +0 -0
  362. {code_towel-1.0.4 → code_towel-1.414}/src/code_towel.egg-info/top_level.txt +0 -0
  363. {code_towel-1.0.4 → code_towel-1.414}/src/towel/__init__.py +0 -0
  364. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/__init__.py +0 -0
  365. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/ast_pretty_printer.py +0 -0
  366. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/builtins.py +0 -0
  367. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/exceptions.py +0 -0
  368. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/nominal_unifier.py +0 -0
  369. {code_towel-1.0.4 → code_towel-1.414}/src/towel/unification/visitor_utils.py +0 -0
  370. {code_towel-1.0.4 → code_towel-1.414}/tests/test_assignment_analyzer_comprehensive.py +0 -0
  371. {code_towel-1.0.4 → code_towel-1.414}/tests/test_ast_normalizer_comprehensive.py +0 -0
  372. {code_towel-1.0.4 → code_towel-1.414}/tests/test_ast_pretty_printer.py +0 -0
  373. {code_towel-1.0.4 → code_towel-1.414}/tests/test_ast_pretty_printer_comprehensive.py +0 -0
  374. {code_towel-1.0.4 → code_towel-1.414}/tests/test_ast_pretty_printer_more.py +0 -0
  375. {code_towel-1.0.4 → code_towel-1.414}/tests/test_binding_detector.py +0 -0
  376. {code_towel-1.0.4 → code_towel-1.414}/tests/test_bindings.py +0 -0
  377. {code_towel-1.0.4 → code_towel-1.414}/tests/test_bindings_additional.py +0 -0
  378. {code_towel-1.0.4 → code_towel-1.414}/tests/test_breakers.py +0 -0
  379. {code_towel-1.0.4 → code_towel-1.414}/tests/test_builtins_comprehensive.py +0 -0
  380. {code_towel-1.0.4 → code_towel-1.414}/tests/test_exceptions.py +0 -0
  381. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_additional_branches.py +0 -0
  382. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_augassign_and_fstrings.py +0 -0
  383. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_callee_and_multi_return.py +0 -0
  384. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_comprehensive.py +0 -0
  385. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_generate_call_variants.py +0 -0
  386. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_more_paths.py +0 -0
  387. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_preamble_and_call_mapping.py +0 -0
  388. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_remaining_branches.py +0 -0
  389. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_return_statements.py +0 -0
  390. {code_towel-1.0.4 → code_towel-1.414}/tests/test_extractor_substituter_and_helpers.py +0 -0
  391. {code_towel-1.0.4 → code_towel-1.414}/tests/test_final_coverage_push.py +0 -0
  392. {code_towel-1.0.4 → code_towel-1.414}/tests/test_free_variable_correspondence.py +0 -0
  393. {code_towel-1.0.4 → code_towel-1.414}/tests/test_fstrings.py +0 -0
  394. {code_towel-1.0.4 → code_towel-1.414}/tests/test_helpers.py +0 -0
  395. {code_towel-1.0.4 → code_towel-1.414}/tests/test_orphan_detection.py +0 -0
  396. {code_towel-1.0.4 → code_towel-1.414}/tests/test_orphan_detector_comprehensive.py +0 -0
  397. {code_towel-1.0.4 → code_towel-1.414}/tests/test_overlap_filtering.py +0 -0
  398. {code_towel-1.0.4 → code_towel-1.414}/tests/test_progress_modes.py +0 -0
  399. {code_towel-1.0.4 → code_towel-1.414}/tests/test_project_layout_and_imports.py +0 -0
  400. {code_towel-1.0.4 → code_towel-1.414}/tests/test_refactor_engine_more.py +0 -0
  401. {code_towel-1.0.4 → code_towel-1.414}/tests/test_return_values.py +0 -0
  402. {code_towel-1.0.4 → code_towel-1.414}/tests/test_scope_analyzer.py +0 -0
  403. {code_towel-1.0.4 → code_towel-1.414}/tests/test_scope_analyzer_comprehensive.py +0 -0
  404. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_bound_variables.py +0 -0
  405. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_comprehensive.py +0 -0
  406. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_edge_cases.py +0 -0
  407. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_more_branches.py +0 -0
  408. {code_towel-1.0.4 → code_towel-1.414}/tests/test_unifier_state_reset.py +0 -0
  409. {code_towel-1.0.4 → code_towel-1.414}/tests/test_validation_partial_lifetime.py +0 -0
  410. {code_towel-1.0.4 → code_towel-1.414}/tests/test_variable_capture_bug.py +0 -0
  411. {code_towel-1.0.4 → code_towel-1.414}/tests/test_visitor_utils_comprehensive.py +0 -0
@@ -0,0 +1,9 @@
1
+ [flake8]
2
+ max-line-length = 100
3
+ exclude = venv,env,__pycache__,tests
4
+ # E501: Line too long (we use Black's 100 char limit, not PEP8's 79)
5
+ # W503: Line break before binary operator (Black style)
6
+ # E203: Whitespace before ':' (Black vs flake8 conflict in slices)
7
+ # E226: Missing whitespace around arithmetic operator (intentional for readability)
8
+ # E741: Ambiguous variable name (contextually clear in usage)
9
+ ignore = E501,W503,E203,E226,E741
@@ -0,0 +1,70 @@
1
+ # Pre-commit hooks for Towel
2
+ # Install: pip install pre-commit && pre-commit install
3
+ # Run manually: pre-commit run --all-files
4
+
5
+ repos:
6
+ - repo: local
7
+ hooks:
8
+ - id: black
9
+ name: Black
10
+ entry: python -m black --check --line-length=100
11
+ language: system
12
+ types: [python]
13
+ exclude: ^(test_examples|\.templates/)
14
+ - id: flake8
15
+ name: Flake8
16
+ entry: python -m flake8 --config=.flake8
17
+ language: system
18
+ types: [python]
19
+ files: ^(src/towel/|scripts/)
20
+ - id: mypy
21
+ name: Mypy
22
+ entry: python -m mypy
23
+ language: system
24
+ pass_filenames: false
25
+ always_run: true
26
+ - id: bandit
27
+ name: Bandit
28
+ entry: python -m bandit -ll
29
+ language: system
30
+ types: [python]
31
+ files: ^src/towel/
32
+
33
+ # General pre-commit hooks
34
+ - repo: https://github.com/pre-commit/pre-commit-hooks
35
+ rev: v4.5.0
36
+ hooks:
37
+ # Remove trailing whitespace
38
+ - id: trailing-whitespace
39
+ exclude: ^test_examples.*_expected_output/
40
+
41
+ # Ensure files end with newline
42
+ - id: end-of-file-fixer
43
+ exclude: ^test_examples.*_expected_output/
44
+
45
+ # Check YAML syntax
46
+ - id: check-yaml
47
+
48
+ # Prevent large files from being committed
49
+ - id: check-added-large-files
50
+ args: [--maxkb=500]
51
+
52
+ # Check for merge conflicts
53
+ - id: check-merge-conflict
54
+
55
+ # Check TOML syntax
56
+ - id: check-toml
57
+
58
+ # Check for Python syntax errors
59
+ - id: check-ast
60
+
61
+ # Check for debugger imports
62
+ - id: debug-statements
63
+
64
+ # Docstring checking with pydocstyle (disabled for pre-commit, run manually)
65
+ # - repo: https://github.com/pycqa/pydocstyle
66
+ # rev: 6.3.0
67
+ # hooks:
68
+ # - id: pydocstyle
69
+ # args: [--config=setup.cfg]
70
+ # exclude: ^(tests/|test_examples|\.templates/|scripts/)
@@ -0,0 +1,406 @@
1
+ # Test Examples - Comprehensive Test Suite
2
+
3
+ This directory contains comprehensive test examples designed to stress test Towel's unification-based refactoring engine.
4
+
5
+ ## Overview
6
+
7
+ **Total Examples:** 18 Python files
8
+ **Total Refactoring Opportunities Detected:** 259
9
+ **Non-Overlapping Proposals:** 60
10
+
11
+ These examples are intentionally designed to test edge cases, complex patterns, and challenging scenarios that might break naive duplicate detection systems.
12
+
13
+ ## Test Categories
14
+
15
+ ### 1. Basic Examples (Original)
16
+
17
+ #### `example1_simple.py`
18
+ - Simple validation logic duplication
19
+ - Basic parameter extraction
20
+ - Tests: Straightforward code with minor differences
21
+
22
+ #### `example2_classes.py`
23
+ - Class-based duplication
24
+ - Method extraction
25
+ - Tests: Object-oriented patterns
26
+
27
+ #### `example3_file1.py` + `example3_file2.py`
28
+ - Cross-file duplication
29
+ - Import generation
30
+ - Tests: Multi-file refactoring with imports
31
+
32
+ #### `example4_complex.py`
33
+ - Complex data processing loops
34
+ - Multiple duplicate patterns
35
+ - Tests: Real-world data transformation code
36
+
37
+ ### 2. Binding Constructs
38
+
39
+ #### `bindings_for_loops.py`
40
+ - Alpha-renaming of loop variables (i, j, k)
41
+ - Tests: For loop variable equivalence
42
+
43
+ #### `bindings_comprehensions.py`
44
+ - List, dict, set comprehensions
45
+ - Generator expressions
46
+ - Tests: Comprehension variable scoping
47
+
48
+ ### 3. Return Value Propagation
49
+
50
+ #### `return_values.py`
51
+ - Early returns in blocks
52
+ - Nested returns
53
+ - Multiple return paths
54
+ - Tests: Return value detection at any position
55
+
56
+ ### 4. F-String and Constants
57
+
58
+ #### `fstrings_constants.py`
59
+ - F-string handling (never parameterize literal parts)
60
+ - Constant parameterization
61
+ - Tests: AST manipulation of format strings
62
+
63
+ ### 5. Scoping Edge Cases
64
+
65
+ #### `scoping_edge_cases.py`
66
+ - Nested functions
67
+ - Lambda expressions
68
+ - Builtin handling
69
+ - Tests: Scope analysis edge cases
70
+
71
+ ---
72
+
73
+ ## New Comprehensive Examples
74
+
75
+ ### 6. Complex Expressions (`complex_expressions.py`)
76
+
77
+ **Purpose:** Test parameterization of complex sub-expressions
78
+
79
+ **Patterns Tested:**
80
+ - Nested arithmetic expressions: `(x * 2 + 10)` vs `(x * 3 - 5)`
81
+ - Complex boolean logic: `(level > 5 and base > 100) or (level > 10 and base > 50)`
82
+ - Method chains: `processor.normalize(data["values"]).upper().strip()`
83
+ - Nested comprehensions with complex expressions
84
+ - Multiple operations in single expression
85
+
86
+ **Detected Opportunities:** ~14 proposals
87
+
88
+ **Why It's Challenging:**
89
+ - Requires identifying exact sub-expression boundaries
90
+ - Must parameterize only the differing parts
91
+ - Tests that the unifier doesn't over-parameterize
92
+
93
+ ---
94
+
95
+ ### 7. Hygienic Naming (`hygienic_naming.py`)
96
+
97
+ **Purpose:** Test name collision avoidance in extracted functions
98
+
99
+ **Patterns Tested:**
100
+ - Parameter names that collide with outer scope variables
101
+ - Temporary variables that might shadow parameters
102
+ - Nested function definitions with same names
103
+ - Multiple parameters with common names (a, b, c, temp, result)
104
+
105
+ **Detected Opportunities:** ~12 proposals
106
+
107
+ **Why It's Challenging:**
108
+ - Extracted function parameters must not collide with:
109
+ - Variables in calling context
110
+ - Function parameters
111
+ - Local variables in surrounding scope
112
+ - Tests hygienic macro-style renaming
113
+
114
+ **Example:**
115
+ ```python
116
+ def process(data, result): # 'result' is parameter
117
+ result = [] # 'result' is reassigned
118
+ for item in data:
119
+ x = item * 2 # Block to extract uses 'x'
120
+ ... # Extracted function must avoid 'result' collision
121
+ ```
122
+
123
+ ---
124
+
125
+ ### 8. Referential Transparency (`referential_transparency.py`)
126
+
127
+ **Purpose:** Test that variable semantics are preserved
128
+
129
+ **Patterns Tested:**
130
+ - Mutable state modifications within blocks
131
+ - Side effects (logging, I/O)
132
+ - Closures over variables
133
+ - Early returns that affect control flow
134
+ - Nested scope variable capture
135
+ - Multiple external state modifications
136
+
137
+ **Detected Opportunities:** ~16 proposals
138
+
139
+ **Why It's Challenging:**
140
+ - Must preserve exact semantics of variable references
141
+ - Side effects must occur in correct order
142
+ - Mutable state modifications must maintain correctness
143
+ - Tests referential transparency violations
144
+
145
+ **Example:**
146
+ ```python
147
+ counter = {"total": 0}
148
+ for item in items:
149
+ counter["total"] += item # Modifies external state
150
+ result = counter["total"] / counter["processed"]
151
+ # Extraction must preserve mutation semantics
152
+ ```
153
+
154
+ ---
155
+
156
+ ### 9. Nested Structures (`nested_structures.py`)
157
+
158
+ **Purpose:** Test deep nesting and complex data structures as parameters
159
+
160
+ **Patterns Tested:**
161
+ - Deep dictionary nesting: `item["user"]["profile"]["settings"]["threshold"]`
162
+ - Nested comprehensions: `[x ** 2 for x in row if x > threshold]`
163
+ - Complex nested structure building
164
+ - Nested list operations
165
+ - Deep dictionary merging
166
+ - Mixed-type processing (strings, numbers, lists)
167
+ - Chained nested operations
168
+
169
+ **Detected Opportunities:** ~18 proposals
170
+
171
+ **Why It's Challenging:**
172
+ - Parameters can be complex nested expressions
173
+ - Must track nesting depth correctly
174
+ - Tests that the unifier handles complex AST structures
175
+
176
+ **Example:**
177
+ ```python
178
+ value = item["user"]["profile"]["settings"]["threshold"] # Deep nesting as parameter
179
+ processed = [x ** 2 for x in [y for y in data if y > 0]] # Nested comprehension
180
+ ```
181
+
182
+ ---
183
+
184
+ ### 10. Method Chains (`method_chains.py`)
185
+
186
+ **Purpose:** Test method chaining and fluent interfaces
187
+
188
+ **Patterns Tested:**
189
+ - API response processing: `response.json().get("data", {}).get("items", [])`
190
+ - Fluent interface patterns: `model.set_name().set_description().validate()`
191
+ - Database query builders
192
+ - Stream processing pipelines
193
+ - Response builder patterns
194
+ - Entity transformations
195
+ - Aggregation with method chaining
196
+
197
+ **Detected Opportunities:** ~16 proposals
198
+
199
+ **Why It's Challenging:**
200
+ - Long method chains must be parameterized correctly
201
+ - Fluent interface calls have intermediate return values
202
+ - Tests object-oriented programming patterns
203
+
204
+ **Example:**
205
+ ```python
206
+ results = (db.table("users")
207
+ .where("age", ">", 18)
208
+ .order_by("created_at", "desc")
209
+ .limit(100)
210
+ .get())
211
+ # Different limit value becomes parameter
212
+ ```
213
+
214
+ ---
215
+
216
+ ### 11. Functional Patterns (`functional_patterns.py`)
217
+
218
+ **Purpose:** Test lambda expressions and functional programming
219
+
220
+ **Patterns Tested:**
221
+ - Lambdas with captured variables: `lambda x: x * multiplier + 10`
222
+ - `map`, `filter`, `reduce` patterns
223
+ - Higher-order functions that return functions
224
+ - Function composition: `h(g(f(x)))`
225
+ - Partial application
226
+ - Currying patterns
227
+ - Generator expressions with lambdas
228
+
229
+ **Detected Opportunities:** ~18 proposals
230
+
231
+ **Why It's Challenging:**
232
+ - Lambdas create closures over variables
233
+ - Higher-order functions have complex types
234
+ - Composition chains must be parameterized correctly
235
+ - Tests functional programming paradigms
236
+
237
+ **Example:**
238
+ ```python
239
+ transform = lambda x: x * multiplier + 10 # Captures multiplier
240
+ filtered = filter(lambda x: x > 0, data)
241
+ result = list(map(transform, filtered))
242
+ # Different offset value becomes parameter
243
+ ```
244
+
245
+ ---
246
+
247
+ ### 12. Real World Patterns (`real_world_patterns.py`)
248
+
249
+ **Purpose:** Test patterns from actual production code
250
+
251
+ **Patterns Tested:**
252
+ - API request handling (auth, rate limiting, validation)
253
+ - ETL pipelines with retry logic
254
+ - Business rule validation
255
+ - Batch processing with error handling
256
+ - Cache-with-fallback patterns
257
+ - Time-series metric aggregation
258
+
259
+ **Detected Opportunities:** ~14 proposals
260
+
261
+ **Why It's Challenging:**
262
+ - Complex multi-step workflows
263
+ - Error handling with multiple exception types
264
+ - Retry logic with state
265
+ - Production-grade patterns with logging and metrics
266
+
267
+ **Example:**
268
+ ```python
269
+ # API request with auth, rate limiting, validation, logging
270
+ if not auth.verify_token(request.headers.get("Authorization")):
271
+ logger.warn("Invalid token")
272
+ return {"error": "Unauthorized"}, 401
273
+ # ... complex multi-step flow
274
+ ```
275
+
276
+ ---
277
+
278
+ ### 13. Edge Cases & Stress Tests (`edge_cases_stress_test.py`)
279
+
280
+ **Purpose:** Combine multiple challenges to stress test the system
281
+
282
+ **Patterns Tested:**
283
+ - **Deep nesting:** 3+ levels of nested loops
284
+ - **Many parameters:** Functions with 8+ parameters in complex expressions
285
+ - **Complex control flow:** Multiple if-elif-else branches
286
+ - **Mixed comprehensions:** List, dict, set, and nested comprehensions together
287
+ - **Exception-heavy code:** Multiple exception types and handlers
288
+ - **State machines:** Complex state transition logic
289
+
290
+ **Detected Opportunities:** ~12 proposals
291
+
292
+ **Why It's Challenging:**
293
+ - Combines multiple edge cases in single examples
294
+ - Tests system limits (parameter count, nesting depth)
295
+ - Complex control flow graphs
296
+ - Real stress test scenarios
297
+
298
+ **Example:**
299
+ ```python
300
+ # Deep nesting with many variables
301
+ for outer in data:
302
+ if outer.get("enabled"):
303
+ for middle in outer.get("children", []):
304
+ if middle.get("status") == "active":
305
+ for inner in middle.get("values", []):
306
+ computed = (inner["value"] * config["multiplier"] +
307
+ config["offset"]) ** config["power"]
308
+ # ... more complexity
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Summary Statistics
314
+
315
+ | Category | Files | Patterns | Proposals |
316
+ |----------|-------|----------|-----------|
317
+ | Basic Examples | 4 | 15+ | ~20 |
318
+ | Binding/Scoping | 3 | 20+ | ~25 |
319
+ | Complex Expressions | 1 | 8 | ~14 |
320
+ | Hygienic Naming | 1 | 8 | ~12 |
321
+ | Referential Transparency | 1 | 8 | ~16 |
322
+ | Nested Structures | 1 | 10 | ~18 |
323
+ | Method Chains | 1 | 8 | ~16 |
324
+ | Functional Patterns | 1 | 9 | ~18 |
325
+ | Real World | 1 | 7 | ~14 |
326
+ | Edge Cases/Stress | 1 | 6 | ~12 |
327
+ | **TOTAL** | **18** | **99+** | **~259** |
328
+
329
+ ## What These Tests Validate
330
+
331
+ ### Unification Algorithm
332
+ - ✅ Correctly identifies structural similarity
333
+ - ✅ Parameterizes only differing expressions
334
+ - ✅ Handles complex nested AST nodes
335
+ - ✅ Respects alpha-equivalence of binding constructs
336
+
337
+ ### Hygienic Function Extraction
338
+ - ✅ Generates unique parameter names without collisions
339
+ - ✅ Preserves variable semantics and scope
340
+ - ✅ Maintains referential transparency
341
+ - ✅ Handles closure capture correctly
342
+
343
+ ### Parameter Extraction
344
+ - ✅ Extracts complex sub-expressions as parameters
345
+ - ✅ Handles nested structures (lists, dicts, comprehensions)
346
+ - ✅ Works with method chains and fluent interfaces
347
+ - ✅ Respects maximum parameter limits
348
+
349
+ ### Scope Analysis
350
+ - ✅ Correctly analyzes nested scopes
351
+ - ✅ Identifies free variables vs bound variables
352
+ - ✅ Handles comprehension variable scoping
353
+ - ✅ Detects orphan variables (would-be undefined refs)
354
+
355
+ ### Safety Guarantees
356
+ - ✅ Never extracts code that would create orphan variables
357
+ - ✅ Propagates return values correctly
358
+ - ✅ Preserves side effects and their ordering
359
+ - ✅ Maintains exception handling semantics
360
+
361
+ ## Usage
362
+
363
+ ### Preview All Examples
364
+ ```bash
365
+ just preview test_examples/
366
+ ```
367
+
368
+ ### Preview Specific Category
369
+ ```bash
370
+ just preview test_examples/complex_expressions.py
371
+ just preview test_examples/hygienic_naming.py
372
+ ```
373
+
374
+ ### Apply Refactorings
375
+ ```bash
376
+ # Always to a different directory first!
377
+ just dry test_examples/ test_examples_refactored/
378
+ ```
379
+
380
+ ### Reset After Testing
381
+ ```bash
382
+ just reset-examples
383
+ just verify-examples
384
+ ```
385
+
386
+ ## Maintaining Examples
387
+
388
+ All example files have pristine templates in `.templates/` directory:
389
+ - **Adding new examples:** Create in `test_examples/`, copy to `.templates/`, update justfile
390
+ - **Verifying integrity:** Run `just verify-examples`
391
+ - **Resetting corrupted files:** Run `just reset-examples`
392
+
393
+ ## Expected Behavior
394
+
395
+ When running `just preview test_examples/`:
396
+
397
+ 1. **Total Proposals:** ~259 refactoring opportunities detected
398
+ 2. **After Overlap Filtering:** ~60 non-overlapping proposals selected
399
+ 3. **Filtering Rate:** ~23% (removes ~199 overlapping proposals)
400
+
401
+ This high number of overlapping proposals is expected because:
402
+ - The engine extracts ALL possible contiguous sub-blocks
403
+ - A 10-line function generates proposals for blocks of length 10, 9, 8, ..., down to min_lines
404
+ - Overlap filtering keeps only the largest non-overlapping proposals
405
+
406
+ The fact that the system handles this gracefully validates the overlap filtering mechanism!
@@ -0,0 +1,137 @@
1
+ """
2
+ Comprehensive test cases for all Python binding constructs.
3
+
4
+ Tests that Towel correctly handles free variable analysis for:
5
+ - Walrus operator (:=)
6
+ - With statements (with ... as)
7
+ - Async constructs (async for, async with)
8
+ """
9
+
10
+
11
+ def test_walrus_basic_a(items):
12
+ """Walrus operator in if condition."""
13
+ result = []
14
+ if (n := len(items)) > 0:
15
+ result.append(n)
16
+ result.append(n * 2)
17
+ return result
18
+
19
+
20
+ def test_walrus_basic_b(items):
21
+ """Duplicate with walrus operator."""
22
+ result = []
23
+ if (n := len(items)) > 0:
24
+ result.append(n)
25
+ result.append(n * 2)
26
+ return result
27
+
28
+
29
+ def test_with_statement_a(filename):
30
+ """With statement binding."""
31
+ lines = []
32
+ with open(filename) as f:
33
+ for line in f:
34
+ lines.append(line.strip())
35
+ return lines
36
+
37
+
38
+ def test_with_statement_b(filename):
39
+ """Duplicate with statement."""
40
+ lines = []
41
+ with open(filename) as f:
42
+ for line in f:
43
+ lines.append(line.strip())
44
+ return lines
45
+
46
+
47
+ def test_with_multiple_a(file1, file2):
48
+ """Multiple context managers."""
49
+ data = []
50
+ with open(file1) as f1, open(file2) as f2:
51
+ data.append(f1.read())
52
+ data.append(f2.read())
53
+ return data
54
+
55
+
56
+ def test_with_multiple_b(file1, file2):
57
+ """Duplicate multiple context managers."""
58
+ data = []
59
+ with open(file1) as f1, open(file2) as f2:
60
+ data.append(f1.read())
61
+ data.append(f2.read())
62
+ return data
63
+
64
+
65
+ def test_walrus_in_comprehension_a(items):
66
+ """Walrus in list comprehension."""
67
+ # Note: Walrus leaks from comprehension scope
68
+ results = [(y := x * 2) for x in items if x > 0]
69
+ return results
70
+
71
+
72
+ def test_walrus_in_comprehension_b(items):
73
+ """Duplicate walrus in comprehension."""
74
+ results = [(y := x * 2) for x in items if x > 0]
75
+ return results
76
+
77
+
78
+ def test_nested_with_a(outer_file, inner_file):
79
+ """Nested with statements."""
80
+ result = []
81
+ with open(outer_file) as f1:
82
+ result.append(f1.readline())
83
+ with open(inner_file) as f2:
84
+ result.append(f2.readline())
85
+ return result
86
+
87
+
88
+ def test_nested_with_b(outer_file, inner_file):
89
+ """Duplicate nested with statements."""
90
+ result = []
91
+ with open(outer_file) as f1:
92
+ result.append(f1.readline())
93
+ with open(inner_file) as f2:
94
+ result.append(f2.readline())
95
+ return result
96
+
97
+
98
+ def test_exception_handler_binding_a(data):
99
+ """Exception variable binding."""
100
+ errors = []
101
+ for item in data:
102
+ try:
103
+ result = int(item)
104
+ except ValueError as e:
105
+ errors.append(str(e))
106
+ return errors
107
+
108
+
109
+ def test_exception_handler_binding_b(data):
110
+ """Duplicate exception variable binding."""
111
+ errors = []
112
+ for item in data:
113
+ try:
114
+ result = int(item)
115
+ except ValueError as e:
116
+ errors.append(str(e))
117
+ return errors
118
+
119
+
120
+ def test_walrus_while_a(items):
121
+ """Walrus in while loop condition."""
122
+ results = []
123
+ idx = 0
124
+ while (item := items[idx] if idx < len(items) else None) is not None:
125
+ results.append(item * 2)
126
+ idx += 1
127
+ return results
128
+
129
+
130
+ def test_walrus_while_b(items):
131
+ """Duplicate walrus in while loop."""
132
+ results = []
133
+ idx = 0
134
+ while (item := items[idx] if idx < len(items) else None) is not None:
135
+ results.append(item * 2)
136
+ idx += 1
137
+ return results
@@ -0,0 +1,61 @@
1
+ """
2
+ Test cases for comprehension binding constructs.
3
+
4
+ Tests that comprehension variables are properly recognized as bindings.
5
+ """
6
+
7
+
8
+ def list_comp_a(numbers):
9
+ """List comprehension with variable 'x'."""
10
+ result = [x * 2 for x in numbers if x > 0]
11
+ return result
12
+
13
+
14
+ def list_comp_b(numbers):
15
+ """List comprehension with variable 'y' (should unify with x)."""
16
+ result = [y * 2 for y in numbers if y > 0]
17
+ return result
18
+
19
+
20
+ def dict_comp_a(items):
21
+ """Dict comprehension."""
22
+ result = {k: v * 2 for k, v in items if v > 0}
23
+ return result
24
+
25
+
26
+ def dict_comp_b(items):
27
+ """Dict comprehension with different variable names."""
28
+ result = {key: val * 2 for key, val in items if val > 0}
29
+ return result
30
+
31
+
32
+ def nested_comp_a(matrix):
33
+ """Nested comprehensions."""
34
+ result = [[cell * 2 for cell in row] for row in matrix]
35
+ return result
36
+
37
+
38
+ def nested_comp_b(matrix):
39
+ """Nested comprehensions with different variable names."""
40
+ result = [[item * 2 for item in line] for line in matrix]
41
+ return result
42
+
43
+
44
+ def generator_expr_a(numbers):
45
+ """Generator expression."""
46
+ return sum(x * 2 for x in numbers if x > 0)
47
+
48
+
49
+ def generator_expr_b(numbers):
50
+ """Generator expression with different variable name."""
51
+ return sum(y * 2 for y in numbers if y > 0)
52
+
53
+
54
+ def set_comp_a(items):
55
+ """Set comprehension."""
56
+ return {item.upper() for item in items if len(item) > 2}
57
+
58
+
59
+ def set_comp_b(items):
60
+ """Set comprehension with different variable name."""
61
+ return {elem.upper() for elem in items if len(elem) > 2}