code-towel 1.732__tar.gz → 1.772__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 (1735) hide show
  1. code_towel-1.772/CHANGELOG.md +2078 -0
  2. code_towel-1.772/CONTRIBUTING.md +222 -0
  3. code_towel-1.772/MANIFEST.in +16 -0
  4. code_towel-1.772/PKG-INFO +432 -0
  5. code_towel-1.772/README.md +387 -0
  6. code_towel-1.772/SECURITY.md +19 -0
  7. code_towel-1.772/docs/ADVERSARIAL_REVIEW.md +173 -0
  8. code_towel-1.772/docs/ARCHITECTURE.md +1589 -0
  9. code_towel-1.772/docs/DECISIONS.md +1027 -0
  10. code_towel-1.772/docs/KNOWN_LIMITATIONS.md +2200 -0
  11. code_towel-1.772/docs/OPEN_SOURCE_AUDIT.md +81 -0
  12. code_towel-1.772/docs/PRODUCTION_READINESS.md +948 -0
  13. code_towel-1.772/docs/QUICKSTART.md +102 -0
  14. code_towel-1.772/docs/RELEASE_LOG.md +454 -0
  15. code_towel-1.772/docs/RELEASING.md +319 -0
  16. code_towel-1.772/docs/USAGE_GUIDE.md +915 -0
  17. code_towel-1.772/docs/proposals/decline-capabilities.md +106 -0
  18. code_towel-1.772/docs/proposals/performance-roadmap.md +148 -0
  19. code_towel-1.772/docs/proposals/preview-what-dry-would-do.md +94 -0
  20. code_towel-1.772/docs/proposals/project-own-check.md +122 -0
  21. code_towel-1.772/docs/proposals/reuse-existing-function.md +175 -0
  22. code_towel-1.772/docs/proposals/shared-helper-module.md +174 -0
  23. code_towel-1.772/docs/proposals/template-method.md +162 -0
  24. code_towel-1.772/docs/proposals/trusted-decorators.md +44 -0
  25. code_towel-1.772/docs/proposals/type-parameters.md +382 -0
  26. code_towel-1.772/justfile +198 -0
  27. code_towel-1.772/pyproject.toml +134 -0
  28. code_towel-1.772/scripts/add_copyright_headers.py +160 -0
  29. code_towel-1.772/scripts/audit_dependencies.py +70 -0
  30. code_towel-1.772/scripts/bench_refactor.py +132 -0
  31. code_towel-1.772/scripts/check_mermaid.mjs +62 -0
  32. code_towel-1.772/scripts/ecosystem/Dockerfile +42 -0
  33. code_towel-1.772/scripts/ecosystem/manifest.toml +1136 -0
  34. code_towel-1.772/scripts/ecosystem_check.py +3423 -0
  35. code_towel-1.772/scripts/report_net_benefit_diff.py +130 -0
  36. code_towel-1.772/scripts/run_crossfile_coverage.py +41 -0
  37. code_towel-1.772/scripts/set_version.py +47 -0
  38. code_towel-1.772/scripts/trace_proposals.py +113 -0
  39. code_towel-1.772/scripts/verify-examples +125 -0
  40. code_towel-1.772/src/code_towel.egg-info/PKG-INFO +432 -0
  41. code_towel-1.772/src/code_towel.egg-info/SOURCES.txt +1275 -0
  42. code_towel-1.772/src/towel/_mypy_worker.py +1321 -0
  43. code_towel-1.772/src/towel/canonical_ast.py +115 -0
  44. code_towel-1.772/src/towel/changes.py +531 -0
  45. code_towel-1.772/src/towel/checker_project.py +1119 -0
  46. code_towel-1.772/src/towel/cli.py +2096 -0
  47. code_towel-1.772/src/towel/consumers.py +405 -0
  48. code_towel-1.772/src/towel/coverage_config.py +339 -0
  49. code_towel-1.772/src/towel/declared_python.py +176 -0
  50. code_towel-1.772/src/towel/declared_requirements.py +404 -0
  51. code_towel-1.772/src/towel/filesystem.py +421 -0
  52. code_towel-1.772/src/towel/formatting.py +770 -0
  53. code_towel-1.772/src/towel/import_model.py +2586 -0
  54. code_towel-1.772/src/towel/program_files.py +424 -0
  55. code_towel-1.772/src/towel/project_layout.py +104 -0
  56. code_towel-1.772/src/towel/pyright_session.py +909 -0
  57. code_towel-1.772/src/towel/reachability.py +214 -0
  58. code_towel-1.772/src/towel/renaming.py +1697 -0
  59. code_towel-1.772/src/towel/shipped_files.py +890 -0
  60. code_towel-1.772/src/towel/source_files.py +108 -0
  61. code_towel-1.772/src/towel/source_text.py +207 -0
  62. code_towel-1.772/src/towel/type_baseline.py +967 -0
  63. code_towel-1.772/src/towel/type_inference.py +2237 -0
  64. code_towel-1.772/src/towel/unification/annotation_imports.py +388 -0
  65. code_towel-1.772/src/towel/unification/annotation_ladder.py +1469 -0
  66. code_towel-1.772/src/towel/unification/annotation_wiring.py +1955 -0
  67. code_towel-1.772/src/towel/unification/annotations.py +1632 -0
  68. code_towel-1.772/src/towel/unification/assert_rewriting.py +439 -0
  69. code_towel-1.772/src/towel/unification/assignment_analyzer.py +346 -0
  70. code_towel-1.772/src/towel/unification/block_analysis.py +1128 -0
  71. code_towel-1.772/src/towel/unification/block_comments.py +2938 -0
  72. code_towel-1.772/src/towel/unification/bounded_cache.py +137 -0
  73. code_towel-1.772/src/towel/unification/builtins.py +261 -0
  74. code_towel-1.772/src/towel/unification/class_private.py +196 -0
  75. code_towel-1.772/src/towel/unification/clustering.py +592 -0
  76. code_towel-1.772/src/towel/unification/constant_consistency.py +174 -0
  77. code_towel-1.772/src/towel/unification/decorator_reach.py +1869 -0
  78. code_towel-1.772/src/towel/unification/definite_assignment.py +390 -0
  79. code_towel-1.772/src/towel/unification/engine_state.py +501 -0
  80. code_towel-1.772/src/towel/unification/exceptions.py +161 -0
  81. code_towel-1.772/src/towel/unification/extractor.py +1040 -0
  82. code_towel-1.772/src/towel/unification/fixed_point.py +1267 -0
  83. code_towel-1.772/src/towel/unification/function_scope.py +456 -0
  84. code_towel-1.772/src/towel/unification/generic_annotations.py +725 -0
  85. code_towel-1.772/src/towel/unification/hof_promotion.py +223 -0
  86. code_towel-1.772/src/towel/unification/import_graph.py +2117 -0
  87. code_towel-1.772/src/towel/unification/insertion.py +338 -0
  88. code_towel-1.772/src/towel/unification/instantiation.py +1183 -0
  89. code_towel-1.772/src/towel/unification/known_bases.py +220 -0
  90. code_towel-1.772/src/towel/unification/known_decorators.py +380 -0
  91. code_towel-1.772/src/towel/unification/known_platforms.py +1166 -0
  92. code_towel-1.772/src/towel/unification/known_source_readers.py +140 -0
  93. code_towel-1.772/src/towel/unification/lexical_scopes.py +195 -0
  94. code_towel-1.772/src/towel/unification/materialize.py +1099 -0
  95. code_towel-1.772/src/towel/unification/models.py +480 -0
  96. code_towel-1.772/src/towel/unification/module_bindings.py +458 -0
  97. code_towel-1.772/src/towel/unification/namespace_writes.py +934 -0
  98. code_towel-1.772/src/towel/unification/narrowing.py +277 -0
  99. code_towel-1.772/src/towel/unification/pair_evaluation.py +1822 -0
  100. code_towel-1.772/src/towel/unification/parallel.py +436 -0
  101. code_towel-1.772/src/towel/unification/parameterization.py +347 -0
  102. code_towel-1.772/src/towel/unification/pipeline.py +467 -0
  103. code_towel-1.772/src/towel/unification/placement.py +823 -0
  104. code_towel-1.772/src/towel/unification/program_imports.py +208 -0
  105. code_towel-1.772/src/towel/unification/progress.py +270 -0
  106. code_towel-1.772/src/towel/unification/project_layout.py +23 -0
  107. code_towel-1.772/src/towel/unification/refactor_engine.py +1090 -0
  108. code_towel-1.772/src/towel/unification/reuse.py +200 -0
  109. code_towel-1.772/src/towel/unification/revealed_types.py +487 -0
  110. code_towel-1.772/src/towel/unification/scope_analyzer.py +793 -0
  111. code_towel-1.772/src/towel/unification/semantic_safety.py +1962 -0
  112. code_towel-1.772/src/towel/unification/source_readers.py +409 -0
  113. code_towel-1.772/src/towel/unification/splicing.py +150 -0
  114. code_towel-1.772/src/towel/unification/statement_facts.py +294 -0
  115. code_towel-1.772/src/towel/unification/static_positions.py +508 -0
  116. code_towel-1.772/src/towel/unification/structural_memo.py +161 -0
  117. code_towel-1.772/src/towel/unification/substitution.py +134 -0
  118. code_towel-1.772/src/towel/unification/thunk_inlining.py +567 -0
  119. code_towel-1.772/src/towel/unification/type_bindings.py +1091 -0
  120. code_towel-1.772/src/towel/unification/type_generalization.py +279 -0
  121. code_towel-1.772/src/towel/unification/typing_forms.py +325 -0
  122. code_towel-1.772/src/towel/unification/unifier.py +1180 -0
  123. code_towel-1.772/src/towel/unification/unifier_state.py +83 -0
  124. code_towel-1.772/src/towel/unification/visitors.py +710 -0
  125. code_towel-1.772/test_examples_crossfile/README.md +244 -0
  126. code_towel-1.772/test_examples_crossfile/multi_level/core/services/payment.py +29 -0
  127. code_towel-1.772/test_examples_crossfile/multi_level/utils/validators.py +28 -0
  128. code_towel-1.772/test_examples_crossfile/nested_structure/src/data_processor.py +25 -0
  129. code_towel-1.772/test_examples_crossfile/simple_crossfile/user_service.py +27 -0
  130. code_towel-1.772/test_examples_crossfile_expected_output/multi_level/api/checkout.py +35 -0
  131. code_towel-1.772/test_examples_crossfile_expected_output/multi_level/core/services/payment.py +20 -0
  132. code_towel-1.772/test_examples_crossfile_expected_output/multi_level/utils/validators.py +19 -0
  133. code_towel-1.772/test_examples_crossfile_expected_output/nested_structure/lib/report_generator.py +28 -0
  134. code_towel-1.772/test_examples_crossfile_expected_output/nested_structure/src/data_processor.py +19 -0
  135. code_towel-1.772/test_examples_crossfile_expected_output/simple_crossfile/admin_service.py +29 -0
  136. code_towel-1.772/test_examples_crossfile_expected_output/simple_crossfile/user_service.py +19 -0
  137. code_towel-1.772/test_examples_expected_output/annotated_module.py +108 -0
  138. code_towel-1.772/test_examples_expected_output/binding_constructs_comprehensive.py +133 -0
  139. code_towel-1.772/test_examples_expected_output/bindings_for_loops.py +60 -0
  140. code_towel-1.772/test_examples_expected_output/closure_adversarial.py +119 -0
  141. code_towel-1.772/test_examples_expected_output/control_flow_adversarial.py +145 -0
  142. code_towel-1.772/test_examples_expected_output/edge_cases_stress_test.py +211 -0
  143. code_towel-1.772/test_examples_expected_output/example4_complex.py +37 -0
  144. code_towel-1.772/test_examples_expected_output/exception_adversarial.py +156 -0
  145. code_towel-1.772/test_examples_expected_output/fstrings_constants.py +88 -0
  146. code_towel-1.772/test_examples_expected_output/functional_patterns.py +239 -0
  147. code_towel-1.772/test_examples_expected_output/method_chains.py +204 -0
  148. code_towel-1.772/test_examples_expected_output/return_values.py +96 -0
  149. code_towel-1.772/test_examples_expected_output/scoping_edge_cases.py +113 -0
  150. code_towel-1.772/test_examples_expected_output/side_effects_adversarial.py +122 -0
  151. code_towel-1.772/test_examples_expected_output/syntactic_coverage_comprehensive.py +735 -0
  152. code_towel-1.772/test_examples_expected_output/tricky_edge_cases_adversarial.py +221 -0
  153. code_towel-1.772/tests/README.md +324 -0
  154. code_towel-1.772/tests/__init__.py +17 -0
  155. code_towel-1.772/tests/audit_defects.py +27 -0
  156. code_towel-1.772/tests/automatic_equivalence_tester.py +652 -0
  157. code_towel-1.772/tests/conftest.py +188 -0
  158. code_towel-1.772/tests/crossfile_equivalence_tester.py +385 -0
  159. code_towel-1.772/tests/differential/__init__.py +37 -0
  160. code_towel-1.772/tests/differential/cases.py +113 -0
  161. code_towel-1.772/tests/differential/comparison.py +148 -0
  162. code_towel-1.772/tests/differential/export.py +264 -0
  163. code_towel-1.772/tests/differential/fuzz.py +259 -0
  164. code_towel-1.772/tests/differential/grammar.py +838 -0
  165. code_towel-1.772/tests/differential/observer.py +416 -0
  166. code_towel-1.772/tests/differential/runner.py +320 -0
  167. code_towel-1.772/tests/differential/scope_grammar.py +298 -0
  168. code_towel-1.772/tests/ecosystem_fixtures.py +214 -0
  169. code_towel-1.772/tests/edge_case_values.py +375 -0
  170. code_towel-1.772/tests/equivalence_targets.py +161 -0
  171. code_towel-1.772/tests/generate_baseline.py +198 -0
  172. code_towel-1.772/tests/hostile_cases/h01_side_effect_order.py +37 -0
  173. code_towel-1.772/tests/hostile_cases/h02_conditional_eval.py +30 -0
  174. code_towel-1.772/tests/hostile_cases/h03_loop_reeval.py +33 -0
  175. code_towel-1.772/tests/hostile_cases/h04_closure_freevar.py +30 -0
  176. code_towel-1.772/tests/hostile_cases/h05_cond_return_plus_retvar.py +34 -0
  177. code_towel-1.772/tests/hostile_cases/h06_del_in_block.py +34 -0
  178. code_towel-1.772/tests/hostile_cases/h07_except_unbind.py +38 -0
  179. code_towel-1.772/tests/hostile_cases/h08_late_binding_closure.py +30 -0
  180. code_towel-1.772/tests/hostile_cases/h10_short_circuit.py +28 -0
  181. code_towel-1.772/tests/hostile_cases/h13_dunder_file.py +27 -0
  182. code_towel-1.772/tests/hostile_cases/h26_match_capture.py +32 -0
  183. code_towel-1.772/tests/hostile_cases/h38_closure_after_block.py +32 -0
  184. code_towel-1.772/tests/hostile_cases/h39_augassign_after_block.py +30 -0
  185. code_towel-1.772/tests/hostile_cases/h51_semicolons.py +28 -0
  186. code_towel-1.772/tests/hostile_cases/h52_del_after_block.py +30 -0
  187. code_towel-1.772/tests/hostile_cases/h53_nested_func_reads_retvar.py +28 -0
  188. code_towel-1.772/tests/hostile_cases/h54_attr_param_property.py +35 -0
  189. code_towel-1.772/tests/hostile_cases/h55_walrus_used_after.py +31 -0
  190. code_towel-1.772/tests/hostile_cases/h56_global_write.py +29 -0
  191. code_towel-1.772/tests/hostile_cases/h57_exception_in_param.py +32 -0
  192. code_towel-1.772/tests/hostile_cases/h58_nested_def_freevar_reassigned.py +28 -0
  193. code_towel-1.772/tests/hostile_cases/h59_retvar_conditional_unbound.py +34 -0
  194. code_towel-1.772/tests/hostile_cases/h60_finally_return.py +32 -0
  195. code_towel-1.772/tests/hostile_cases/h61_str_method_param.py +30 -0
  196. code_towel-1.772/tests/hostile_cases/p01_static_helper_class_name_shadowed_by_parameter.py +39 -0
  197. code_towel-1.772/tests/hostile_cases/p02_static_helper_class_deleted.py +39 -0
  198. code_towel-1.772/tests/hostile_cases/p03_static_helper_mangled_class_name.py +38 -0
  199. code_towel-1.772/tests/hostile_cases/p04_static_helper_class_decorator_returns_factory.py +42 -0
  200. code_towel-1.772/tests/hostile_cases/p05_static_helper_class_global_rebound.py +41 -0
  201. code_towel-1.772/tests/hostile_cases/p06_static_helper_called_while_class_body_runs.py +38 -0
  202. code_towel-1.772/tests/hostile_cases/p07_static_methods_called_while_class_body_runs.py +39 -0
  203. code_towel-1.772/tests/hostile_cases/p08_classmethod_never_using_cls.py +39 -0
  204. code_towel-1.772/tests/hostile_cases/p09_static_helper_metaclass_hides_attribute.py +42 -0
  205. code_towel-1.772/tests/hostile_cases/p10_one_line_exception_base.py +39 -0
  206. code_towel-1.772/tests/hostile_cases/p11_one_line_base_docstring_and_assignment.py +37 -0
  207. code_towel-1.772/tests/hostile_cases/p12_one_line_body_after_a_split_header.py +40 -0
  208. code_towel-1.772/tests/hostile_cases/p13_protocol_default_methods.py +50 -0
  209. code_towel-1.772/tests/hostile_cases/p14_protocol_common_ancestor.py +46 -0
  210. code_towel-1.772/tests/hostile_cases/p15_class_decorator_rebuilds_namespace.py +44 -0
  211. code_towel-1.772/tests/hostile_cases/p16_class_decorator_wraps_every_function.py +52 -0
  212. code_towel-1.772/tests/hostile_cases/p17_decorated_base_rebuilds_namespace.py +46 -0
  213. code_towel-1.772/tests/hostile_cases/p18_known_class_decorators_keep_the_helper.py +99 -0
  214. code_towel-1.772/tests/hostile_cases/p19_thunk_of_possibly_unbound_local.py +44 -0
  215. code_towel-1.772/tests/hostile_cases/p20_thunk_of_loop_variable.py +42 -0
  216. code_towel-1.772/tests/hostile_cases/p21_helper_placed_after_an_assignment_that_calls_it.py +51 -0
  217. code_towel-1.772/tests/hostile_cases/r01_property_thunk_order.py +33 -0
  218. code_towel-1.772/tests/hostile_cases/r02_cluster_attrs.py +39 -0
  219. code_towel-1.772/tests/hostile_cases/r07_attr_two_positions.py +30 -0
  220. code_towel-1.772/tests/hostile_cases/r100_clustered_site_with_live_binding.py +36 -0
  221. code_towel-1.772/tests/hostile_cases/r101_same_named_method_forces_module_helper.py +57 -0
  222. code_towel-1.772/tests/hostile_cases/r102_whole_body_reuse.py +40 -0
  223. code_towel-1.772/tests/hostile_cases/r103_lambda_parameter_spelling.py +39 -0
  224. code_towel-1.772/tests/hostile_cases/r104_walrus_target_spelling.py +28 -0
  225. code_towel-1.772/tests/hostile_cases/r105_closure_over_block_bound_name_rebound_after.py +32 -0
  226. code_towel-1.772/tests/hostile_cases/r106_dir_reads_the_frame.py +27 -0
  227. code_towel-1.772/tests/hostile_cases/r107_unbound_global_argument.py +28 -0
  228. code_towel-1.772/tests/hostile_cases/r108_clustered_sites_assign_the_returned_name.py +37 -0
  229. code_towel-1.772/tests/hostile_cases/r109_eager_optional_import_failed.py +33 -0
  230. code_towel-1.772/tests/hostile_cases/r110_eager_type_checking_import.py +32 -0
  231. code_towel-1.772/tests/hostile_cases/r111_eager_module_except_as_name.py +36 -0
  232. code_towel-1.772/tests/hostile_cases/r112_eager_module_match_capture_unmatched.py +33 -0
  233. code_towel-1.772/tests/hostile_cases/r113_eager_class_attr_in_method.py +31 -0
  234. code_towel-1.772/tests/hostile_cases/r114_eager_nested_class_attr.py +33 -0
  235. code_towel-1.772/tests/hostile_cases/r115_eager_class_in_function_attr.py +34 -0
  236. code_towel-1.772/tests/hostile_cases/r116_eager_enclosing_bound_after_inner_call.py +33 -0
  237. code_towel-1.772/tests/hostile_cases/r117_eager_enclosing_deleted_before_inner_call.py +33 -0
  238. code_towel-1.772/tests/hostile_cases/r118_eager_enclosing_conditionally_bound.py +33 -0
  239. code_towel-1.772/tests/hostile_cases/r119_eager_module_def_and_class_after_call.py +33 -0
  240. code_towel-1.772/tests/hostile_cases/r11_same_function_pair.py +28 -0
  241. code_towel-1.772/tests/hostile_cases/r120_eager_module_import_after_call.py +31 -0
  242. code_towel-1.772/tests/hostile_cases/r121_eager_comprehension_variable.py +30 -0
  243. code_towel-1.772/tests/hostile_cases/r122_eager_lambda_parameter.py +30 -0
  244. code_towel-1.772/tests/hostile_cases/r123_direct_eval_after_block.py +30 -0
  245. code_towel-1.772/tests/hostile_cases/r124_eval_alias_reads_preblock_name.py +60 -0
  246. code_towel-1.772/tests/hostile_cases/r125_locals_dir_after_block.py +54 -0
  247. code_towel-1.772/tests/hostile_cases/r126_locals_in_nested_scope.py +44 -0
  248. code_towel-1.772/tests/hostile_cases/r127_eval_alias_partial_block.py +67 -0
  249. code_towel-1.772/tests/hostile_cases/r128_warn_stacklevel_via_alias.py +45 -0
  250. code_towel-1.772/tests/hostile_cases/r129_warning_registry_dedup.py +32 -0
  251. code_towel-1.772/tests/hostile_cases/r130_resource_observed_after_block_differs.py +34 -0
  252. code_towel-1.772/tests/hostile_cases/r131_weakref_and_id_identity.py +41 -0
  253. code_towel-1.772/tests/hostile_cases/r132_async_comprehension_in_block.py +30 -0
  254. code_towel-1.772/tests/hostile_cases/r134_u2028_in_comment_shifts_splice.py +31 -0
  255. code_towel-1.772/tests/hostile_cases/r136_u2028_in_string_literal.py +31 -0
  256. code_towel-1.772/tests/hostile_cases/r137_x1c_x85_in_comment.py +31 -0
  257. code_towel-1.772/tests/hostile_cases/r138_u2028_comment_same_indent_neighbours.py +38 -0
  258. code_towel-1.772/tests/hostile_cases/r139_literal_roundtrip.py +32 -0
  259. code_towel-1.772/tests/hostile_cases/r140_unbound_name_on_untaken_branch.py +35 -0
  260. code_towel-1.772/tests/hostile_cases/r141_module_name_bound_after_an_early_call.py +38 -0
  261. code_towel-1.772/tests/hostile_cases/r142_module_data_rebound_by_callback.py +36 -0
  262. code_towel-1.772/tests/hostile_cases/r143_module_name_shadowed_by_a_local_elsewhere.py +36 -0
  263. code_towel-1.772/tests/hostile_cases/r144_class_cell_in_a_method_helper.py +46 -0
  264. code_towel-1.772/tests/hostile_cases/r145_getframe_assigned_alias.py +61 -0
  265. code_towel-1.772/tests/hostile_cases/r146_module_function_reads_caller_frame.py +51 -0
  266. code_towel-1.772/tests/hostile_cases/r147_wide_int_literal.py +44 -0
  267. code_towel-1.772/tests/hostile_cases/r148_equal_constants_of_different_types.py +52 -0
  268. code_towel-1.772/tests/hostile_cases/r149_match_pattern_reads_caller_names.py +86 -0
  269. code_towel-1.772/tests/hostile_cases/r14_starred_param.py +29 -0
  270. code_towel-1.772/tests/hostile_cases/r150_pattern_parts_that_differ.py +95 -0
  271. code_towel-1.772/tests/hostile_cases/r151_pattern_names_that_may_differ.py +61 -0
  272. code_towel-1.772/tests/hostile_cases/r152_definition_time_reads.py +68 -0
  273. code_towel-1.772/tests/hostile_cases/r153_class_definition_reads.py +78 -0
  274. code_towel-1.772/tests/hostile_cases/r154_bare_annotation_reads_the_caller.py +32 -0
  275. code_towel-1.772/tests/hostile_cases/r155_lazy_type_values_close_over_caller_names.pynew +53 -0
  276. code_towel-1.772/tests/hostile_cases/r156_created_objects_that_escape.py +105 -0
  277. code_towel-1.772/tests/hostile_cases/r157_created_objects_only_called.py +69 -0
  278. code_towel-1.772/tests/hostile_cases/r158_getattr_serves_unknown_names_from_data.py +56 -0
  279. code_towel-1.772/tests/hostile_cases/r159_frame_handle_before_a_nested_block.py +46 -0
  280. code_towel-1.772/tests/hostile_cases/r160_host_binds_any_before_its_last_import.py +55 -0
  281. code_towel-1.772/tests/hostile_cases/r161_module_with_all_gains_no_public_name.py +50 -0
  282. code_towel-1.772/tests/hostile_cases/r162_host_star_imports_callable.py +46 -0
  283. code_towel-1.772/tests/hostile_cases/r22_fstring_attr.py +33 -0
  284. code_towel-1.772/tests/hostile_cases/r26_attr_store_param.py +28 -0
  285. code_towel-1.772/tests/hostile_cases/r27_del_subscript.py +26 -0
  286. code_towel-1.772/tests/hostile_cases/r32_method_callee.py +32 -0
  287. code_towel-1.772/tests/hostile_cases/r38_name_two_positions.py +28 -0
  288. code_towel-1.772/tests/hostile_cases/r39_cluster_alpha.py +31 -0
  289. code_towel-1.772/tests/hostile_cases/r40_class_hierarchy.py +35 -0
  290. code_towel-1.772/tests/hostile_cases/r41_conditional_call_param.py +31 -0
  291. code_towel-1.772/tests/hostile_cases/r42_loop_call_param.py +30 -0
  292. code_towel-1.772/tests/hostile_cases/r43_opaque_decorator_receiver.py +31 -0
  293. code_towel-1.772/tests/hostile_cases/r44_cluster_nested_overlap.py +37 -0
  294. code_towel-1.772/tests/hostile_cases/r45_async_no_await.py +31 -0
  295. code_towel-1.772/tests/hostile_cases/r46_nonlocal_counter.py +36 -0
  296. code_towel-1.772/tests/hostile_cases/r47_lru_cache_method.py +33 -0
  297. code_towel-1.772/tests/hostile_cases/r50_helper_name_collision.py +32 -0
  298. code_towel-1.772/tests/hostile_cases/r53_try_else_binding.py +42 -0
  299. code_towel-1.772/tests/hostile_cases/r54_with_binding_used_after.py +29 -0
  300. code_towel-1.772/tests/hostile_cases/r55_chained_assign.py +28 -0
  301. code_towel-1.772/tests/hostile_cases/r57_star_unpack.py +26 -0
  302. code_towel-1.772/tests/hostile_cases/r58_escaping_closure.py +30 -0
  303. code_towel-1.772/tests/hostile_cases/r60_local_import.py +28 -0
  304. code_towel-1.772/tests/hostile_cases/r61_same_function_clean.py +27 -0
  305. code_towel-1.772/tests/hostile_cases/r62_recursion.py +28 -0
  306. code_towel-1.772/tests/hostile_cases/r63_except_var_used_in_handler.py +36 -0
  307. code_towel-1.772/tests/hostile_cases/r66_conditional_callee.py +31 -0
  308. code_towel-1.772/tests/hostile_cases/r69_annassign.py +28 -0
  309. code_towel-1.772/tests/hostile_cases/r72_global_promoted.py +31 -0
  310. code_towel-1.772/tests/hostile_cases/r73_slice_vs_index.py +26 -0
  311. code_towel-1.772/tests/hostile_cases/r74_starred_vs_plain.py +29 -0
  312. code_towel-1.772/tests/hostile_cases/r75_prior_helper_param_names.py +28 -0
  313. code_towel-1.772/tests/hostile_cases/r76_return_order.py +30 -0
  314. code_towel-1.772/tests/hostile_cases/r77_mixed_return_and_variables.py +31 -0
  315. code_towel-1.772/tests/hostile_cases/r78_walrus_in_parameter.py +31 -0
  316. code_towel-1.772/tests/hostile_cases/r79_list_display_in_loop.py +37 -0
  317. code_towel-1.772/tests/hostile_cases/r7bi_augassign_and_del_after_block.py +61 -0
  318. code_towel-1.772/tests/hostile_cases/r7bi_except_name_in_nested_block.py +46 -0
  319. code_towel-1.772/tests/hostile_cases/r7bi_loop_capture_def_rebind_prebound.py +86 -0
  320. code_towel-1.772/tests/hostile_cases/r7bi_loop_del_before_eager_read.py +38 -0
  321. code_towel-1.772/tests/hostile_cases/r7bi_read_before_own_binding.py +46 -0
  322. code_towel-1.772/tests/hostile_cases/r7bi_renamed_binder_named_by_unbound_error.py +62 -0
  323. code_towel-1.772/tests/hostile_cases/r7c_loop_copy_beside_a_top_level_twin.py +38 -0
  324. code_towel-1.772/tests/hostile_cases/r7c_mangled_import_names.py +81 -0
  325. code_towel-1.772/tests/hostile_cases/r7c_mangled_parameter_passed_by_keyword.py +39 -0
  326. code_towel-1.772/tests/hostile_cases/r7c_mangled_parameter_passed_by_keyword_in_a_method_helper.py +42 -0
  327. code_towel-1.772/tests/hostile_cases/r7c_rebinding_enclosing_function_beside_a_benign_twin.py +64 -0
  328. code_towel-1.772/tests/hostile_cases/r7c_top_level_copy_beside_a_loop_copy.py +39 -0
  329. code_towel-1.772/tests/hostile_cases/r7c_unmangled_parameter_passed_by_keyword.py +51 -0
  330. code_towel-1.772/tests/hostile_cases/r7d_instrumenting_call_applied_by_hand.py +63 -0
  331. code_towel-1.772/tests/hostile_cases/r7d_instrumenting_class_decorator.py +63 -0
  332. code_towel-1.772/tests/hostile_cases/r7d_instrumenting_enclosing_function.py +64 -0
  333. code_towel-1.772/tests/hostile_cases/r7d_instrumenting_function_decorator.py +57 -0
  334. code_towel-1.772/tests/hostile_cases/r7d_metaclass_recompiles_methods.py +63 -0
  335. code_towel-1.772/tests/hostile_cases/r7d_method_helper_in_a_testcase.py +70 -0
  336. code_towel-1.772/tests/hostile_cases/r7d_plain_wrapper_applied_by_hand.py +74 -0
  337. code_towel-1.772/tests/hostile_cases/r7d_plain_wrapper_decorator.py +57 -0
  338. code_towel-1.772/tests/hostile_cases/r7fz_binding_builtin_rebound_mid_module.py +63 -0
  339. code_towel-1.772/tests/hostile_cases/r7fz_binding_cell_filled_late.py +71 -0
  340. code_towel-1.772/tests/hostile_cases/r7fz_binding_cell_read_before_fill.py +75 -0
  341. code_towel-1.772/tests/hostile_cases/r7fz_binding_class_attr_vs_local.py +62 -0
  342. code_towel-1.772/tests/hostile_cases/r7fz_binding_dunder_name_file_samemodule.py +60 -0
  343. code_towel-1.772/tests/hostile_cases/r7fz_binding_global_rebound_by_callee.py +67 -0
  344. code_towel-1.772/tests/hostile_cases/r7fz_binding_global_vs_local.py +68 -0
  345. code_towel-1.772/tests/hostile_cases/r7fz_binding_loop_var_leak.py +62 -0
  346. code_towel-1.772/tests/hostile_cases/r7fz_binding_module_getattr.py +69 -0
  347. code_towel-1.772/tests/hostile_cases/r7fz_binding_unbound_local_handler.py +71 -0
  348. code_towel-1.772/tests/hostile_cases/r7fz_builtins_differ_in_builtin.py +61 -0
  349. code_towel-1.772/tests/hostile_cases/r7fz_builtins_mock_patch_module.py +80 -0
  350. code_towel-1.772/tests/hostile_cases/r7fz_builtins_module_shadows_len.py +68 -0
  351. code_towel-1.772/tests/hostile_cases/r7fz_builtins_param_named_len.py +63 -0
  352. code_towel-1.772/tests/hostile_cases/r7fz_builtins_print_shadowed_local.py +64 -0
  353. code_towel-1.772/tests/hostile_cases/r7fz_classhost_dataclass_slots_super.py +81 -0
  354. code_towel-1.772/tests/hostile_cases/r7fz_classhost_dunder_class_same_class.py +65 -0
  355. code_towel-1.772/tests/hostile_cases/r7fz_classhost_enum_methods.py +64 -0
  356. code_towel-1.772/tests/hostile_cases/r7fz_classhost_getattr_fallback.py +68 -0
  357. code_towel-1.772/tests/hostile_cases/r7fz_classhost_init_subclass_wraps.py +79 -0
  358. code_towel-1.772/tests/hostile_cases/r7fz_classhost_metaclass_registry.py +74 -0
  359. code_towel-1.772/tests/hostile_cases/r7fz_classhost_private_same_class.py +81 -0
  360. code_towel-1.772/tests/hostile_cases/r7fz_classhost_siblings_plain.py +69 -0
  361. code_towel-1.772/tests/hostile_cases/r7fz_classhost_underscore_class_names.py +82 -0
  362. code_towel-1.772/tests/hostile_cases/r7fz_classhost_zero_arg_super.py +71 -0
  363. code_towel-1.772/tests/hostile_cases/r7fz_cluster_K_cell.py +73 -0
  364. code_towel-1.772/tests/hostile_cases/r7fz_cluster_generator_site.py +72 -0
  365. code_towel-1.772/tests/hostile_cases/r7fz_cluster_local_K.py +77 -0
  366. code_towel-1.772/tests/hostile_cases/r7fz_cluster_method_site.py +74 -0
  367. code_towel-1.772/tests/hostile_cases/r7fz_ctrl_break_in_try_finally.py +72 -0
  368. code_towel-1.772/tests/hostile_cases/r7fz_ctrl_match_class_pattern.py +78 -0
  369. code_towel-1.772/tests/hostile_cases/r7fz_ctrl_nested_try.py +74 -0
  370. code_towel-1.772/tests/hostile_cases/r7fz_ctrl_suppress_context.py +64 -0
  371. code_towel-1.772/tests/hostile_cases/r7fz_ctrl_walrus_while.py +64 -0
  372. code_towel-1.772/tests/hostile_cases/r7fz_directives_fmt_off_region.py +69 -0
  373. code_towel-1.772/tests/hostile_cases/r7fz_directives_noqa_on_argument.py +57 -0
  374. code_towel-1.772/tests/hostile_cases/r7fz_directives_pragma_branch.py +65 -0
  375. code_towel-1.772/tests/hostile_cases/r7fz_directives_type_ignore_line.py +68 -0
  376. code_towel-1.772/tests/hostile_cases/r7fz_extra_pragma_with_header.py +70 -0
  377. code_towel-1.772/tests/hostile_cases/r7fz_grammar_t11254_read_before_bind.py +119 -0
  378. code_towel-1.772/tests/hostile_cases/r7fz_grammar_u0217_read_before_bind.py +127 -0
  379. code_towel-1.772/tests/hostile_cases/r7fz_grammar_u0417_augassign_after_block.py +127 -0
  380. code_towel-1.772/tests/hostile_cases/r7fz_grammar_u0898_for_target_prebound.py +121 -0
  381. code_towel-1.772/tests/hostile_cases/r7fz_late_after_block_augassign.py +64 -0
  382. code_towel-1.772/tests/hostile_cases/r7fz_late_after_block_del.py +64 -0
  383. code_towel-1.772/tests/hostile_cases/r7fz_literals_cast_literal.py +61 -0
  384. code_towel-1.772/tests/hostile_cases/r7fz_literals_enum_functional.py +61 -0
  385. code_towel-1.772/tests/hostile_cases/r7fz_literals_gettext_differ.py +60 -0
  386. code_towel-1.772/tests/hostile_cases/r7fz_literals_typevar_name.py +60 -0
  387. code_towel-1.772/tests/hostile_cases/r7fz_misc_async_no_await.py +62 -0
  388. code_towel-1.772/tests/hostile_cases/r7fz_misc_binder_renamed_del_message.py +62 -0
  389. code_towel-1.772/tests/hostile_cases/r7fz_misc_binder_renamed_unbound_message_inner.py +59 -0
  390. code_towel-1.772/tests/hostile_cases/r7fz_misc_block_raises_in_handler_context.py +73 -0
  391. code_towel-1.772/tests/hostile_cases/r7fz_misc_decorated_lru_cache.py +63 -0
  392. code_towel-1.772/tests/hostile_cases/r7fz_misc_except_star_reraise.py +81 -0
  393. code_towel-1.772/tests/hostile_cases/r7fz_misc_generator_return_block.py +71 -0
  394. code_towel-1.772/tests/hostile_cases/r7fz_misc_mutable_default_host.py +56 -0
  395. code_towel-1.772/tests/hostile_cases/r7fz_misc_nested_host_called_early.py +62 -0
  396. code_towel-1.772/tests/hostile_cases/r7fz_misc_super_inside_block.py +80 -0
  397. code_towel-1.772/tests/hostile_cases/r7fz_prebound_class_conditional_rebind.py +67 -0
  398. code_towel-1.772/tests/hostile_cases/r7fz_prebound_def_conditional_rebind.py +67 -0
  399. code_towel-1.772/tests/hostile_cases/r7fz_prebound_for_else_prebound.py +67 -0
  400. code_towel-1.772/tests/hostile_cases/r7fz_prebound_for_read_in_block.py +62 -0
  401. code_towel-1.772/tests/hostile_cases/r7fz_prebound_for_target_attr_prebound.py +63 -0
  402. code_towel-1.772/tests/hostile_cases/r7fz_prebound_for_tuple_target.py +61 -0
  403. code_towel-1.772/tests/hostile_cases/r7fz_prebound_match_capture_prebound.py +67 -0
  404. code_towel-1.772/tests/hostile_cases/r7fz_prebound_try_except_else_bind.py +67 -0
  405. code_towel-1.772/tests/hostile_cases/r7fz_prebound_while_walrus_prebound.py +61 -0
  406. code_towel-1.772/tests/hostile_cases/r7fz_prebound_with_enter_raises_caught.py +93 -0
  407. code_towel-1.772/tests/hostile_cases/r7fz_srctext_bom.py +42 -0
  408. code_towel-1.772/tests/hostile_cases/r7fz_srctext_cp1252_quotes.py +44 -0
  409. code_towel-1.772/tests/hostile_cases/r7fz_srctext_crlf.py +42 -0
  410. code_towel-1.772/tests/hostile_cases/r7fz_srctext_form_feed.py +44 -0
  411. code_towel-1.772/tests/hostile_cases/r7fz_srctext_fstring_nested_quotes.py +59 -0
  412. code_towel-1.772/tests/hostile_cases/r7fz_srctext_latin1_literals.py +44 -0
  413. code_towel-1.772/tests/hostile_cases/r7fz_srctext_named_escape.py +56 -0
  414. code_towel-1.772/tests/hostile_cases/r7fz_srctext_semicolons_continuations.py +60 -0
  415. code_towel-1.772/tests/hostile_cases/r7fz_srctext_tabs_indent.py +57 -0
  416. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_dict_unhashable.py +115 -0
  417. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_dict_unpack.py +114 -0
  418. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_lambda_default.py +115 -0
  419. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_lambda_kwdefault.py +114 -0
  420. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_list_starred.py +114 -0
  421. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_plain_first.py +112 -0
  422. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_property_first.py +114 -0
  423. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_set_hash_effect.py +114 -0
  424. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_set_unhashable.py +115 -0
  425. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_tuple_starred.py +114 -0
  426. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_two_thunks_order.py +114 -0
  427. code_towel-1.772/tests/hostile_cases/r7fz_thunks2_undefined_global.py +120 -0
  428. code_towel-1.772/tests/hostile_cases/r7fz_thunks_call_arg_order.py +95 -0
  429. code_towel-1.772/tests/hostile_cases/r7fz_thunks_conditional.py +95 -0
  430. code_towel-1.772/tests/hostile_cases/r7fz_thunks_dict_order.py +91 -0
  431. code_towel-1.772/tests/hostile_cases/r7fz_thunks_lambda_default_first.py +93 -0
  432. code_towel-1.772/tests/hostile_cases/r7fz_thunks_match_guard.py +99 -0
  433. code_towel-1.772/tests/hostile_cases/r7fz_thunks_short_circuit_and.py +91 -0
  434. code_towel-1.772/tests/hostile_cases/r7sp_block_ends_before_a_semicolon.py +29 -0
  435. code_towel-1.772/tests/hostile_cases/r7sp_block_starts_after_a_semicolon.py +33 -0
  436. code_towel-1.772/tests/hostile_cases/r7sp_directive_on_a_shared_line.py +38 -0
  437. code_towel-1.772/tests/hostile_cases/r7sp_thunk_after_a_lambda_default.py +37 -0
  438. code_towel-1.772/tests/hostile_cases/r7sp_thunk_after_a_raise.py +38 -0
  439. code_towel-1.772/tests/hostile_cases/r7sp_thunk_after_a_set_display.py +44 -0
  440. code_towel-1.772/tests/hostile_cases/r7sp_thunk_after_a_starred_display.py +37 -0
  441. code_towel-1.772/tests/hostile_cases/r7sp_thunk_after_an_unbound_global_read.py +41 -0
  442. code_towel-1.772/tests/hostile_cases/r80_inlined_leading_thunk.py +33 -0
  443. code_towel-1.772/tests/hostile_cases/r81_conditionally_bound_free_variable.py +35 -0
  444. code_towel-1.772/tests/hostile_cases/r82_local_classes_same_name.py +34 -0
  445. code_towel-1.772/tests/hostile_cases/r83_tab_indented_class.py +34 -0
  446. code_towel-1.772/tests/hostile_cases/r84_warn_stacklevel.py +32 -0
  447. code_towel-1.772/tests/hostile_cases/r85_conditionally_bound_parameter.py +39 -0
  448. code_towel-1.772/tests/hostile_cases/r86_annotated_assignment_live.py +40 -0
  449. code_towel-1.772/tests/hostile_cases/r87_nested_function_in_method.py +36 -0
  450. code_towel-1.772/tests/hostile_cases/r88_elif_branch.py +39 -0
  451. code_towel-1.772/tests/hostile_cases/r89_conditionally_bound_return.py +42 -0
  452. code_towel-1.772/tests/hostile_cases/r90_cluster_across_classes.py +42 -0
  453. code_towel-1.772/tests/hostile_cases/r91_class_body_helper.py +37 -0
  454. code_towel-1.772/tests/hostile_cases/r92_parameterless_class_body_helper.py +39 -0
  455. code_towel-1.772/tests/hostile_cases/r93_import_name_differs.py +53 -0
  456. code_towel-1.772/tests/hostile_cases/r94_import_binds_live_name.py +26 -0
  457. code_towel-1.772/tests/hostile_cases/r95_type_checking_annotation.py +32 -0
  458. code_towel-1.772/tests/hostile_cases/r96_unpacked_targets_live.py +33 -0
  459. code_towel-1.772/tests/hostile_cases/r97_helper_in_function_with_outside_site.py +35 -0
  460. code_towel-1.772/tests/hostile_cases/r98_same_named_methods_nested_helpers.py +51 -0
  461. code_towel-1.772/tests/hostile_cases/r99_partial_return_branches.py +39 -0
  462. code_towel-1.772/tests/hostile_cases/r9bd_augmented_builtin_read_in_block.py +53 -0
  463. code_towel-1.772/tests/hostile_cases/r9bd_global_bound_by_every_binder.py +135 -0
  464. code_towel-1.772/tests/hostile_cases/r9bd_global_declared_at_one_site.py +40 -0
  465. code_towel-1.772/tests/hostile_cases/r9bd_only_binding_deleted_read_after.py +61 -0
  466. code_towel-1.772/tests/hostile_cases/r9bd_only_binding_every_binder.py +329 -0
  467. code_towel-1.772/tests/hostile_cases/r9bd_only_binding_kept_local.py +72 -0
  468. code_towel-1.772/tests/hostile_cases/r9bd_only_binding_read_before_block.py +76 -0
  469. code_towel-1.772/tests/hostile_cases/r9bd_only_binding_read_by_nested_scopes.py +109 -0
  470. code_towel-1.772/tests/hostile_cases/r9bd_type_alias_only_binding.pynew +48 -0
  471. code_towel-1.772/tests/hostile_cases/r9dc_stacked_decoration_by_hand.py +90 -0
  472. code_towel-1.772/tests/hostile_cases/r9dr_fmt_off_layout_moves_verbatim.py +90 -0
  473. code_towel-1.772/tests/hostile_cases/r9dr_region_directive_around_block.py +93 -0
  474. code_towel-1.772/tests/hostile_cases/r9sb_binding_target_loads.py +71 -0
  475. code_towel-1.772/tests/hostile_cases/r9sb_fstring_format_spec.py +44 -0
  476. code_towel-1.772/tests/hostile_cases/r9sb_lambda_parameter_capture.py +45 -0
  477. code_towel-1.772/tests/hostile_cases/r9sb_thunk_meets_inlined_comprehension.py +45 -0
  478. code_towel-1.772/tests/hostile_crossfile/r9dc_decorator_rebound_by_another_module/enable_checks.py +40 -0
  479. code_towel-1.772/tests/hostile_crossfile/r9dc_decorator_rebound_by_another_module/pkg/__init__.py +13 -0
  480. code_towel-1.772/tests/hostile_crossfile/r9dc_decorator_rebound_by_another_module/pkg/checks.py +17 -0
  481. code_towel-1.772/tests/hostile_crossfile/r9dc_decorator_rebound_by_another_module/pkg/core.py +36 -0
  482. code_towel-1.772/tests/hostile_crossfile/r9dc_decorator_rebound_by_another_module/run.py +18 -0
  483. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_binds_an_instrumenting_decorator/pkg/__init__.py +13 -0
  484. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_binds_an_instrumenting_decorator/pkg/common.py +40 -0
  485. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_binds_an_instrumenting_decorator/pkg/shapes.py +33 -0
  486. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_binds_an_instrumenting_decorator/run.py +17 -0
  487. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_of_a_module_part_way_through_a_cycle/pkg/__init__.py +13 -0
  488. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_of_a_module_part_way_through_a_cycle/pkg/common.py +46 -0
  489. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_of_a_module_part_way_through_a_cycle/pkg/shapes.py +33 -0
  490. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_of_a_module_part_way_through_a_cycle/run.py +18 -0
  491. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_that_cannot_bind_a_decorator/pkg/__init__.py +13 -0
  492. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_that_cannot_bind_a_decorator/pkg/common.py +19 -0
  493. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_that_cannot_bind_a_decorator/pkg/shapes.py +41 -0
  494. code_towel-1.772/tests/hostile_crossfile/r9dc_star_import_that_cannot_bind_a_decorator/run.py +17 -0
  495. code_towel-1.772/tests/hostile_crossfile/r9sb_lambda_capture_across_modules/pkg/__init__.py +13 -0
  496. code_towel-1.772/tests/hostile_crossfile/r9sb_lambda_capture_across_modules/pkg/orders.py +20 -0
  497. code_towel-1.772/tests/hostile_crossfile/r9sb_lambda_capture_across_modules/pkg/quotes.py +21 -0
  498. code_towel-1.772/tests/hostile_crossfile/r9sb_lambda_capture_across_modules/pyproject.toml +4 -0
  499. code_towel-1.772/tests/hostile_crossfile/r9sb_lambda_capture_across_modules/run.py +20 -0
  500. code_towel-1.772/tests/hostile_crossfile/r9sr_inline_snapshot/inline_snapshot.py +35 -0
  501. code_towel-1.772/tests/hostile_crossfile/r9sr_inline_snapshot/pkg/__init__.py +13 -0
  502. code_towel-1.772/tests/hostile_crossfile/r9sr_inline_snapshot/pkg/check_render.py +35 -0
  503. code_towel-1.772/tests/hostile_crossfile/r9sr_inline_snapshot/pytest.ini +3 -0
  504. code_towel-1.772/tests/hostile_crossfile/r9sr_inline_snapshot/run.py +36 -0
  505. code_towel-1.772/tests/hostile_crossfile/xf10_reuse_existing_function/pkg/__init__.py +13 -0
  506. code_towel-1.772/tests/hostile_crossfile/xf10_reuse_existing_function/pkg/a.py +20 -0
  507. code_towel-1.772/tests/hostile_crossfile/xf10_reuse_existing_function/pkg/b.py +26 -0
  508. code_towel-1.772/tests/hostile_crossfile/xf10_reuse_existing_function/run.py +17 -0
  509. code_towel-1.772/tests/hostile_crossfile/xf11_package_init_reaches_back/pkg/__init__.py +23 -0
  510. code_towel-1.772/tests/hostile_crossfile/xf11_package_init_reaches_back/pkg/detail.py +21 -0
  511. code_towel-1.772/tests/hostile_crossfile/xf11_package_init_reaches_back/run.py +17 -0
  512. code_towel-1.772/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/__init__.py +15 -0
  513. code_towel-1.772/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/core.py +17 -0
  514. code_towel-1.772/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/sub/__init__.py +16 -0
  515. code_towel-1.772/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/sub/leaf.py +20 -0
  516. code_towel-1.772/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/vendor/__init__.py +13 -0
  517. code_towel-1.772/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/vendor/tool.py +20 -0
  518. code_towel-1.772/tests/hostile_crossfile/xf12_cycle_through_package_init/run.py +18 -0
  519. code_towel-1.772/tests/hostile_crossfile/xf13_import_time_effects/pkg/__init__.py +13 -0
  520. code_towel-1.772/tests/hostile_crossfile/xf13_import_time_effects/pkg/a.py +22 -0
  521. code_towel-1.772/tests/hostile_crossfile/xf13_import_time_effects/pkg/b.py +24 -0
  522. code_towel-1.772/tests/hostile_crossfile/xf13_import_time_effects/run.py +18 -0
  523. code_towel-1.772/tests/hostile_crossfile/xf14_script_with_leading_statement/pkg/__init__.py +13 -0
  524. code_towel-1.772/tests/hostile_crossfile/xf14_script_with_leading_statement/pkg/app.py +23 -0
  525. code_towel-1.772/tests/hostile_crossfile/xf14_script_with_leading_statement/pkg/util.py +22 -0
  526. code_towel-1.772/tests/hostile_crossfile/xf14_script_with_leading_statement/run.py +17 -0
  527. code_towel-1.772/tests/hostile_crossfile/xf15_ancestor_in_another_module/pkg/__init__.py +13 -0
  528. code_towel-1.772/tests/hostile_crossfile/xf15_ancestor_in_another_module/pkg/base.py +18 -0
  529. code_towel-1.772/tests/hostile_crossfile/xf15_ancestor_in_another_module/pkg/servers.py +36 -0
  530. code_towel-1.772/tests/hostile_crossfile/xf15_ancestor_in_another_module/pkg/tokens.py +21 -0
  531. code_towel-1.772/tests/hostile_crossfile/xf15_ancestor_in_another_module/run.py +19 -0
  532. code_towel-1.772/tests/hostile_crossfile/xf16_consumer_outside_target_owns_helper_name/app/__init__.py +13 -0
  533. code_towel-1.772/tests/hostile_crossfile/xf16_consumer_outside_target_owns_helper_name/app/consumer.py +20 -0
  534. code_towel-1.772/tests/hostile_crossfile/xf16_consumer_outside_target_owns_helper_name/pkg/__init__.py +13 -0
  535. code_towel-1.772/tests/hostile_crossfile/xf16_consumer_outside_target_owns_helper_name/pkg/lib.py +40 -0
  536. code_towel-1.772/tests/hostile_crossfile/xf16_consumer_outside_target_owns_helper_name/run.py +18 -0
  537. code_towel-1.772/tests/hostile_crossfile/xf17_builtin_shadowed_in_borrower/pkg/__init__.py +13 -0
  538. code_towel-1.772/tests/hostile_crossfile/xf17_builtin_shadowed_in_borrower/pkg/a.py +21 -0
  539. code_towel-1.772/tests/hostile_crossfile/xf17_builtin_shadowed_in_borrower/pkg/b.py +29 -0
  540. code_towel-1.772/tests/hostile_crossfile/xf17_builtin_shadowed_in_borrower/run.py +18 -0
  541. code_towel-1.772/tests/hostile_crossfile/xf18_builtin_shadowed_by_star_import/pkg/__init__.py +13 -0
  542. code_towel-1.772/tests/hostile_crossfile/xf18_builtin_shadowed_by_star_import/pkg/a.py +20 -0
  543. code_towel-1.772/tests/hostile_crossfile/xf18_builtin_shadowed_by_star_import/pkg/b.py +26 -0
  544. code_towel-1.772/tests/hostile_crossfile/xf18_builtin_shadowed_by_star_import/pkg/util.py +16 -0
  545. code_towel-1.772/tests/hostile_crossfile/xf18_builtin_shadowed_by_star_import/run.py +18 -0
  546. code_towel-1.772/tests/hostile_crossfile/xf19_builtin_shadowed_by_borrower_local/pkg/__init__.py +13 -0
  547. code_towel-1.772/tests/hostile_crossfile/xf19_builtin_shadowed_by_borrower_local/pkg/a.py +20 -0
  548. code_towel-1.772/tests/hostile_crossfile/xf19_builtin_shadowed_by_borrower_local/pkg/b.py +22 -0
  549. code_towel-1.772/tests/hostile_crossfile/xf19_builtin_shadowed_by_borrower_local/run.py +18 -0
  550. code_towel-1.772/tests/hostile_crossfile/xf20_builtin_shadowed_in_ancestor_module/pkg/__init__.py +13 -0
  551. code_towel-1.772/tests/hostile_crossfile/xf20_builtin_shadowed_in_ancestor_module/pkg/base.py +24 -0
  552. code_towel-1.772/tests/hostile_crossfile/xf20_builtin_shadowed_in_ancestor_module/pkg/servers.py +31 -0
  553. code_towel-1.772/tests/hostile_crossfile/xf20_builtin_shadowed_in_ancestor_module/run.py +19 -0
  554. code_towel-1.772/tests/hostile_crossfile/xf21_builtin_shadowed_in_third_module/pkg/__init__.py +13 -0
  555. code_towel-1.772/tests/hostile_crossfile/xf21_builtin_shadowed_in_third_module/pkg/a.py +20 -0
  556. code_towel-1.772/tests/hostile_crossfile/xf21_builtin_shadowed_in_third_module/pkg/b.py +20 -0
  557. code_towel-1.772/tests/hostile_crossfile/xf21_builtin_shadowed_in_third_module/pkg/c.py +26 -0
  558. code_towel-1.772/tests/hostile_crossfile/xf21_builtin_shadowed_in_third_module/pkg/util.py +16 -0
  559. code_towel-1.772/tests/hostile_crossfile/xf21_builtin_shadowed_in_third_module/run.py +19 -0
  560. code_towel-1.772/tests/hostile_crossfile/xf22_borrower_rebinds_builtins_namespace/pkg/__init__.py +13 -0
  561. code_towel-1.772/tests/hostile_crossfile/xf22_borrower_rebinds_builtins_namespace/pkg/a.py +20 -0
  562. code_towel-1.772/tests/hostile_crossfile/xf22_borrower_rebinds_builtins_namespace/pkg/b.py +28 -0
  563. code_towel-1.772/tests/hostile_crossfile/xf22_borrower_rebinds_builtins_namespace/run.py +18 -0
  564. code_towel-1.772/tests/hostile_crossfile/xf23_relative_import_in_another_package/pkg/__init__.py +13 -0
  565. code_towel-1.772/tests/hostile_crossfile/xf23_relative_import_in_another_package/pkg/x/__init__.py +13 -0
  566. code_towel-1.772/tests/hostile_crossfile/xf23_relative_import_in_another_package/pkg/x/a.py +20 -0
  567. code_towel-1.772/tests/hostile_crossfile/xf23_relative_import_in_another_package/pkg/x/sub.py +15 -0
  568. code_towel-1.772/tests/hostile_crossfile/xf23_relative_import_in_another_package/pkg/y/__init__.py +13 -0
  569. code_towel-1.772/tests/hostile_crossfile/xf23_relative_import_in_another_package/pkg/y/b.py +22 -0
  570. code_towel-1.772/tests/hostile_crossfile/xf23_relative_import_in_another_package/pkg/y/sub.py +15 -0
  571. code_towel-1.772/tests/hostile_crossfile/xf23_relative_import_in_another_package/run.py +18 -0
  572. code_towel-1.772/tests/hostile_crossfile/xf24_relative_import_climbs_elsewhere/pkg/__init__.py +13 -0
  573. code_towel-1.772/tests/hostile_crossfile/xf24_relative_import_climbs_elsewhere/pkg/b.py +25 -0
  574. code_towel-1.772/tests/hostile_crossfile/xf24_relative_import_climbs_elsewhere/pkg/sub.py +15 -0
  575. code_towel-1.772/tests/hostile_crossfile/xf24_relative_import_climbs_elsewhere/pkg/x/__init__.py +13 -0
  576. code_towel-1.772/tests/hostile_crossfile/xf24_relative_import_climbs_elsewhere/pkg/x/a.py +23 -0
  577. code_towel-1.772/tests/hostile_crossfile/xf24_relative_import_climbs_elsewhere/run.py +18 -0
  578. code_towel-1.772/tests/hostile_crossfile/xf25_relative_import_in_the_same_package/pkg/__init__.py +13 -0
  579. code_towel-1.772/tests/hostile_crossfile/xf25_relative_import_in_the_same_package/pkg/x/__init__.py +13 -0
  580. code_towel-1.772/tests/hostile_crossfile/xf25_relative_import_in_the_same_package/pkg/x/a.py +20 -0
  581. code_towel-1.772/tests/hostile_crossfile/xf25_relative_import_in_the_same_package/pkg/x/b.py +21 -0
  582. code_towel-1.772/tests/hostile_crossfile/xf25_relative_import_in_the_same_package/pkg/x/sub.py +15 -0
  583. code_towel-1.772/tests/hostile_crossfile/xf25_relative_import_in_the_same_package/run.py +18 -0
  584. code_towel-1.772/tests/hostile_crossfile/xf26_relative_import_ancestor_in_another_package/pkg/__init__.py +13 -0
  585. code_towel-1.772/tests/hostile_crossfile/xf26_relative_import_ancestor_in_another_package/pkg/x/__init__.py +13 -0
  586. code_towel-1.772/tests/hostile_crossfile/xf26_relative_import_ancestor_in_another_package/pkg/x/base.py +17 -0
  587. code_towel-1.772/tests/hostile_crossfile/xf26_relative_import_ancestor_in_another_package/pkg/x/settings.py +15 -0
  588. code_towel-1.772/tests/hostile_crossfile/xf26_relative_import_ancestor_in_another_package/pkg/y/__init__.py +13 -0
  589. code_towel-1.772/tests/hostile_crossfile/xf26_relative_import_ancestor_in_another_package/pkg/y/servers.py +33 -0
  590. code_towel-1.772/tests/hostile_crossfile/xf26_relative_import_ancestor_in_another_package/pkg/y/settings.py +15 -0
  591. code_towel-1.772/tests/hostile_crossfile/xf26_relative_import_ancestor_in_another_package/run.py +19 -0
  592. code_towel-1.772/tests/hostile_crossfile/xf27_registration_decorator_in_host/pkg/__init__.py +13 -0
  593. code_towel-1.772/tests/hostile_crossfile/xf27_registration_decorator_in_host/pkg/alpha.py +30 -0
  594. code_towel-1.772/tests/hostile_crossfile/xf27_registration_decorator_in_host/pkg/beta.py +20 -0
  595. code_towel-1.772/tests/hostile_crossfile/xf27_registration_decorator_in_host/pkg/registry.py +20 -0
  596. code_towel-1.772/tests/hostile_crossfile/xf27_registration_decorator_in_host/run.py +24 -0
  597. code_towel-1.772/tests/hostile_crossfile/xf28_registration_decorator_in_reused_module/pkg/__init__.py +13 -0
  598. code_towel-1.772/tests/hostile_crossfile/xf28_registration_decorator_in_reused_module/pkg/alpha.py +29 -0
  599. code_towel-1.772/tests/hostile_crossfile/xf28_registration_decorator_in_reused_module/pkg/beta.py +19 -0
  600. code_towel-1.772/tests/hostile_crossfile/xf28_registration_decorator_in_reused_module/pkg/registry.py +20 -0
  601. code_towel-1.772/tests/hostile_crossfile/xf28_registration_decorator_in_reused_module/run.py +21 -0
  602. code_towel-1.772/tests/hostile_crossfile/xf29_type_checking_block_with_branches/pkg/__init__.py +13 -0
  603. code_towel-1.772/tests/hostile_crossfile/xf29_type_checking_block_with_branches/pkg/alpha.py +33 -0
  604. code_towel-1.772/tests/hostile_crossfile/xf29_type_checking_block_with_branches/pkg/beta.py +24 -0
  605. code_towel-1.772/tests/hostile_crossfile/xf29_type_checking_block_with_branches/run.py +18 -0
  606. code_towel-1.772/tests/hostile_crossfile/xf30_star_importer_takes_a_typing_name/pkg/__init__.py +13 -0
  607. code_towel-1.772/tests/hostile_crossfile/xf30_star_importer_takes_a_typing_name/pkg/a.py +39 -0
  608. code_towel-1.772/tests/hostile_crossfile/xf30_star_importer_takes_a_typing_name/pkg/b.py +35 -0
  609. code_towel-1.772/tests/hostile_crossfile/xf30_star_importer_takes_a_typing_name/run.py +19 -0
  610. code_towel-1.772/tests/hostile_crossfile/xf31_package_init_star_imports_a_matcher_named_any/pkg/__init__.py +17 -0
  611. code_towel-1.772/tests/hostile_crossfile/xf31_package_init_star_imports_a_matcher_named_any/pkg/matchers.py +22 -0
  612. code_towel-1.772/tests/hostile_crossfile/xf31_package_init_star_imports_a_matcher_named_any/pkg/report.py +36 -0
  613. code_towel-1.772/tests/hostile_crossfile/xf31_package_init_star_imports_a_matcher_named_any/run.py +19 -0
  614. code_towel-1.772/tests/hostile_crossfile/xf32_star_importer_outside_the_target/consumer.py +26 -0
  615. code_towel-1.772/tests/hostile_crossfile/xf32_star_importer_outside_the_target/pkg/__init__.py +13 -0
  616. code_towel-1.772/tests/hostile_crossfile/xf32_star_importer_outside_the_target/pkg/report.py +36 -0
  617. code_towel-1.772/tests/hostile_crossfile/xf32_star_importer_outside_the_target/run.py +17 -0
  618. code_towel-1.772/tests/hostile_crossfile/xf3_same_named_local_function/pkg/__init__.py +13 -0
  619. code_towel-1.772/tests/hostile_crossfile/xf3_same_named_local_function/pkg/a.py +22 -0
  620. code_towel-1.772/tests/hostile_crossfile/xf3_same_named_local_function/pkg/b.py +22 -0
  621. code_towel-1.772/tests/hostile_crossfile/xf3_same_named_local_function/run.py +17 -0
  622. code_towel-1.772/tests/hostile_crossfile/xf4_same_named_class/pkg/__init__.py +13 -0
  623. code_towel-1.772/tests/hostile_crossfile/xf4_same_named_class/pkg/a.py +23 -0
  624. code_towel-1.772/tests/hostile_crossfile/xf4_same_named_class/pkg/b.py +23 -0
  625. code_towel-1.772/tests/hostile_crossfile/xf4_same_named_class/run.py +17 -0
  626. code_towel-1.772/tests/hostile_crossfile/xf5_dunder_file/pkg/__init__.py +13 -0
  627. code_towel-1.772/tests/hostile_crossfile/xf5_dunder_file/pkg/a.py +21 -0
  628. code_towel-1.772/tests/hostile_crossfile/xf5_dunder_file/pkg/b.py +21 -0
  629. code_towel-1.772/tests/hostile_crossfile/xf5_dunder_file/run.py +17 -0
  630. code_towel-1.772/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/__init__.py +13 -0
  631. code_towel-1.772/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/a.py +21 -0
  632. code_towel-1.772/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/b.py +21 -0
  633. code_towel-1.772/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/util1.py +15 -0
  634. code_towel-1.772/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/util2.py +15 -0
  635. code_towel-1.772/tests/hostile_crossfile/xf6_same_alias_different_import/run.py +17 -0
  636. code_towel-1.772/tests/hostile_crossfile/xf7_docstring_import/pkg/__init__.py +13 -0
  637. code_towel-1.772/tests/hostile_crossfile/xf7_docstring_import/pkg/a.py +28 -0
  638. code_towel-1.772/tests/hostile_crossfile/xf7_docstring_import/pkg/b.py +27 -0
  639. code_towel-1.772/tests/hostile_crossfile/xf7_docstring_import/run.py +17 -0
  640. code_towel-1.772/tests/hostile_crossfile/xf7c_rebinding_enclosing_function_beside_a_twin_in_another_module/pkg/__init__.py +13 -0
  641. code_towel-1.772/tests/hostile_crossfile/xf7c_rebinding_enclosing_function_beside_a_twin_in_another_module/pkg/a.py +32 -0
  642. code_towel-1.772/tests/hostile_crossfile/xf7c_rebinding_enclosing_function_beside_a_twin_in_another_module/pkg/b.py +37 -0
  643. code_towel-1.772/tests/hostile_crossfile/xf7c_rebinding_enclosing_function_beside_a_twin_in_another_module/run.py +19 -0
  644. code_towel-1.772/tests/hostile_crossfile/xf7d_assert_moves_to_a_module_pytest_does_not_rewrite/pkg/__init__.py +13 -0
  645. code_towel-1.772/tests/hostile_crossfile/xf7d_assert_moves_to_a_module_pytest_does_not_rewrite/pkg/check_values.py +27 -0
  646. code_towel-1.772/tests/hostile_crossfile/xf7d_assert_moves_to_a_module_pytest_does_not_rewrite/pkg/checks.py +19 -0
  647. code_towel-1.772/tests/hostile_crossfile/xf7d_assert_moves_to_a_module_pytest_does_not_rewrite/pytest.ini +3 -0
  648. code_towel-1.772/tests/hostile_crossfile/xf7d_assert_moves_to_a_module_pytest_does_not_rewrite/run.py +43 -0
  649. code_towel-1.772/tests/hostile_crossfile/xf7d_asserts_shared_by_modules_rewritten_alike/pkg/__init__.py +13 -0
  650. code_towel-1.772/tests/hostile_crossfile/xf7d_asserts_shared_by_modules_rewritten_alike/pkg/alpha.py +20 -0
  651. code_towel-1.772/tests/hostile_crossfile/xf7d_asserts_shared_by_modules_rewritten_alike/pkg/beta.py +23 -0
  652. code_towel-1.772/tests/hostile_crossfile/xf7d_asserts_shared_by_modules_rewritten_alike/pkg/check_suite.py +24 -0
  653. code_towel-1.772/tests/hostile_crossfile/xf7d_asserts_shared_by_modules_rewritten_alike/pytest.ini +3 -0
  654. code_towel-1.772/tests/hostile_crossfile/xf7d_asserts_shared_by_modules_rewritten_alike/run.py +42 -0
  655. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_class_level_name/pkg/__init__.py +13 -0
  656. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_class_level_name/pkg/a.py +23 -0
  657. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_class_level_name/pkg/b.py +30 -0
  658. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_class_level_name/pyproject.toml +4 -0
  659. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_class_level_name/run.py +44 -0
  660. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_dunder_file/pkg/__init__.py +13 -0
  661. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_dunder_file/pkg/a.py +24 -0
  662. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_dunder_file/pkg/b.py +30 -0
  663. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_dunder_file/pyproject.toml +4 -0
  664. code_towel-1.772/tests/hostile_crossfile/xf7fz_binding_x_dunder_file/run.py +44 -0
  665. code_towel-1.772/tests/hostile_crossfile/xf7fz_builtins_x_shadow_in_b/pkg/__init__.py +13 -0
  666. code_towel-1.772/tests/hostile_crossfile/xf7fz_builtins_x_shadow_in_b/pkg/a.py +22 -0
  667. code_towel-1.772/tests/hostile_crossfile/xf7fz_builtins_x_shadow_in_b/pkg/b.py +29 -0
  668. code_towel-1.772/tests/hostile_crossfile/xf7fz_builtins_x_shadow_in_b/pyproject.toml +4 -0
  669. code_towel-1.772/tests/hostile_crossfile/xf7fz_builtins_x_shadow_in_b/run.py +44 -0
  670. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_binder_message_mypy/pkg/__init__.py +13 -0
  671. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_binder_message_mypy/pkg/m.py +30 -0
  672. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_binder_message_mypy/pyproject.toml +7 -0
  673. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_binder_message_mypy/run.py +43 -0
  674. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_binder_message_pyright/pkg/__init__.py +13 -0
  675. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_binder_message_pyright/pkg/m.py +30 -0
  676. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_binder_message_pyright/pyproject.toml +7 -0
  677. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_binder_message_pyright/run.py +43 -0
  678. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_read_before_bind_mypy/pkg/__init__.py +13 -0
  679. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_read_before_bind_mypy/pkg/m.py +30 -0
  680. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_read_before_bind_mypy/pyproject.toml +7 -0
  681. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_read_before_bind_mypy/run.py +45 -0
  682. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_read_before_bind_pyright/pkg/__init__.py +13 -0
  683. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_read_before_bind_pyright/pkg/m.py +30 -0
  684. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_read_before_bind_pyright/pyproject.toml +7 -0
  685. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_read_before_bind_pyright/run.py +45 -0
  686. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_thunk_lambda_default_mypy/pkg/__init__.py +13 -0
  687. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_thunk_lambda_default_mypy/pkg/m.py +49 -0
  688. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_thunk_lambda_default_mypy/pyproject.toml +7 -0
  689. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_thunk_lambda_default_mypy/run.py +44 -0
  690. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_thunk_lambda_default_pyright/pkg/__init__.py +13 -0
  691. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_thunk_lambda_default_pyright/pkg/m.py +49 -0
  692. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_thunk_lambda_default_pyright/pyproject.toml +7 -0
  693. code_towel-1.772/tests/hostile_crossfile/xf7fz_extra_typed_thunk_lambda_default_pyright/run.py +44 -0
  694. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_t11094_semicolon/pkg/__init__.py +13 -0
  695. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_t11094_semicolon/pkg/m.py +102 -0
  696. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_t11094_semicolon/pyproject.toml +7 -0
  697. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_t11094_semicolon/run.py +47 -0
  698. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_t11271_semicolon/pkg/__init__.py +13 -0
  699. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_t11271_semicolon/pkg/m.py +110 -0
  700. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_t11271_semicolon/pyproject.toml +10 -0
  701. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_t11271_semicolon/run.py +47 -0
  702. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0474_for_target_prebound/pkg/__init__.py +13 -0
  703. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0474_for_target_prebound/pkg/a.py +26 -0
  704. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0474_for_target_prebound/pkg/b.py +27 -0
  705. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0474_for_target_prebound/pkg/common.py +52 -0
  706. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0474_for_target_prebound/pyproject.toml +4 -0
  707. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0474_for_target_prebound/run.py +53 -0
  708. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0624_for_target_prebound/pkg/__init__.py +13 -0
  709. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0624_for_target_prebound/pkg/a.py +33 -0
  710. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0624_for_target_prebound/pkg/b.py +34 -0
  711. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0624_for_target_prebound/pkg/common.py +52 -0
  712. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0624_for_target_prebound/pyproject.toml +4 -0
  713. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u0624_for_target_prebound/run.py +53 -0
  714. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u1209_for_target_prebound/pkg/__init__.py +13 -0
  715. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u1209_for_target_prebound/pkg/a.py +33 -0
  716. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u1209_for_target_prebound/pkg/b.py +34 -0
  717. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u1209_for_target_prebound/pkg/common.py +52 -0
  718. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u1209_for_target_prebound/pyproject.toml +4 -0
  719. code_towel-1.772/tests/hostile_crossfile/xf7fz_grammar_u1209_for_target_prebound/run.py +53 -0
  720. code_towel-1.772/tests/hostile_crossfile/xf7fz_late_toplevel_module_in_package/pkg/__init__.py +13 -0
  721. code_towel-1.772/tests/hostile_crossfile/xf7fz_late_toplevel_module_in_package/pkg/a.py +20 -0
  722. code_towel-1.772/tests/hostile_crossfile/xf7fz_late_toplevel_module_in_package/pkg/c.py +23 -0
  723. code_towel-1.772/tests/hostile_crossfile/xf7fz_late_toplevel_module_in_package/pkg/helpers_top.py +15 -0
  724. code_towel-1.772/tests/hostile_crossfile/xf7fz_late_toplevel_module_in_package/pyproject.toml +4 -0
  725. code_towel-1.772/tests/hostile_crossfile/xf7fz_late_toplevel_module_in_package/run.py +50 -0
  726. code_towel-1.772/tests/hostile_crossfile/xf7fz_late_toplevel_module_in_package/tests/__init__.py +13 -0
  727. code_towel-1.772/tests/hostile_crossfile/xf7fz_late_toplevel_module_in_package/tests/test_x.py +15 -0
  728. code_towel-1.772/tests/hostile_crossfile/xf7fz_misc_typed_for_prebound_mypy/pkg/__init__.py +13 -0
  729. code_towel-1.772/tests/hostile_crossfile/xf7fz_misc_typed_for_prebound_mypy/pkg/m.py +30 -0
  730. code_towel-1.772/tests/hostile_crossfile/xf7fz_misc_typed_for_prebound_mypy/pyproject.toml +7 -0
  731. code_towel-1.772/tests/hostile_crossfile/xf7fz_misc_typed_for_prebound_mypy/run.py +43 -0
  732. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_b_imports_a/pkg/__init__.py +13 -0
  733. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_b_imports_a/pkg/a.py +20 -0
  734. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_b_imports_a/pkg/b.py +23 -0
  735. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_b_imports_a/pyproject.toml +4 -0
  736. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_b_imports_a/run.py +56 -0
  737. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_init_hosts/pkg/__init__.py +20 -0
  738. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_init_hosts/pkg/a.py +23 -0
  739. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_init_hosts/pyproject.toml +4 -0
  740. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_init_hosts/run.py +56 -0
  741. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_rel_import_in_block/pkg/__init__.py +13 -0
  742. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_rel_import_in_block/pkg/a.py +21 -0
  743. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_rel_import_in_block/pkg/b.py +24 -0
  744. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_rel_import_in_block/pkg/conf.py +15 -0
  745. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_rel_import_in_block/pyproject.toml +4 -0
  746. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_rel_import_in_block/run.py +57 -0
  747. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_script_main_guard/pkg/__init__.py +13 -0
  748. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_script_main_guard/pkg/a.py +20 -0
  749. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_script_main_guard/pkg/b.py +27 -0
  750. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_script_main_guard/pyproject.toml +4 -0
  751. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_script_main_guard/run.py +68 -0
  752. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_three_modules_cluster/pkg/__init__.py +13 -0
  753. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_three_modules_cluster/pkg/a.py +20 -0
  754. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_three_modules_cluster/pkg/b.py +23 -0
  755. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_three_modules_cluster/pkg/c.py +23 -0
  756. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_three_modules_cluster/pyproject.toml +4 -0
  757. code_towel-1.772/tests/hostile_crossfile/xf7fz_modules_three_modules_cluster/run.py +64 -0
  758. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/pkg/__init__.py +13 -0
  759. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/pkg/compat.py +16 -0
  760. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/pkg/compat_false.py +15 -0
  761. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/pkg/f.py +26 -0
  762. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/pkg/m.py +42 -0
  763. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/pkg/w.py +23 -0
  764. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/pyproject.toml +10 -0
  765. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/run.py +47 -0
  766. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/tests/__init__.py +13 -0
  767. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_both/tests/test_w.py +20 -0
  768. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/pkg/__init__.py +13 -0
  769. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/pkg/compat.py +16 -0
  770. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/pkg/compat_false.py +15 -0
  771. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/pkg/f.py +26 -0
  772. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/pkg/m.py +41 -0
  773. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/pkg/w.py +23 -0
  774. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/pyproject.toml +10 -0
  775. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/run.py +47 -0
  776. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/tests/__init__.py +13 -0
  777. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_both/tests/test_w.py +20 -0
  778. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/pkg/__init__.py +13 -0
  779. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/pkg/compat.py +16 -0
  780. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/pkg/compat_false.py +15 -0
  781. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/pkg/f.py +26 -0
  782. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/pkg/m.py +41 -0
  783. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/pkg/w.py +23 -0
  784. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/pyproject.toml +7 -0
  785. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/run.py +47 -0
  786. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/tests/__init__.py +13 -0
  787. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_late_use_mypy/tests/test_w.py +20 -0
  788. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/pkg/__init__.py +13 -0
  789. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/pkg/compat.py +16 -0
  790. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/pkg/compat_false.py +15 -0
  791. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/pkg/f.py +26 -0
  792. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/pkg/m.py +42 -0
  793. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/pkg/w.py +23 -0
  794. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/pyproject.toml +7 -0
  795. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/run.py +47 -0
  796. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/tests/__init__.py +13 -0
  797. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_from_compat_true_mypy/tests/test_w.py +20 -0
  798. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/pkg/__init__.py +13 -0
  799. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/pkg/compat.py +16 -0
  800. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/pkg/compat_false.py +15 -0
  801. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/pkg/f.py +26 -0
  802. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/pkg/m.py +41 -0
  803. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/pkg/w.py +23 -0
  804. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/pyproject.toml +10 -0
  805. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/run.py +47 -0
  806. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/tests/__init__.py +13 -0
  807. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_both/tests/test_w.py +20 -0
  808. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/pkg/__init__.py +13 -0
  809. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/pkg/compat.py +16 -0
  810. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/pkg/compat_false.py +15 -0
  811. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/pkg/f.py +26 -0
  812. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/pkg/m.py +41 -0
  813. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/pkg/w.py +23 -0
  814. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/pyproject.toml +7 -0
  815. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/run.py +47 -0
  816. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/tests/__init__.py +13 -0
  817. code_towel-1.772/tests/hostile_crossfile/xf7fz_typeguard2_import_alias_flag_mypy/tests/test_w.py +20 -0
  818. code_towel-1.772/tests/hostile_crossfile/xf7n_host_the_wheel_leaves_out/pkg/pyproject.toml +11 -0
  819. code_towel-1.772/tests/hostile_crossfile/xf7n_host_the_wheel_leaves_out/pkg/src/shop/__init__.py +15 -0
  820. code_towel-1.772/tests/hostile_crossfile/xf7n_host_the_wheel_leaves_out/pkg/src/shop/_devtools.py +22 -0
  821. code_towel-1.772/tests/hostile_crossfile/xf7n_host_the_wheel_leaves_out/pkg/src/shop/stats.py +19 -0
  822. code_towel-1.772/tests/hostile_crossfile/xf7n_host_the_wheel_leaves_out/run.py +38 -0
  823. code_towel-1.772/tests/hostile_crossfile/xf7n_namesake_of_a_required_library/pkg/pyproject.toml +7 -0
  824. code_towel-1.772/tests/hostile_crossfile/xf7n_namesake_of_a_required_library/pkg/zzapp/__init__.py +13 -0
  825. code_towel-1.772/tests/hostile_crossfile/xf7n_namesake_of_a_required_library/pkg/zzapp/core.py +25 -0
  826. code_towel-1.772/tests/hostile_crossfile/xf7n_namesake_of_a_required_library/pkg/zzlib/__init__.py +13 -0
  827. code_towel-1.772/tests/hostile_crossfile/xf7n_namesake_of_a_required_library/pkg/zzlib/utils.py +25 -0
  828. code_towel-1.772/tests/hostile_crossfile/xf7n_namesake_of_a_required_library/run.py +27 -0
  829. code_towel-1.772/tests/hostile_crossfile/xf7n_namesake_of_a_required_library/site-packages/zzlib/__init__.py +13 -0
  830. code_towel-1.772/tests/hostile_crossfile/xf7n_namesake_of_a_required_library/site-packages/zzlib/utils.py +17 -0
  831. code_towel-1.772/tests/hostile_crossfile/xf7n_subpackage_the_wheel_leaves_out/pkg/pyproject.toml +11 -0
  832. code_towel-1.772/tests/hostile_crossfile/xf7n_subpackage_the_wheel_leaves_out/pkg/src/shop/__init__.py +15 -0
  833. code_towel-1.772/tests/hostile_crossfile/xf7n_subpackage_the_wheel_leaves_out/pkg/src/shop/cli.py +21 -0
  834. code_towel-1.772/tests/hostile_crossfile/xf7n_subpackage_the_wheel_leaves_out/pkg/src/shop/devtools/__init__.py +13 -0
  835. code_towel-1.772/tests/hostile_crossfile/xf7n_subpackage_the_wheel_leaves_out/pkg/src/shop/devtools/dump.py +22 -0
  836. code_towel-1.772/tests/hostile_crossfile/xf7n_subpackage_the_wheel_leaves_out/pkg/src/shop/stats.py +19 -0
  837. code_towel-1.772/tests/hostile_crossfile/xf7n_subpackage_the_wheel_leaves_out/run.py +39 -0
  838. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pkg/__init__.py +13 -0
  839. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pkg/alpha.py +17 -0
  840. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pkg/app.py +36 -0
  841. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pkg/beta.py +17 -0
  842. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pkg/gamma.py +17 -0
  843. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pkg/loader.py +36 -0
  844. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pkg/registry.py +19 -0
  845. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pkg/zeta.py +17 -0
  846. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/pyproject.toml +13 -0
  847. code_towel-1.772/tests/hostile_crossfile/xf7t_import_order_is_registration_order/run.py +21 -0
  848. code_towel-1.772/tests/hostile_crossfile/xf8_helper_name_collision/pkg/__init__.py +13 -0
  849. code_towel-1.772/tests/hostile_crossfile/xf8_helper_name_collision/pkg/a.py +20 -0
  850. code_towel-1.772/tests/hostile_crossfile/xf8_helper_name_collision/pkg/b.py +20 -0
  851. code_towel-1.772/tests/hostile_crossfile/xf8_helper_name_collision/pkg/c.py +20 -0
  852. code_towel-1.772/tests/hostile_crossfile/xf8_helper_name_collision/pkg/d.py +20 -0
  853. code_towel-1.772/tests/hostile_crossfile/xf8_helper_name_collision/pkg/e.py +26 -0
  854. code_towel-1.772/tests/hostile_crossfile/xf8_helper_name_collision/run.py +20 -0
  855. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/__init__.py +13 -0
  856. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/__init__.py +13 -0
  857. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/base.py +17 -0
  858. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/introspect.py +24 -0
  859. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/revoke.py +24 -0
  860. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/__init__.py +13 -0
  861. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/access.py +24 -0
  862. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/base.py +17 -0
  863. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/request.py +24 -0
  864. code_towel-1.772/tests/hostile_crossfile/xf9_same_named_base_class/run.py +22 -0
  865. code_towel-1.772/tests/hostile_crossfile/xf9bd_only_binding_across_modules/pkg/__init__.py +13 -0
  866. code_towel-1.772/tests/hostile_crossfile/xf9bd_only_binding_across_modules/pkg/a.py +28 -0
  867. code_towel-1.772/tests/hostile_crossfile/xf9bd_only_binding_across_modules/pkg/b.py +24 -0
  868. code_towel-1.772/tests/hostile_crossfile/xf9bd_only_binding_across_modules/run.py +23 -0
  869. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_hides_hand_decoration/pkg/__init__.py +13 -0
  870. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_hides_hand_decoration/pkg/fast.pynew +22 -0
  871. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_hides_hand_decoration/pkg/instrument.py +24 -0
  872. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_hides_hand_decoration/pkg/kernels.py +34 -0
  873. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_hides_hand_decoration/pyproject.toml +4 -0
  874. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_hides_hand_decoration/run.py +23 -0
  875. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_test_hides_builtin_patch/pkg/__init__.py +13 -0
  876. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_test_hides_builtin_patch/pkg/a.py +26 -0
  877. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_test_hides_builtin_patch/pkg/b.py +20 -0
  878. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_test_hides_builtin_patch/pyproject.toml +4 -0
  879. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_test_hides_builtin_patch/run.py +25 -0
  880. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_test_hides_builtin_patch/tests/__init__.py +13 -0
  881. code_towel-1.772/tests/hostile_crossfile/xf9up_newer_syntax_test_hides_builtin_patch/tests/test_b.pynew +32 -0
  882. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/pkg/pyproject.toml +8 -0
  883. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/pkg/third_party/zzlib/utils.py +25 -0
  884. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/pkg/zzapp/__init__.py +13 -0
  885. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/pkg/zzapp/cli.py +22 -0
  886. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/pkg/zzapp/core.py +25 -0
  887. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/run.py +27 -0
  888. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/site-packages/zzlib/__init__.py +13 -0
  889. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/site-packages/zzlib/core.py +16 -0
  890. code_towel-1.772/tests/hostile_crossfile/xf9xi_namesake_lacking_a_module/site-packages/zzlib/utils.py +16 -0
  891. code_towel-1.772/tests/hostile_crossfile/xf9xi_type_only_import_of_a_missing_module/pkg/__init__.py +13 -0
  892. code_towel-1.772/tests/hostile_crossfile/xf9xi_type_only_import_of_a_missing_module/pkg/a.py +28 -0
  893. code_towel-1.772/tests/hostile_crossfile/xf9xi_type_only_import_of_a_missing_module/pkg/b.py +24 -0
  894. code_towel-1.772/tests/hostile_crossfile/xf9xi_type_only_import_of_a_missing_module/run.py +18 -0
  895. code_towel-1.772/tests/hostile_execution.py +343 -0
  896. code_towel-1.772/tests/hostile_refactoring.py +131 -0
  897. code_towel-1.772/tests/probe_answers.py +54 -0
  898. code_towel-1.772/tests/run_tests.py +45 -0
  899. code_towel-1.772/tests/test_adversarial_renaming.py +342 -0
  900. code_towel-1.772/tests/test_adversarial_semantics.py +387 -0
  901. code_towel-1.772/tests/test_analysis_edge_paths.py +177 -0
  902. code_towel-1.772/tests/test_analysis_sessions.py +278 -0
  903. code_towel-1.772/tests/test_annotation_imports.py +510 -0
  904. code_towel-1.772/tests/test_annotation_ladder.py +164 -0
  905. code_towel-1.772/tests/test_annotation_ladder_rules.py +437 -0
  906. code_towel-1.772/tests/test_annotation_runtime_effects.py +245 -0
  907. code_towel-1.772/tests/test_annotations_for_the_oldest_python.py +259 -0
  908. code_towel-1.772/tests/test_assert_rewriting.py +239 -0
  909. code_towel-1.772/tests/test_assignment_analyzer_comprehensive.py +693 -0
  910. code_towel-1.772/tests/test_attribute_declaration_order.py +91 -0
  911. code_towel-1.772/tests/test_audit_io_safety.py +358 -0
  912. code_towel-1.772/tests/test_baseline_shapes.py +438 -0
  913. code_towel-1.772/tests/test_binding_and_read_collectors.py +474 -0
  914. code_towel-1.772/tests/test_binding_collectors_match_cpython.py +955 -0
  915. code_towel-1.772/tests/test_binding_context_memo.py +72 -0
  916. code_towel-1.772/tests/test_binding_detector.py +302 -0
  917. code_towel-1.772/tests/test_binding_detector_edge_cases.py +392 -0
  918. code_towel-1.772/tests/test_bindings.py +271 -0
  919. code_towel-1.772/tests/test_bindings_additional.py +110 -0
  920. code_towel-1.772/tests/test_block_comments.py +652 -0
  921. code_towel-1.772/tests/test_block_site_caches.py +213 -0
  922. code_towel-1.772/tests/test_breakers.py +133 -0
  923. code_towel-1.772/tests/test_builtins_across_modules.py +588 -0
  924. code_towel-1.772/tests/test_builtins_comprehensive.py +412 -0
  925. code_towel-1.772/tests/test_cache_lifetimes.py +86 -0
  926. code_towel-1.772/tests/test_candidate_index.py +223 -0
  927. code_towel-1.772/tests/test_canonical_ast.py +353 -0
  928. code_towel-1.772/tests/test_change_transactions.py +321 -0
  929. code_towel-1.772/tests/test_checker_failure_reporting.py +182 -0
  930. code_towel-1.772/tests/test_checker_reports.py +142 -0
  931. code_towel-1.772/tests/test_checker_snapshot_follows_project.py +160 -0
  932. code_towel-1.772/tests/test_class_object_annotations.py +146 -0
  933. code_towel-1.772/tests/test_class_private_helpers.py +579 -0
  934. code_towel-1.772/tests/test_class_private_positions.py +300 -0
  935. code_towel-1.772/tests/test_cli_dispatch_paths.py +105 -0
  936. code_towel-1.772/tests/test_cli_input_errors.py +100 -0
  937. code_towel-1.772/tests/test_cli_integration.py +316 -0
  938. code_towel-1.772/tests/test_cli_oracle_lifecycle.py +117 -0
  939. code_towel-1.772/tests/test_cli_output_safety.py +65 -0
  940. code_towel-1.772/tests/test_cold_confirmation.py +321 -0
  941. code_towel-1.772/tests/test_complete_check_scope.py +468 -0
  942. code_towel-1.772/tests/test_comprehensive_coverage.py +307 -0
  943. code_towel-1.772/tests/test_configured_checker_must_run.py +148 -0
  944. code_towel-1.772/tests/test_consumer_reach.py +250 -0
  945. code_towel-1.772/tests/test_copy_preservation.py +52 -0
  946. code_towel-1.772/tests/test_coverage_config.py +254 -0
  947. code_towel-1.772/tests/test_created_object_escapes.py +172 -0
  948. code_towel-1.772/tests/test_cross_distribution.py +134 -0
  949. code_towel-1.772/tests/test_cross_module_opt_in.py +200 -0
  950. code_towel-1.772/tests/test_crossfile_integration.py +470 -0
  951. code_towel-1.772/tests/test_crossfile_observational_equivalence.py +42 -0
  952. code_towel-1.772/tests/test_decline_traces.py +260 -0
  953. code_towel-1.772/tests/test_declined_run_reports.py +269 -0
  954. code_towel-1.772/tests/test_decorator_reach.py +1515 -0
  955. code_towel-1.772/tests/test_deeply_nested_source.py +58 -0
  956. code_towel-1.772/tests/test_definite_assignment.py +236 -0
  957. code_towel-1.772/tests/test_definite_assignment_runtime.py +110 -0
  958. code_towel-1.772/tests/test_dependency_audit_export.py +95 -0
  959. code_towel-1.772/tests/test_differential_baseline.py +922 -0
  960. code_towel-1.772/tests/test_differential_grammar.py +207 -0
  961. code_towel-1.772/tests/test_differential_scope_family.py +55 -0
  962. code_towel-1.772/tests/test_distinct_proposals.py +102 -0
  963. code_towel-1.772/tests/test_ecosystem_environment.py +764 -0
  964. code_towel-1.772/tests/test_ecosystem_manifest_regressions.py +103 -0
  965. code_towel-1.772/tests/test_ecosystem_tool_pins.py +280 -0
  966. code_towel-1.772/tests/test_ecosystem_typing_deps.py +402 -0
  967. code_towel-1.772/tests/test_ecosystem_verdicts.py +1517 -0
  968. code_towel-1.772/tests/test_ecosystem_work_isolation.py +140 -0
  969. code_towel-1.772/tests/test_effect_free.py +360 -0
  970. code_towel-1.772/tests/test_engine_adversarial.py +1358 -0
  971. code_towel-1.772/tests/test_engine_failure_visibility.py +48 -0
  972. code_towel-1.772/tests/test_environment_independence.py +377 -0
  973. code_towel-1.772/tests/test_environment_inside_the_root.py +283 -0
  974. code_towel-1.772/tests/test_equivalence_harness_regressions.py +175 -0
  975. code_towel-1.772/tests/test_equivalence_target_selection.py +172 -0
  976. code_towel-1.772/tests/test_error_paths.py +292 -0
  977. code_towel-1.772/tests/test_exceptions.py +30 -0
  978. code_towel-1.772/tests/test_extractor_additional_branches.py +130 -0
  979. code_towel-1.772/tests/test_extractor_augassign_and_fstrings.py +126 -0
  980. code_towel-1.772/tests/test_extractor_callee_and_multi_return.py +137 -0
  981. code_towel-1.772/tests/test_extractor_comprehensive.py +569 -0
  982. code_towel-1.772/tests/test_extractor_edge_cases.py +369 -0
  983. code_towel-1.772/tests/test_extractor_final_lines.py +165 -0
  984. code_towel-1.772/tests/test_extractor_generate_call_variants.py +96 -0
  985. code_towel-1.772/tests/test_extractor_more_paths.py +137 -0
  986. code_towel-1.772/tests/test_extractor_preamble_and_call_mapping.py +151 -0
  987. code_towel-1.772/tests/test_extractor_remaining_branches.py +273 -0
  988. code_towel-1.772/tests/test_extractor_return_statements.py +341 -0
  989. code_towel-1.772/tests/test_extractor_substituter_and_helpers.py +425 -0
  990. code_towel-1.772/tests/test_file_under_two_names.py +308 -0
  991. code_towel-1.772/tests/test_filesystem_guards.py +87 -0
  992. code_towel-1.772/tests/test_final_coverage_push.py +262 -0
  993. code_towel-1.772/tests/test_fixed_point_refusals.py +161 -0
  994. code_towel-1.772/tests/test_forwarder_chains.py +635 -0
  995. code_towel-1.772/tests/test_frame_sensitivity_scan.py +83 -0
  996. code_towel-1.772/tests/test_free_variable_correspondence.py +285 -0
  997. code_towel-1.772/tests/test_fstrings.py +188 -0
  998. code_towel-1.772/tests/test_fully_annotated_modules.py +80 -0
  999. code_towel-1.772/tests/test_function_facts_equivalence.py +93 -0
  1000. code_towel-1.772/tests/test_function_index.py +90 -0
  1001. code_towel-1.772/tests/test_functions_keep_their_own_bodies.py +193 -0
  1002. code_towel-1.772/tests/test_generated_code_formatting.py +433 -0
  1003. code_towel-1.772/tests/test_generic_body_annotations.py +308 -0
  1004. code_towel-1.772/tests/test_generic_expression_types.py +313 -0
  1005. code_towel-1.772/tests/test_generic_extraction.py +408 -0
  1006. code_towel-1.772/tests/test_generic_method_boundaries.py +305 -0
  1007. code_towel-1.772/tests/test_generic_methods.py +285 -0
  1008. code_towel-1.772/tests/test_generic_probe_positions.py +110 -0
  1009. code_towel-1.772/tests/test_generic_verification.py +365 -0
  1010. code_towel-1.772/tests/test_helper_annotations.py +458 -0
  1011. code_towel-1.772/tests/test_helper_hosts.py +147 -0
  1012. code_towel-1.772/tests/test_helper_inventory.py +230 -0
  1013. code_towel-1.772/tests/test_helpers.py +282 -0
  1014. code_towel-1.772/tests/test_helpers_that_compute_nothing.py +151 -0
  1015. code_towel-1.772/tests/test_host_left_out_of_the_build.py +399 -0
  1016. code_towel-1.772/tests/test_host_ships_with_borrower.py +236 -0
  1017. code_towel-1.772/tests/test_hostile_battery.py +433 -0
  1018. code_towel-1.772/tests/test_hostile_crossfile_battery.py +342 -0
  1019. code_towel-1.772/tests/test_hosting_rationale.py +399 -0
  1020. code_towel-1.772/tests/test_import_bindings.py +112 -0
  1021. code_towel-1.772/tests/test_import_cycle_package_init.py +71 -0
  1022. code_towel-1.772/tests/test_import_cycles_by_the_program.py +141 -0
  1023. code_towel-1.772/tests/test_import_model.py +1187 -0
  1024. code_towel-1.772/tests/test_import_name_corroboration.py +466 -0
  1025. code_towel-1.772/tests/test_import_position.py +214 -0
  1026. code_towel-1.772/tests/test_import_problem_refusal.py +673 -0
  1027. code_towel-1.772/tests/test_import_resolution.py +80 -0
  1028. code_towel-1.772/tests/test_import_sorting_scope.py +310 -0
  1029. code_towel-1.772/tests/test_import_time_code.py +417 -0
  1030. code_towel-1.772/tests/test_import_verdicts_hold_at_run_time.py +388 -0
  1031. code_towel-1.772/tests/test_in_place_stage.py +261 -0
  1032. code_towel-1.772/tests/test_incremental_global_passes.py +155 -0
  1033. code_towel-1.772/tests/test_instantiation_by_binding.py +236 -0
  1034. code_towel-1.772/tests/test_instantiation_check.py +380 -0
  1035. code_towel-1.772/tests/test_known_bases.py +122 -0
  1036. code_towel-1.772/tests/test_known_platforms.py +125 -0
  1037. code_towel-1.772/tests/test_ladder_hygiene.py +310 -0
  1038. code_towel-1.772/tests/test_lazy_engine_import.py +49 -0
  1039. code_towel-1.772/tests/test_lexical_binding_regressions.py +217 -0
  1040. code_towel-1.772/tests/test_licence_headers.py +58 -0
  1041. code_towel-1.772/tests/test_memoization_is_invisible.py +274 -0
  1042. code_towel-1.772/tests/test_method_helper_hosting.py +352 -0
  1043. code_towel-1.772/tests/test_method_host_machinery.py +534 -0
  1044. code_towel-1.772/tests/test_method_level_and_attributes.py +83 -0
  1045. code_towel-1.772/tests/test_method_receiver_arguments.py +54 -0
  1046. code_towel-1.772/tests/test_missing_submodules.py +213 -0
  1047. code_towel-1.772/tests/test_module_names_stay_free.py +133 -0
  1048. code_towel-1.772/tests/test_moved_comments.py +1312 -0
  1049. code_towel-1.772/tests/test_mypy_judged_as_configured.py +752 -0
  1050. code_towel-1.772/tests/test_mypy_matches_its_own_run.py +326 -0
  1051. code_towel-1.772/tests/test_mypy_plugins.py +278 -0
  1052. code_towel-1.772/tests/test_mypy_worker_isolation.py +265 -0
  1053. code_towel-1.772/tests/test_names_typed_any.py +362 -0
  1054. code_towel-1.772/tests/test_narrowing_refusal.py +263 -0
  1055. code_towel-1.772/tests/test_no_builtin_arguments.py +454 -0
  1056. code_towel-1.772/tests/test_no_quoted_none.py +56 -0
  1057. code_towel-1.772/tests/test_noop_application.py +86 -0
  1058. code_towel-1.772/tests/test_observational_equivalence.py +922 -0
  1059. code_towel-1.772/tests/test_only_binding_moves.py +245 -0
  1060. code_towel-1.772/tests/test_orphan_detection.py +268 -0
  1061. code_towel-1.772/tests/test_orphan_detector_comprehensive.py +604 -0
  1062. code_towel-1.772/tests/test_out_of_place_cycle_regression.py +181 -0
  1063. code_towel-1.772/tests/test_out_of_place_stage.py +141 -0
  1064. code_towel-1.772/tests/test_out_of_place_whole_project.py +279 -0
  1065. code_towel-1.772/tests/test_output_import_names.py +124 -0
  1066. code_towel-1.772/tests/test_overlap_filtering.py +389 -0
  1067. code_towel-1.772/tests/test_pair_budget.py +125 -0
  1068. code_towel-1.772/tests/test_parallel_evaluation.py +230 -0
  1069. code_towel-1.772/tests/test_parameterize_builtins.py +572 -0
  1070. code_towel-1.772/tests/test_parameters.py +45 -0
  1071. code_towel-1.772/tests/test_parameters_stand_alone.py +170 -0
  1072. code_towel-1.772/tests/test_parent_watchdog.py +124 -0
  1073. code_towel-1.772/tests/test_pending_journal_rule.py +353 -0
  1074. code_towel-1.772/tests/test_perf_memos.py +252 -0
  1075. code_towel-1.772/tests/test_pipeline_api.py +71 -0
  1076. code_towel-1.772/tests/test_pipeline_phases.py +363 -0
  1077. code_towel-1.772/tests/test_platform_only_hosts.py +255 -0
  1078. code_towel-1.772/tests/test_program_imports.py +150 -0
  1079. code_towel-1.772/tests/test_program_type_names.py +901 -0
  1080. code_towel-1.772/tests/test_progress_heartbeat.py +194 -0
  1081. code_towel-1.772/tests/test_progress_modes.py +177 -0
  1082. code_towel-1.772/tests/test_project_helper_names.py +145 -0
  1083. code_towel-1.772/tests/test_project_layout_and_imports.py +225 -0
  1084. code_towel-1.772/tests/test_project_layout_behavior.py +109 -0
  1085. code_towel-1.772/tests/test_project_layout_shim.py +27 -0
  1086. code_towel-1.772/tests/test_promotion_and_mangling.py +207 -0
  1087. code_towel-1.772/tests/test_properties.py +1202 -0
  1088. code_towel-1.772/tests/test_pyright_configuration_grammar.py +238 -0
  1089. code_towel-1.772/tests/test_pyright_editable_installs.py +155 -0
  1090. code_towel-1.772/tests/test_pyright_probes_stay_private.py +122 -0
  1091. code_towel-1.772/tests/test_pyright_scope.py +216 -0
  1092. code_towel-1.772/tests/test_pyright_server_configuration.py +349 -0
  1093. code_towel-1.772/tests/test_pyright_session.py +321 -0
  1094. code_towel-1.772/tests/test_r9my_check_fails_unchanged.py +116 -0
  1095. code_towel-1.772/tests/test_r9my_check_scope.py +203 -0
  1096. code_towel-1.772/tests/test_r9my_environment_by_content.py +162 -0
  1097. code_towel-1.772/tests/test_r9my_mypy_config_groups.py +209 -0
  1098. code_towel-1.772/tests/test_r9my_mypy_jurisdiction.py +238 -0
  1099. code_towel-1.772/tests/test_r9py_pyright_config_groups.py +149 -0
  1100. code_towel-1.772/tests/test_r9py_pyright_config_venv.py +155 -0
  1101. code_towel-1.772/tests/test_r9py_pyright_copy_inputs.py +167 -0
  1102. code_towel-1.772/tests/test_r9py_pyright_fork_guard.py +157 -0
  1103. code_towel-1.772/tests/test_r9py_pyright_marker_outside_include.py +106 -0
  1104. code_towel-1.772/tests/test_r9py_pyright_message_paths.py +149 -0
  1105. code_towel-1.772/tests/test_readme_links.py +75 -0
  1106. code_towel-1.772/tests/test_receiver_dependency.py +132 -0
  1107. code_towel-1.772/tests/test_recovery_journal.py +516 -0
  1108. code_towel-1.772/tests/test_refactor_engine_comprehensive.py +523 -0
  1109. code_towel-1.772/tests/test_refactor_engine_edge_cases.py +337 -0
  1110. code_towel-1.772/tests/test_refactor_engine_more.py +138 -0
  1111. code_towel-1.772/tests/test_refactor_engine_targeted_branches.py +383 -0
  1112. code_towel-1.772/tests/test_refactoring_engine.py +268 -0
  1113. code_towel-1.772/tests/test_region_directives.py +633 -0
  1114. code_towel-1.772/tests/test_regression.py +495 -0
  1115. code_towel-1.772/tests/test_regressions_broader.py +174 -0
  1116. code_towel-1.772/tests/test_rejected_proposal_memory.py +212 -0
  1117. code_towel-1.772/tests/test_release_regressions.py +157 -0
  1118. code_towel-1.772/tests/test_rename_audit_regressions.py +261 -0
  1119. code_towel-1.772/tests/test_rename_by_the_programs_names.py +117 -0
  1120. code_towel-1.772/tests/test_rename_class_private_helpers.py +368 -0
  1121. code_towel-1.772/tests/test_rename_discovery_regressions.py +189 -0
  1122. code_towel-1.772/tests/test_rename_parameters_and_methods.py +144 -0
  1123. code_towel-1.772/tests/test_renamed_binder_observability_runtime.py +240 -0
  1124. code_towel-1.772/tests/test_required_namesakes.py +619 -0
  1125. code_towel-1.772/tests/test_return_alignment.py +58 -0
  1126. code_towel-1.772/tests/test_return_values.py +108 -0
  1127. code_towel-1.772/tests/test_returned_variables_not_orphaned.py +112 -0
  1128. code_towel-1.772/tests/test_reuse_existing_function.py +332 -0
  1129. code_towel-1.772/tests/test_reuse_type_checks.py +132 -0
  1130. code_towel-1.772/tests/test_revealed_spellings.py +249 -0
  1131. code_towel-1.772/tests/test_robustness_paths.py +212 -0
  1132. code_towel-1.772/tests/test_run_by_path.py +225 -0
  1133. code_towel-1.772/tests/test_run_lookups.py +188 -0
  1134. code_towel-1.772/tests/test_scope_analyzer.py +248 -0
  1135. code_towel-1.772/tests/test_scope_analyzer_comprehensive.py +786 -0
  1136. code_towel-1.772/tests/test_sdist_manifest.py +132 -0
  1137. code_towel-1.772/tests/test_self_outside_its_class.py +70 -0
  1138. code_towel-1.772/tests/test_semantic_guard_traces.py +204 -0
  1139. code_towel-1.772/tests/test_semantic_guards_extended.py +215 -0
  1140. code_towel-1.772/tests/test_semantic_safety_regressions.py +407 -0
  1141. code_towel-1.772/tests/test_shared_line_directives.py +195 -0
  1142. code_towel-1.772/tests/test_shipped_files_recorded.py +346 -0
  1143. code_towel-1.772/tests/test_sidecar_records_applied_helpers.py +96 -0
  1144. code_towel-1.772/tests/test_signature_prefilter.py +79 -0
  1145. code_towel-1.772/tests/test_source_encoding.py +178 -0
  1146. code_towel-1.772/tests/test_source_reading_callees.py +440 -0
  1147. code_towel-1.772/tests/test_speculative_cache_isolation.py +137 -0
  1148. code_towel-1.772/tests/test_splicing.py +420 -0
  1149. code_towel-1.772/tests/test_stale_proposal_recovery.py +223 -0
  1150. code_towel-1.772/tests/test_statement_facts.py +276 -0
  1151. code_towel-1.772/tests/test_statically_read_literals.py +498 -0
  1152. code_towel-1.772/tests/test_structural_memo.py +122 -0
  1153. code_towel-1.772/tests/test_stub_resolution.py +111 -0
  1154. code_towel-1.772/tests/test_stubbed_host.py +125 -0
  1155. code_towel-1.772/tests/test_substitution_follows_bindings.py +298 -0
  1156. code_towel-1.772/tests/test_substitution_keys.py +60 -0
  1157. code_towel-1.772/tests/test_super_in_method_helpers.py +992 -0
  1158. code_towel-1.772/tests/test_symlinked_input_directory.py +132 -0
  1159. code_towel-1.772/tests/test_symlinked_project_paths.py +235 -0
  1160. code_towel-1.772/tests/test_targeted_rung.py +191 -0
  1161. code_towel-1.772/tests/test_thunk_inlining.py +190 -0
  1162. code_towel-1.772/tests/test_thunks_meet_inlined_comprehensions.py +129 -0
  1163. code_towel-1.772/tests/test_top_level_module_inside_package.py +247 -0
  1164. code_towel-1.772/tests/test_trivial_helper_filter.py +130 -0
  1165. code_towel-1.772/tests/test_type_bindings.py +561 -0
  1166. code_towel-1.772/tests/test_type_checking_import_cycles.py +329 -0
  1167. code_towel-1.772/tests/test_type_checking_imports.py +427 -0
  1168. code_towel-1.772/tests/test_type_declaration_materialization.py +389 -0
  1169. code_towel-1.772/tests/test_type_generalization.py +253 -0
  1170. code_towel-1.772/tests/test_type_inference.py +679 -0
  1171. code_towel-1.772/tests/test_type_only_imports.py +221 -0
  1172. code_towel-1.772/tests/test_type_oracle_projects.py +568 -0
  1173. code_towel-1.772/tests/test_type_run_policy.py +429 -0
  1174. code_towel-1.772/tests/test_typing_form_resolution.py +207 -0
  1175. code_towel-1.772/tests/test_typing_forms_cost.py +55 -0
  1176. code_towel-1.772/tests/test_typing_forms_resolved.py +496 -0
  1177. code_towel-1.772/tests/test_unchecked_code.py +372 -0
  1178. code_towel-1.772/tests/test_unconfigured_mypy.py +476 -0
  1179. code_towel-1.772/tests/test_unifier_additional_branches.py +103 -0
  1180. code_towel-1.772/tests/test_unifier_batch2.py +144 -0
  1181. code_towel-1.772/tests/test_unifier_batch3.py +183 -0
  1182. code_towel-1.772/tests/test_unifier_batch4.py +116 -0
  1183. code_towel-1.772/tests/test_unifier_batch5.py +119 -0
  1184. code_towel-1.772/tests/test_unifier_bound_variables.py +360 -0
  1185. code_towel-1.772/tests/test_unifier_comprehensions.py +76 -0
  1186. code_towel-1.772/tests/test_unifier_comprehensive.py +360 -0
  1187. code_towel-1.772/tests/test_unifier_core.py +309 -0
  1188. code_towel-1.772/tests/test_unifier_edge_cases.py +205 -0
  1189. code_towel-1.772/tests/test_unifier_more_branches.py +222 -0
  1190. code_towel-1.772/tests/test_unifier_state_reset.py +107 -0
  1191. code_towel-1.772/tests/test_unparsed_program.py +622 -0
  1192. code_towel-1.772/tests/test_unreadable_and_revealed.py +189 -0
  1193. code_towel-1.772/tests/test_unsupported_layout.py +113 -0
  1194. code_towel-1.772/tests/test_untypeable_extractions.py +229 -0
  1195. code_towel-1.772/tests/test_validation_partial_lifetime.py +169 -0
  1196. code_towel-1.772/tests/test_variable_capture_bug.py +223 -0
  1197. code_towel-1.772/tests/test_visitors.py +419 -0
  1198. code_towel-1.772/tests/test_workers_fork_alone.py +493 -0
  1199. code_towel-1.772/tests/typed_fixtures.py +131 -0
  1200. code_towel-1.772/uv.lock +1554 -0
  1201. code_towel-1.732/CHANGELOG.md +0 -770
  1202. code_towel-1.732/CONTRIBUTING.md +0 -209
  1203. code_towel-1.732/MANIFEST.in +0 -16
  1204. code_towel-1.732/PKG-INFO +0 -378
  1205. code_towel-1.732/README.md +0 -333
  1206. code_towel-1.732/SECURITY.md +0 -19
  1207. code_towel-1.732/docs/ADVERSARIAL_REVIEW.md +0 -155
  1208. code_towel-1.732/docs/ARCHITECTURE.md +0 -898
  1209. code_towel-1.732/docs/KNOWN_LIMITATIONS.md +0 -565
  1210. code_towel-1.732/docs/OPEN_SOURCE_AUDIT.md +0 -68
  1211. code_towel-1.732/docs/PRODUCTION_READINESS.md +0 -697
  1212. code_towel-1.732/docs/QUICKSTART.md +0 -84
  1213. code_towel-1.732/docs/RELEASE_LOG.md +0 -344
  1214. code_towel-1.732/docs/RELEASING.md +0 -126
  1215. code_towel-1.732/docs/USAGE_GUIDE.md +0 -467
  1216. code_towel-1.732/docs/proposals/performance-roadmap.md +0 -144
  1217. code_towel-1.732/docs/proposals/reuse-existing-function.md +0 -171
  1218. code_towel-1.732/docs/proposals/template-method.md +0 -157
  1219. code_towel-1.732/docs/proposals/type-parameters.md +0 -322
  1220. code_towel-1.732/justfile +0 -172
  1221. code_towel-1.732/pyproject.toml +0 -134
  1222. code_towel-1.732/scripts/add_copyright_headers.py +0 -67
  1223. code_towel-1.732/scripts/audit_dependencies.py +0 -56
  1224. code_towel-1.732/scripts/bench_refactor.py +0 -118
  1225. code_towel-1.732/scripts/ecosystem/manifest.toml +0 -1158
  1226. code_towel-1.732/scripts/ecosystem_check.py +0 -1033
  1227. code_towel-1.732/scripts/report_net_benefit_diff.py +0 -116
  1228. code_towel-1.732/scripts/run_crossfile_coverage.py +0 -27
  1229. code_towel-1.732/scripts/set_version.py +0 -33
  1230. code_towel-1.732/scripts/trace_proposals.py +0 -99
  1231. code_towel-1.732/scripts/verify-examples +0 -111
  1232. code_towel-1.732/src/code_towel.egg-info/PKG-INFO +0 -378
  1233. code_towel-1.732/src/code_towel.egg-info/SOURCES.txt +0 -534
  1234. code_towel-1.732/src/towel/_mypy_worker.py +0 -177
  1235. code_towel-1.732/src/towel/changes.py +0 -327
  1236. code_towel-1.732/src/towel/checker_project.py +0 -220
  1237. code_towel-1.732/src/towel/cli.py +0 -1537
  1238. code_towel-1.732/src/towel/filesystem.py +0 -68
  1239. code_towel-1.732/src/towel/formatting.py +0 -437
  1240. code_towel-1.732/src/towel/project_layout.py +0 -633
  1241. code_towel-1.732/src/towel/renaming.py +0 -1326
  1242. code_towel-1.732/src/towel/source_files.py +0 -70
  1243. code_towel-1.732/src/towel/source_text.py +0 -94
  1244. code_towel-1.732/src/towel/type_inference.py +0 -1081
  1245. code_towel-1.732/src/towel/unification/annotation_wiring.py +0 -303
  1246. code_towel-1.732/src/towel/unification/annotations.py +0 -968
  1247. code_towel-1.732/src/towel/unification/assignment_analyzer.py +0 -394
  1248. code_towel-1.732/src/towel/unification/block_analysis.py +0 -846
  1249. code_towel-1.732/src/towel/unification/bounded_cache.py +0 -93
  1250. code_towel-1.732/src/towel/unification/builtins.py +0 -235
  1251. code_towel-1.732/src/towel/unification/clustering.py +0 -468
  1252. code_towel-1.732/src/towel/unification/constant_consistency.py +0 -149
  1253. code_towel-1.732/src/towel/unification/definite_assignment.py +0 -343
  1254. code_towel-1.732/src/towel/unification/engine_state.py +0 -301
  1255. code_towel-1.732/src/towel/unification/exceptions.py +0 -39
  1256. code_towel-1.732/src/towel/unification/extractor.py +0 -813
  1257. code_towel-1.732/src/towel/unification/fixed_point.py +0 -637
  1258. code_towel-1.732/src/towel/unification/hof_promotion.py +0 -219
  1259. code_towel-1.732/src/towel/unification/import_graph.py +0 -542
  1260. code_towel-1.732/src/towel/unification/insertion.py +0 -315
  1261. code_towel-1.732/src/towel/unification/instantiation.py +0 -478
  1262. code_towel-1.732/src/towel/unification/materialize.py +0 -472
  1263. code_towel-1.732/src/towel/unification/models.py +0 -392
  1264. code_towel-1.732/src/towel/unification/pair_evaluation.py +0 -1318
  1265. code_towel-1.732/src/towel/unification/parallel.py +0 -339
  1266. code_towel-1.732/src/towel/unification/parameterization.py +0 -313
  1267. code_towel-1.732/src/towel/unification/pipeline.py +0 -477
  1268. code_towel-1.732/src/towel/unification/placement.py +0 -531
  1269. code_towel-1.732/src/towel/unification/progress.py +0 -133
  1270. code_towel-1.732/src/towel/unification/project_layout.py +0 -10
  1271. code_towel-1.732/src/towel/unification/refactor_engine.py +0 -797
  1272. code_towel-1.732/src/towel/unification/reuse.py +0 -560
  1273. code_towel-1.732/src/towel/unification/scope_analyzer.py +0 -628
  1274. code_towel-1.732/src/towel/unification/semantic_safety.py +0 -1128
  1275. code_towel-1.732/src/towel/unification/statement_facts.py +0 -252
  1276. code_towel-1.732/src/towel/unification/structural_memo.py +0 -146
  1277. code_towel-1.732/src/towel/unification/substitution.py +0 -128
  1278. code_towel-1.732/src/towel/unification/thunk_inlining.py +0 -334
  1279. code_towel-1.732/src/towel/unification/unifier.py +0 -917
  1280. code_towel-1.732/src/towel/unification/unifier_state.py +0 -65
  1281. code_towel-1.732/src/towel/unification/visitors.py +0 -605
  1282. code_towel-1.732/test_examples_crossfile/README.md +0 -242
  1283. code_towel-1.732/test_examples_crossfile/multi_level/core/services/payment.py +0 -27
  1284. code_towel-1.732/test_examples_crossfile/multi_level/utils/validators.py +0 -26
  1285. code_towel-1.732/test_examples_crossfile/nested_structure/src/data_processor.py +0 -23
  1286. code_towel-1.732/test_examples_crossfile/simple_crossfile/user_service.py +0 -25
  1287. code_towel-1.732/test_examples_crossfile_expected_output/multi_level/api/checkout.py +0 -27
  1288. code_towel-1.732/test_examples_crossfile_expected_output/multi_level/core/services/payment.py +0 -18
  1289. code_towel-1.732/test_examples_crossfile_expected_output/multi_level/utils/validators.py +0 -17
  1290. code_towel-1.732/test_examples_crossfile_expected_output/nested_structure/lib/report_generator.py +0 -30
  1291. code_towel-1.732/test_examples_crossfile_expected_output/nested_structure/src/data_processor.py +0 -17
  1292. code_towel-1.732/test_examples_crossfile_expected_output/simple_crossfile/admin_service.py +0 -25
  1293. code_towel-1.732/test_examples_crossfile_expected_output/simple_crossfile/user_service.py +0 -17
  1294. code_towel-1.732/test_examples_expected_output/annotated_module.py +0 -108
  1295. code_towel-1.732/test_examples_expected_output/binding_constructs_comprehensive.py +0 -109
  1296. code_towel-1.732/test_examples_expected_output/bindings_for_loops.py +0 -48
  1297. code_towel-1.732/test_examples_expected_output/closure_adversarial.py +0 -115
  1298. code_towel-1.732/test_examples_expected_output/control_flow_adversarial.py +0 -117
  1299. code_towel-1.732/test_examples_expected_output/edge_cases_stress_test.py +0 -181
  1300. code_towel-1.732/test_examples_expected_output/example4_complex.py +0 -34
  1301. code_towel-1.732/test_examples_expected_output/exception_adversarial.py +0 -132
  1302. code_towel-1.732/test_examples_expected_output/fstrings_constants.py +0 -84
  1303. code_towel-1.732/test_examples_expected_output/functional_patterns.py +0 -224
  1304. code_towel-1.732/test_examples_expected_output/method_chains.py +0 -182
  1305. code_towel-1.732/test_examples_expected_output/return_values.py +0 -76
  1306. code_towel-1.732/test_examples_expected_output/scoping_edge_cases.py +0 -109
  1307. code_towel-1.732/test_examples_expected_output/side_effects_adversarial.py +0 -102
  1308. code_towel-1.732/test_examples_expected_output/syntactic_coverage_comprehensive.py +0 -632
  1309. code_towel-1.732/test_examples_expected_output/tricky_edge_cases_adversarial.py +0 -173
  1310. code_towel-1.732/tests/README.md +0 -254
  1311. code_towel-1.732/tests/__init__.py +0 -3
  1312. code_towel-1.732/tests/automatic_equivalence_tester.py +0 -638
  1313. code_towel-1.732/tests/conftest.py +0 -28
  1314. code_towel-1.732/tests/crossfile_equivalence_tester.py +0 -358
  1315. code_towel-1.732/tests/edge_case_values.py +0 -361
  1316. code_towel-1.732/tests/equivalence_targets.py +0 -147
  1317. code_towel-1.732/tests/generate_baseline.py +0 -176
  1318. code_towel-1.732/tests/hostile_cases/h01_side_effect_order.py +0 -23
  1319. code_towel-1.732/tests/hostile_cases/h02_conditional_eval.py +0 -16
  1320. code_towel-1.732/tests/hostile_cases/h03_loop_reeval.py +0 -19
  1321. code_towel-1.732/tests/hostile_cases/h04_closure_freevar.py +0 -16
  1322. code_towel-1.732/tests/hostile_cases/h05_cond_return_plus_retvar.py +0 -20
  1323. code_towel-1.732/tests/hostile_cases/h06_del_in_block.py +0 -20
  1324. code_towel-1.732/tests/hostile_cases/h07_except_unbind.py +0 -24
  1325. code_towel-1.732/tests/hostile_cases/h08_late_binding_closure.py +0 -16
  1326. code_towel-1.732/tests/hostile_cases/h10_short_circuit.py +0 -14
  1327. code_towel-1.732/tests/hostile_cases/h13_dunder_file.py +0 -13
  1328. code_towel-1.732/tests/hostile_cases/h26_match_capture.py +0 -18
  1329. code_towel-1.732/tests/hostile_cases/h38_closure_after_block.py +0 -18
  1330. code_towel-1.732/tests/hostile_cases/h39_augassign_after_block.py +0 -16
  1331. code_towel-1.732/tests/hostile_cases/h51_semicolons.py +0 -14
  1332. code_towel-1.732/tests/hostile_cases/h52_del_after_block.py +0 -16
  1333. code_towel-1.732/tests/hostile_cases/h53_nested_func_reads_retvar.py +0 -14
  1334. code_towel-1.732/tests/hostile_cases/h54_attr_param_property.py +0 -21
  1335. code_towel-1.732/tests/hostile_cases/h55_walrus_used_after.py +0 -17
  1336. code_towel-1.732/tests/hostile_cases/h56_global_write.py +0 -15
  1337. code_towel-1.732/tests/hostile_cases/h57_exception_in_param.py +0 -18
  1338. code_towel-1.732/tests/hostile_cases/h58_nested_def_freevar_reassigned.py +0 -14
  1339. code_towel-1.732/tests/hostile_cases/h59_retvar_conditional_unbound.py +0 -20
  1340. code_towel-1.732/tests/hostile_cases/h60_finally_return.py +0 -18
  1341. code_towel-1.732/tests/hostile_cases/h61_str_method_param.py +0 -16
  1342. code_towel-1.732/tests/hostile_cases/r01_property_thunk_order.py +0 -19
  1343. code_towel-1.732/tests/hostile_cases/r02_cluster_attrs.py +0 -25
  1344. code_towel-1.732/tests/hostile_cases/r07_attr_two_positions.py +0 -16
  1345. code_towel-1.732/tests/hostile_cases/r100_clustered_site_with_live_binding.py +0 -22
  1346. code_towel-1.732/tests/hostile_cases/r101_same_named_method_forces_module_helper.py +0 -43
  1347. code_towel-1.732/tests/hostile_cases/r102_whole_body_reuse.py +0 -26
  1348. code_towel-1.732/tests/hostile_cases/r103_lambda_parameter_spelling.py +0 -25
  1349. code_towel-1.732/tests/hostile_cases/r104_walrus_target_spelling.py +0 -14
  1350. code_towel-1.732/tests/hostile_cases/r105_closure_over_block_bound_name_rebound_after.py +0 -18
  1351. code_towel-1.732/tests/hostile_cases/r106_dir_reads_the_frame.py +0 -13
  1352. code_towel-1.732/tests/hostile_cases/r107_unbound_global_argument.py +0 -14
  1353. code_towel-1.732/tests/hostile_cases/r108_clustered_sites_assign_the_returned_name.py +0 -23
  1354. code_towel-1.732/tests/hostile_cases/r109_eager_optional_import_failed.py +0 -19
  1355. code_towel-1.732/tests/hostile_cases/r110_eager_type_checking_import.py +0 -18
  1356. code_towel-1.732/tests/hostile_cases/r111_eager_module_except_as_name.py +0 -22
  1357. code_towel-1.732/tests/hostile_cases/r112_eager_module_match_capture_unmatched.py +0 -19
  1358. code_towel-1.732/tests/hostile_cases/r113_eager_class_attr_in_method.py +0 -17
  1359. code_towel-1.732/tests/hostile_cases/r114_eager_nested_class_attr.py +0 -19
  1360. code_towel-1.732/tests/hostile_cases/r115_eager_class_in_function_attr.py +0 -20
  1361. code_towel-1.732/tests/hostile_cases/r116_eager_enclosing_bound_after_inner_call.py +0 -19
  1362. code_towel-1.732/tests/hostile_cases/r117_eager_enclosing_deleted_before_inner_call.py +0 -19
  1363. code_towel-1.732/tests/hostile_cases/r118_eager_enclosing_conditionally_bound.py +0 -19
  1364. code_towel-1.732/tests/hostile_cases/r119_eager_module_def_and_class_after_call.py +0 -19
  1365. code_towel-1.732/tests/hostile_cases/r11_same_function_pair.py +0 -14
  1366. code_towel-1.732/tests/hostile_cases/r120_eager_module_import_after_call.py +0 -17
  1367. code_towel-1.732/tests/hostile_cases/r121_eager_comprehension_variable.py +0 -16
  1368. code_towel-1.732/tests/hostile_cases/r122_eager_lambda_parameter.py +0 -16
  1369. code_towel-1.732/tests/hostile_cases/r123_direct_eval_after_block.py +0 -16
  1370. code_towel-1.732/tests/hostile_cases/r124_eval_alias_reads_preblock_name.py +0 -46
  1371. code_towel-1.732/tests/hostile_cases/r125_locals_dir_after_block.py +0 -40
  1372. code_towel-1.732/tests/hostile_cases/r126_locals_in_nested_scope.py +0 -30
  1373. code_towel-1.732/tests/hostile_cases/r127_eval_alias_partial_block.py +0 -53
  1374. code_towel-1.732/tests/hostile_cases/r128_warn_stacklevel_via_alias.py +0 -31
  1375. code_towel-1.732/tests/hostile_cases/r129_warning_registry_dedup.py +0 -18
  1376. code_towel-1.732/tests/hostile_cases/r130_resource_observed_after_block_differs.py +0 -20
  1377. code_towel-1.732/tests/hostile_cases/r131_weakref_and_id_identity.py +0 -27
  1378. code_towel-1.732/tests/hostile_cases/r132_async_comprehension_in_block.py +0 -16
  1379. code_towel-1.732/tests/hostile_cases/r134_u2028_in_comment_shifts_splice.py +0 -17
  1380. code_towel-1.732/tests/hostile_cases/r136_u2028_in_string_literal.py +0 -17
  1381. code_towel-1.732/tests/hostile_cases/r137_x1c_x85_in_comment.py +0 -17
  1382. code_towel-1.732/tests/hostile_cases/r138_u2028_comment_same_indent_neighbours.py +0 -24
  1383. code_towel-1.732/tests/hostile_cases/r139_literal_roundtrip.py +0 -18
  1384. code_towel-1.732/tests/hostile_cases/r140_unbound_name_on_untaken_branch.py +0 -21
  1385. code_towel-1.732/tests/hostile_cases/r141_module_name_bound_after_an_early_call.py +0 -24
  1386. code_towel-1.732/tests/hostile_cases/r142_module_data_rebound_by_callback.py +0 -22
  1387. code_towel-1.732/tests/hostile_cases/r143_module_name_shadowed_by_a_local_elsewhere.py +0 -22
  1388. code_towel-1.732/tests/hostile_cases/r144_class_cell_in_a_method_helper.py +0 -32
  1389. code_towel-1.732/tests/hostile_cases/r145_getframe_assigned_alias.py +0 -47
  1390. code_towel-1.732/tests/hostile_cases/r146_module_function_reads_caller_frame.py +0 -37
  1391. code_towel-1.732/tests/hostile_cases/r14_starred_param.py +0 -15
  1392. code_towel-1.732/tests/hostile_cases/r22_fstring_attr.py +0 -19
  1393. code_towel-1.732/tests/hostile_cases/r26_attr_store_param.py +0 -14
  1394. code_towel-1.732/tests/hostile_cases/r27_del_subscript.py +0 -12
  1395. code_towel-1.732/tests/hostile_cases/r32_method_callee.py +0 -18
  1396. code_towel-1.732/tests/hostile_cases/r38_name_two_positions.py +0 -14
  1397. code_towel-1.732/tests/hostile_cases/r39_cluster_alpha.py +0 -17
  1398. code_towel-1.732/tests/hostile_cases/r40_class_hierarchy.py +0 -21
  1399. code_towel-1.732/tests/hostile_cases/r41_conditional_call_param.py +0 -17
  1400. code_towel-1.732/tests/hostile_cases/r42_loop_call_param.py +0 -16
  1401. code_towel-1.732/tests/hostile_cases/r43_opaque_decorator_receiver.py +0 -17
  1402. code_towel-1.732/tests/hostile_cases/r44_cluster_nested_overlap.py +0 -23
  1403. code_towel-1.732/tests/hostile_cases/r45_async_no_await.py +0 -17
  1404. code_towel-1.732/tests/hostile_cases/r46_nonlocal_counter.py +0 -22
  1405. code_towel-1.732/tests/hostile_cases/r47_lru_cache_method.py +0 -19
  1406. code_towel-1.732/tests/hostile_cases/r50_helper_name_collision.py +0 -18
  1407. code_towel-1.732/tests/hostile_cases/r53_try_else_binding.py +0 -28
  1408. code_towel-1.732/tests/hostile_cases/r54_with_binding_used_after.py +0 -15
  1409. code_towel-1.732/tests/hostile_cases/r55_chained_assign.py +0 -14
  1410. code_towel-1.732/tests/hostile_cases/r57_star_unpack.py +0 -12
  1411. code_towel-1.732/tests/hostile_cases/r58_escaping_closure.py +0 -16
  1412. code_towel-1.732/tests/hostile_cases/r60_local_import.py +0 -14
  1413. code_towel-1.732/tests/hostile_cases/r61_same_function_clean.py +0 -13
  1414. code_towel-1.732/tests/hostile_cases/r62_recursion.py +0 -14
  1415. code_towel-1.732/tests/hostile_cases/r63_except_var_used_in_handler.py +0 -22
  1416. code_towel-1.732/tests/hostile_cases/r66_conditional_callee.py +0 -17
  1417. code_towel-1.732/tests/hostile_cases/r69_annassign.py +0 -14
  1418. code_towel-1.732/tests/hostile_cases/r72_global_promoted.py +0 -17
  1419. code_towel-1.732/tests/hostile_cases/r73_slice_vs_index.py +0 -12
  1420. code_towel-1.732/tests/hostile_cases/r74_starred_vs_plain.py +0 -15
  1421. code_towel-1.732/tests/hostile_cases/r75_prior_helper_param_names.py +0 -14
  1422. code_towel-1.732/tests/hostile_cases/r76_return_order.py +0 -16
  1423. code_towel-1.732/tests/hostile_cases/r77_mixed_return_and_variables.py +0 -17
  1424. code_towel-1.732/tests/hostile_cases/r78_walrus_in_parameter.py +0 -17
  1425. code_towel-1.732/tests/hostile_cases/r79_list_display_in_loop.py +0 -23
  1426. code_towel-1.732/tests/hostile_cases/r80_inlined_leading_thunk.py +0 -19
  1427. code_towel-1.732/tests/hostile_cases/r81_conditionally_bound_free_variable.py +0 -21
  1428. code_towel-1.732/tests/hostile_cases/r82_local_classes_same_name.py +0 -20
  1429. code_towel-1.732/tests/hostile_cases/r83_tab_indented_class.py +0 -20
  1430. code_towel-1.732/tests/hostile_cases/r84_warn_stacklevel.py +0 -18
  1431. code_towel-1.732/tests/hostile_cases/r85_conditionally_bound_parameter.py +0 -25
  1432. code_towel-1.732/tests/hostile_cases/r86_annotated_assignment_live.py +0 -26
  1433. code_towel-1.732/tests/hostile_cases/r87_nested_function_in_method.py +0 -22
  1434. code_towel-1.732/tests/hostile_cases/r88_elif_branch.py +0 -25
  1435. code_towel-1.732/tests/hostile_cases/r89_conditionally_bound_return.py +0 -28
  1436. code_towel-1.732/tests/hostile_cases/r90_cluster_across_classes.py +0 -28
  1437. code_towel-1.732/tests/hostile_cases/r91_class_body_helper.py +0 -23
  1438. code_towel-1.732/tests/hostile_cases/r92_parameterless_class_body_helper.py +0 -25
  1439. code_towel-1.732/tests/hostile_cases/r93_import_name_differs.py +0 -39
  1440. code_towel-1.732/tests/hostile_cases/r94_import_binds_live_name.py +0 -12
  1441. code_towel-1.732/tests/hostile_cases/r95_type_checking_annotation.py +0 -18
  1442. code_towel-1.732/tests/hostile_cases/r96_unpacked_targets_live.py +0 -19
  1443. code_towel-1.732/tests/hostile_cases/r97_helper_in_function_with_outside_site.py +0 -21
  1444. code_towel-1.732/tests/hostile_cases/r98_same_named_methods_nested_helpers.py +0 -37
  1445. code_towel-1.732/tests/hostile_cases/r99_partial_return_branches.py +0 -25
  1446. code_towel-1.732/tests/hostile_crossfile/xf10_reuse_existing_function/pkg/__init__.py +0 -0
  1447. code_towel-1.732/tests/hostile_crossfile/xf10_reuse_existing_function/pkg/a.py +0 -6
  1448. code_towel-1.732/tests/hostile_crossfile/xf10_reuse_existing_function/pkg/b.py +0 -12
  1449. code_towel-1.732/tests/hostile_crossfile/xf10_reuse_existing_function/run.py +0 -3
  1450. code_towel-1.732/tests/hostile_crossfile/xf11_package_init_reaches_back/pkg/__init__.py +0 -9
  1451. code_towel-1.732/tests/hostile_crossfile/xf11_package_init_reaches_back/pkg/detail.py +0 -7
  1452. code_towel-1.732/tests/hostile_crossfile/xf11_package_init_reaches_back/run.py +0 -3
  1453. code_towel-1.732/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/__init__.py +0 -1
  1454. code_towel-1.732/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/core.py +0 -3
  1455. code_towel-1.732/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/sub/__init__.py +0 -2
  1456. code_towel-1.732/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/sub/leaf.py +0 -6
  1457. code_towel-1.732/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/vendor/__init__.py +0 -0
  1458. code_towel-1.732/tests/hostile_crossfile/xf12_cycle_through_package_init/pkg/vendor/tool.py +0 -6
  1459. code_towel-1.732/tests/hostile_crossfile/xf12_cycle_through_package_init/run.py +0 -4
  1460. code_towel-1.732/tests/hostile_crossfile/xf13_import_time_effects/pkg/__init__.py +0 -0
  1461. code_towel-1.732/tests/hostile_crossfile/xf13_import_time_effects/pkg/a.py +0 -8
  1462. code_towel-1.732/tests/hostile_crossfile/xf13_import_time_effects/pkg/b.py +0 -10
  1463. code_towel-1.732/tests/hostile_crossfile/xf13_import_time_effects/run.py +0 -4
  1464. code_towel-1.732/tests/hostile_crossfile/xf14_script_with_leading_statement/pkg/__init__.py +0 -0
  1465. code_towel-1.732/tests/hostile_crossfile/xf14_script_with_leading_statement/pkg/app.py +0 -9
  1466. code_towel-1.732/tests/hostile_crossfile/xf14_script_with_leading_statement/pkg/util.py +0 -8
  1467. code_towel-1.732/tests/hostile_crossfile/xf14_script_with_leading_statement/run.py +0 -3
  1468. code_towel-1.732/tests/hostile_crossfile/xf15_ancestor_in_another_module/pkg/__init__.py +0 -0
  1469. code_towel-1.732/tests/hostile_crossfile/xf15_ancestor_in_another_module/pkg/base.py +0 -4
  1470. code_towel-1.732/tests/hostile_crossfile/xf15_ancestor_in_another_module/pkg/servers.py +0 -22
  1471. code_towel-1.732/tests/hostile_crossfile/xf15_ancestor_in_another_module/pkg/tokens.py +0 -7
  1472. code_towel-1.732/tests/hostile_crossfile/xf15_ancestor_in_another_module/run.py +0 -5
  1473. code_towel-1.732/tests/hostile_crossfile/xf3_same_named_local_function/pkg/__init__.py +0 -0
  1474. code_towel-1.732/tests/hostile_crossfile/xf3_same_named_local_function/pkg/a.py +0 -8
  1475. code_towel-1.732/tests/hostile_crossfile/xf3_same_named_local_function/pkg/b.py +0 -8
  1476. code_towel-1.732/tests/hostile_crossfile/xf3_same_named_local_function/run.py +0 -3
  1477. code_towel-1.732/tests/hostile_crossfile/xf4_same_named_class/pkg/__init__.py +0 -0
  1478. code_towel-1.732/tests/hostile_crossfile/xf4_same_named_class/pkg/a.py +0 -9
  1479. code_towel-1.732/tests/hostile_crossfile/xf4_same_named_class/pkg/b.py +0 -9
  1480. code_towel-1.732/tests/hostile_crossfile/xf4_same_named_class/run.py +0 -3
  1481. code_towel-1.732/tests/hostile_crossfile/xf5_dunder_file/pkg/__init__.py +0 -0
  1482. code_towel-1.732/tests/hostile_crossfile/xf5_dunder_file/pkg/a.py +0 -7
  1483. code_towel-1.732/tests/hostile_crossfile/xf5_dunder_file/pkg/b.py +0 -7
  1484. code_towel-1.732/tests/hostile_crossfile/xf5_dunder_file/run.py +0 -3
  1485. code_towel-1.732/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/__init__.py +0 -0
  1486. code_towel-1.732/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/a.py +0 -7
  1487. code_towel-1.732/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/b.py +0 -7
  1488. code_towel-1.732/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/util1.py +0 -1
  1489. code_towel-1.732/tests/hostile_crossfile/xf6_same_alias_different_import/pkg/util2.py +0 -1
  1490. code_towel-1.732/tests/hostile_crossfile/xf6_same_alias_different_import/run.py +0 -3
  1491. code_towel-1.732/tests/hostile_crossfile/xf7_docstring_import/pkg/__init__.py +0 -0
  1492. code_towel-1.732/tests/hostile_crossfile/xf7_docstring_import/pkg/a.py +0 -14
  1493. code_towel-1.732/tests/hostile_crossfile/xf7_docstring_import/pkg/b.py +0 -13
  1494. code_towel-1.732/tests/hostile_crossfile/xf7_docstring_import/run.py +0 -3
  1495. code_towel-1.732/tests/hostile_crossfile/xf8_helper_name_collision/pkg/__init__.py +0 -0
  1496. code_towel-1.732/tests/hostile_crossfile/xf8_helper_name_collision/pkg/a.py +0 -6
  1497. code_towel-1.732/tests/hostile_crossfile/xf8_helper_name_collision/pkg/b.py +0 -6
  1498. code_towel-1.732/tests/hostile_crossfile/xf8_helper_name_collision/pkg/c.py +0 -6
  1499. code_towel-1.732/tests/hostile_crossfile/xf8_helper_name_collision/pkg/d.py +0 -6
  1500. code_towel-1.732/tests/hostile_crossfile/xf8_helper_name_collision/pkg/e.py +0 -12
  1501. code_towel-1.732/tests/hostile_crossfile/xf8_helper_name_collision/run.py +0 -6
  1502. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/__init__.py +0 -0
  1503. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/__init__.py +0 -0
  1504. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/base.py +0 -3
  1505. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/introspect.py +0 -10
  1506. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/one/revoke.py +0 -10
  1507. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/__init__.py +0 -0
  1508. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/access.py +0 -10
  1509. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/base.py +0 -3
  1510. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/pkg/two/request.py +0 -10
  1511. code_towel-1.732/tests/hostile_crossfile/xf9_same_named_base_class/run.py +0 -8
  1512. code_towel-1.732/tests/hostile_execution.py +0 -31
  1513. code_towel-1.732/tests/run_tests.py +0 -31
  1514. code_towel-1.732/tests/test_adversarial_renaming.py +0 -328
  1515. code_towel-1.732/tests/test_adversarial_semantics.py +0 -305
  1516. code_towel-1.732/tests/test_analysis_edge_paths.py +0 -165
  1517. code_towel-1.732/tests/test_analysis_sessions.py +0 -268
  1518. code_towel-1.732/tests/test_ancestor_insertion.py +0 -76
  1519. code_towel-1.732/tests/test_assignment_analyzer_comprehensive.py +0 -725
  1520. code_towel-1.732/tests/test_audit_io_safety.py +0 -306
  1521. code_towel-1.732/tests/test_backend_layouts.py +0 -379
  1522. code_towel-1.732/tests/test_binding_context_memo.py +0 -58
  1523. code_towel-1.732/tests/test_binding_detector.py +0 -288
  1524. code_towel-1.732/tests/test_binding_detector_edge_cases.py +0 -378
  1525. code_towel-1.732/tests/test_bindings.py +0 -259
  1526. code_towel-1.732/tests/test_bindings_additional.py +0 -96
  1527. code_towel-1.732/tests/test_breakers.py +0 -119
  1528. code_towel-1.732/tests/test_builtins_comprehensive.py +0 -398
  1529. code_towel-1.732/tests/test_cache_lifetimes.py +0 -71
  1530. code_towel-1.732/tests/test_candidate_index.py +0 -190
  1531. code_towel-1.732/tests/test_change_transactions.py +0 -254
  1532. code_towel-1.732/tests/test_cli_dispatch_paths.py +0 -83
  1533. code_towel-1.732/tests/test_cli_input_errors.py +0 -86
  1534. code_towel-1.732/tests/test_cli_integration.py +0 -292
  1535. code_towel-1.732/tests/test_cli_oracle_lifecycle.py +0 -88
  1536. code_towel-1.732/tests/test_cli_output_safety.py +0 -51
  1537. code_towel-1.732/tests/test_comprehensive_coverage.py +0 -293
  1538. code_towel-1.732/tests/test_copy_preservation.py +0 -38
  1539. code_towel-1.732/tests/test_crossfile_integration.py +0 -419
  1540. code_towel-1.732/tests/test_crossfile_observational_equivalence.py +0 -28
  1541. code_towel-1.732/tests/test_definite_assignment.py +0 -169
  1542. code_towel-1.732/tests/test_dependency_audit_export.py +0 -81
  1543. code_towel-1.732/tests/test_distinct_proposals.py +0 -88
  1544. code_towel-1.732/tests/test_ecosystem_manifest_regressions.py +0 -89
  1545. code_towel-1.732/tests/test_ecosystem_verdicts.py +0 -1233
  1546. code_towel-1.732/tests/test_engine_adversarial.py +0 -1453
  1547. code_towel-1.732/tests/test_engine_failure_visibility.py +0 -34
  1548. code_towel-1.732/tests/test_environment_independence.py +0 -223
  1549. code_towel-1.732/tests/test_equivalence_harness_regressions.py +0 -161
  1550. code_towel-1.732/tests/test_equivalence_target_selection.py +0 -158
  1551. code_towel-1.732/tests/test_error_paths.py +0 -275
  1552. code_towel-1.732/tests/test_exceptions.py +0 -16
  1553. code_towel-1.732/tests/test_extractor_additional_branches.py +0 -116
  1554. code_towel-1.732/tests/test_extractor_augassign_and_fstrings.py +0 -112
  1555. code_towel-1.732/tests/test_extractor_callee_and_multi_return.py +0 -123
  1556. code_towel-1.732/tests/test_extractor_comprehensive.py +0 -555
  1557. code_towel-1.732/tests/test_extractor_edge_cases.py +0 -355
  1558. code_towel-1.732/tests/test_extractor_final_lines.py +0 -151
  1559. code_towel-1.732/tests/test_extractor_generate_call_variants.py +0 -82
  1560. code_towel-1.732/tests/test_extractor_more_paths.py +0 -123
  1561. code_towel-1.732/tests/test_extractor_preamble_and_call_mapping.py +0 -137
  1562. code_towel-1.732/tests/test_extractor_remaining_branches.py +0 -259
  1563. code_towel-1.732/tests/test_extractor_return_statements.py +0 -327
  1564. code_towel-1.732/tests/test_extractor_substituter_and_helpers.py +0 -411
  1565. code_towel-1.732/tests/test_filesystem_guards.py +0 -73
  1566. code_towel-1.732/tests/test_final_coverage_push.py +0 -251
  1567. code_towel-1.732/tests/test_fixed_point_refusals.py +0 -141
  1568. code_towel-1.732/tests/test_forwarder_chains.py +0 -264
  1569. code_towel-1.732/tests/test_frame_sensitivity_scan.py +0 -69
  1570. code_towel-1.732/tests/test_free_variable_correspondence.py +0 -271
  1571. code_towel-1.732/tests/test_fstrings.py +0 -174
  1572. code_towel-1.732/tests/test_function_facts_equivalence.py +0 -78
  1573. code_towel-1.732/tests/test_function_index.py +0 -76
  1574. code_towel-1.732/tests/test_generated_code_formatting.py +0 -282
  1575. code_towel-1.732/tests/test_helper_annotations.py +0 -436
  1576. code_towel-1.732/tests/test_helper_inventory.py +0 -186
  1577. code_towel-1.732/tests/test_helpers.py +0 -268
  1578. code_towel-1.732/tests/test_hostile_battery.py +0 -150
  1579. code_towel-1.732/tests/test_hostile_crossfile_battery.py +0 -77
  1580. code_towel-1.732/tests/test_import_bindings.py +0 -98
  1581. code_towel-1.732/tests/test_import_cycle_package_init.py +0 -74
  1582. code_towel-1.732/tests/test_import_resolution.py +0 -66
  1583. code_towel-1.732/tests/test_incremental_global_passes.py +0 -122
  1584. code_towel-1.732/tests/test_instantiation_check.py +0 -273
  1585. code_towel-1.732/tests/test_lazy_engine_import.py +0 -35
  1586. code_towel-1.732/tests/test_lexical_binding_regressions.py +0 -203
  1587. code_towel-1.732/tests/test_method_level_and_attributes.py +0 -69
  1588. code_towel-1.732/tests/test_method_receiver_arguments.py +0 -40
  1589. code_towel-1.732/tests/test_module_names_stay_free.py +0 -119
  1590. code_towel-1.732/tests/test_observational_equivalence.py +0 -908
  1591. code_towel-1.732/tests/test_orphan_detection.py +0 -254
  1592. code_towel-1.732/tests/test_orphan_detector_comprehensive.py +0 -589
  1593. code_towel-1.732/tests/test_out_of_place_cycle_regression.py +0 -166
  1594. code_towel-1.732/tests/test_overlap_filtering.py +0 -375
  1595. code_towel-1.732/tests/test_pair_budget.py +0 -111
  1596. code_towel-1.732/tests/test_parallel_evaluation.py +0 -222
  1597. code_towel-1.732/tests/test_parameters.py +0 -31
  1598. code_towel-1.732/tests/test_parent_watchdog.py +0 -110
  1599. code_towel-1.732/tests/test_perf_memos.py +0 -237
  1600. code_towel-1.732/tests/test_pipeline_api.py +0 -57
  1601. code_towel-1.732/tests/test_pipeline_phases.py +0 -367
  1602. code_towel-1.732/tests/test_progress_modes.py +0 -162
  1603. code_towel-1.732/tests/test_project_layout_and_imports.py +0 -266
  1604. code_towel-1.732/tests/test_project_layout_behavior.py +0 -324
  1605. code_towel-1.732/tests/test_project_layout_shim.py +0 -19
  1606. code_towel-1.732/tests/test_promotion_and_mangling.py +0 -193
  1607. code_towel-1.732/tests/test_properties.py +0 -1187
  1608. code_towel-1.732/tests/test_recovery_journal.py +0 -413
  1609. code_towel-1.732/tests/test_refactor_engine_comprehensive.py +0 -509
  1610. code_towel-1.732/tests/test_refactor_engine_edge_cases.py +0 -315
  1611. code_towel-1.732/tests/test_refactor_engine_more.py +0 -119
  1612. code_towel-1.732/tests/test_refactor_engine_targeted_branches.py +0 -363
  1613. code_towel-1.732/tests/test_refactoring_engine.py +0 -249
  1614. code_towel-1.732/tests/test_regression.py +0 -472
  1615. code_towel-1.732/tests/test_regressions_broader.py +0 -155
  1616. code_towel-1.732/tests/test_release_regressions.py +0 -145
  1617. code_towel-1.732/tests/test_rename_audit_regressions.py +0 -225
  1618. code_towel-1.732/tests/test_rename_discovery_regressions.py +0 -175
  1619. code_towel-1.732/tests/test_rename_parameters_and_methods.py +0 -130
  1620. code_towel-1.732/tests/test_return_alignment.py +0 -44
  1621. code_towel-1.732/tests/test_return_values.py +0 -94
  1622. code_towel-1.732/tests/test_returned_variables_not_orphaned.py +0 -98
  1623. code_towel-1.732/tests/test_reuse_existing_function.py +0 -578
  1624. code_towel-1.732/tests/test_reuse_type_checks.py +0 -125
  1625. code_towel-1.732/tests/test_robustness_paths.py +0 -178
  1626. code_towel-1.732/tests/test_scope_analyzer.py +0 -234
  1627. code_towel-1.732/tests/test_scope_analyzer_comprehensive.py +0 -663
  1628. code_towel-1.732/tests/test_semantic_guard_traces.py +0 -186
  1629. code_towel-1.732/tests/test_semantic_guards_extended.py +0 -201
  1630. code_towel-1.732/tests/test_semantic_safety_regressions.py +0 -427
  1631. code_towel-1.732/tests/test_signature_prefilter.py +0 -65
  1632. code_towel-1.732/tests/test_source_encoding.py +0 -86
  1633. code_towel-1.732/tests/test_stale_proposal_recovery.py +0 -209
  1634. code_towel-1.732/tests/test_statement_facts.py +0 -267
  1635. code_towel-1.732/tests/test_structural_memo.py +0 -98
  1636. code_towel-1.732/tests/test_substitution_keys.py +0 -25
  1637. code_towel-1.732/tests/test_symlinked_input_directory.py +0 -118
  1638. code_towel-1.732/tests/test_thunk_inlining.py +0 -173
  1639. code_towel-1.732/tests/test_trivial_helper_filter.py +0 -116
  1640. code_towel-1.732/tests/test_type_inference.py +0 -656
  1641. code_towel-1.732/tests/test_type_oracle_projects.py +0 -555
  1642. code_towel-1.732/tests/test_type_run_policy.py +0 -408
  1643. code_towel-1.732/tests/test_unifier_additional_branches.py +0 -89
  1644. code_towel-1.732/tests/test_unifier_batch2.py +0 -130
  1645. code_towel-1.732/tests/test_unifier_batch3.py +0 -169
  1646. code_towel-1.732/tests/test_unifier_batch4.py +0 -102
  1647. code_towel-1.732/tests/test_unifier_batch5.py +0 -105
  1648. code_towel-1.732/tests/test_unifier_bound_variables.py +0 -346
  1649. code_towel-1.732/tests/test_unifier_comprehensions.py +0 -62
  1650. code_towel-1.732/tests/test_unifier_comprehensive.py +0 -346
  1651. code_towel-1.732/tests/test_unifier_core.py +0 -188
  1652. code_towel-1.732/tests/test_unifier_edge_cases.py +0 -191
  1653. code_towel-1.732/tests/test_unifier_more_branches.py +0 -208
  1654. code_towel-1.732/tests/test_unifier_state_reset.py +0 -93
  1655. code_towel-1.732/tests/test_unsupported_layout.py +0 -84
  1656. code_towel-1.732/tests/test_validation_partial_lifetime.py +0 -155
  1657. code_towel-1.732/tests/test_variable_capture_bug.py +0 -209
  1658. code_towel-1.732/tests/test_visitors.py +0 -405
  1659. code_towel-1.732/uv.lock +0 -1554
  1660. {code_towel-1.732 → code_towel-1.772}/.flake8 +0 -0
  1661. {code_towel-1.732 → code_towel-1.772}/.pre-commit-config.yaml +0 -0
  1662. {code_towel-1.732 → code_towel-1.772}/CODE_OF_CONDUCT.md +0 -0
  1663. {code_towel-1.732 → code_towel-1.772}/LICENSE +0 -0
  1664. {code_towel-1.732 → code_towel-1.772}/docs/proposals/dethunking.md +0 -0
  1665. {code_towel-1.732 → code_towel-1.772}/scripts/bench_similar_blocks.py +0 -0
  1666. {code_towel-1.732 → code_towel-1.772}/scripts/promote_dry_helpers.py +0 -0
  1667. {code_towel-1.732 → code_towel-1.772}/setup.cfg +0 -0
  1668. {code_towel-1.732 → code_towel-1.772}/src/code_towel.egg-info/dependency_links.txt +0 -0
  1669. {code_towel-1.732 → code_towel-1.772}/src/code_towel.egg-info/entry_points.txt +0 -0
  1670. {code_towel-1.732 → code_towel-1.772}/src/code_towel.egg-info/requires.txt +0 -0
  1671. {code_towel-1.732 → code_towel-1.772}/src/code_towel.egg-info/top_level.txt +0 -0
  1672. {code_towel-1.732 → code_towel-1.772}/src/towel/__init__.py +0 -0
  1673. {code_towel-1.732 → code_towel-1.772}/src/towel/diagnostics.py +0 -0
  1674. {code_towel-1.732 → code_towel-1.772}/src/towel/project_tools.py +0 -0
  1675. {code_towel-1.732 → code_towel-1.772}/src/towel/py.typed +0 -0
  1676. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/__init__.py +0 -0
  1677. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/binding_context.py +0 -0
  1678. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/binding_detector.py +0 -0
  1679. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/block_signature.py +0 -0
  1680. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/defaults.py +0 -0
  1681. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/function_index.py +0 -0
  1682. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/orphan_detector.py +0 -0
  1683. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/overlap.py +0 -0
  1684. {code_towel-1.732 → code_towel-1.772}/src/towel/unification/parameters.py +0 -0
  1685. {code_towel-1.732 → code_towel-1.772}/test_examples/README.md +0 -0
  1686. {code_towel-1.732 → code_towel-1.772}/test_examples/annotated_module.py +0 -0
  1687. {code_towel-1.732 → code_towel-1.772}/test_examples/binding_constructs_comprehensive.py +0 -0
  1688. {code_towel-1.732 → code_towel-1.772}/test_examples/bindings_comprehensions.py +0 -0
  1689. {code_towel-1.732 → code_towel-1.772}/test_examples/bindings_for_loops.py +0 -0
  1690. {code_towel-1.732 → code_towel-1.772}/test_examples/closure_adversarial.py +0 -0
  1691. {code_towel-1.732 → code_towel-1.772}/test_examples/complex_expressions.py +0 -0
  1692. {code_towel-1.732 → code_towel-1.772}/test_examples/control_flow_adversarial.py +0 -0
  1693. {code_towel-1.732 → code_towel-1.772}/test_examples/edge_cases_stress_test.py +0 -0
  1694. {code_towel-1.732 → code_towel-1.772}/test_examples/example1_simple.py +0 -0
  1695. {code_towel-1.732 → code_towel-1.772}/test_examples/example2_classes.py +0 -0
  1696. {code_towel-1.732 → code_towel-1.772}/test_examples/example3_file1.py +0 -0
  1697. {code_towel-1.732 → code_towel-1.772}/test_examples/example3_file2.py +0 -0
  1698. {code_towel-1.732 → code_towel-1.772}/test_examples/example4_complex.py +0 -0
  1699. {code_towel-1.732 → code_towel-1.772}/test_examples/exception_adversarial.py +0 -0
  1700. {code_towel-1.732 → code_towel-1.772}/test_examples/fstrings_constants.py +0 -0
  1701. {code_towel-1.732 → code_towel-1.772}/test_examples/functional_patterns.py +0 -0
  1702. {code_towel-1.732 → code_towel-1.772}/test_examples/generators_async.py +0 -0
  1703. {code_towel-1.732 → code_towel-1.772}/test_examples/global_nonlocal_examples.py +0 -0
  1704. {code_towel-1.732 → code_towel-1.772}/test_examples/hygienic_naming.py +0 -0
  1705. {code_towel-1.732 → code_towel-1.772}/test_examples/method_chains.py +0 -0
  1706. {code_towel-1.732 → code_towel-1.772}/test_examples/nested_structures.py +0 -0
  1707. {code_towel-1.732 → code_towel-1.772}/test_examples/real_world_patterns.py +0 -0
  1708. {code_towel-1.732 → code_towel-1.772}/test_examples/referential_transparency.py +0 -0
  1709. {code_towel-1.732 → code_towel-1.772}/test_examples/return_values.py +0 -0
  1710. {code_towel-1.732 → code_towel-1.772}/test_examples/scoping_edge_cases.py +0 -0
  1711. {code_towel-1.732 → code_towel-1.772}/test_examples/side_effects_adversarial.py +0 -0
  1712. {code_towel-1.732 → code_towel-1.772}/test_examples/syntactic_coverage_comprehensive.py +0 -0
  1713. {code_towel-1.732 → code_towel-1.772}/test_examples/tricky_edge_cases_adversarial.py +0 -0
  1714. {code_towel-1.732 → code_towel-1.772}/test_examples_crossfile/class_hierarchy/base_record.py +0 -0
  1715. {code_towel-1.732 → code_towel-1.772}/test_examples_crossfile/class_hierarchy/scored_record.py +0 -0
  1716. {code_towel-1.732 → code_towel-1.772}/test_examples_crossfile/multi_level/api/checkout.py +0 -0
  1717. {code_towel-1.732 → code_towel-1.772}/test_examples_crossfile/nested_structure/lib/report_generator.py +0 -0
  1718. {code_towel-1.732 → code_towel-1.772}/test_examples_crossfile/simple_crossfile/admin_service.py +0 -0
  1719. {code_towel-1.732 → code_towel-1.772}/test_examples_crossfile_expected_output/class_hierarchy/base_record.py +0 -0
  1720. {code_towel-1.732 → code_towel-1.772}/test_examples_crossfile_expected_output/class_hierarchy/scored_record.py +0 -0
  1721. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/bindings_comprehensions.py +0 -0
  1722. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/complex_expressions.py +0 -0
  1723. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/example1_simple.py +0 -0
  1724. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/example2_classes.py +0 -0
  1725. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/example3_file1.py +0 -0
  1726. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/example3_file2.py +0 -0
  1727. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/generators_async.py +0 -0
  1728. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/global_nonlocal_examples.py +0 -0
  1729. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/hygienic_naming.py +0 -0
  1730. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/nested_structures.py +0 -0
  1731. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/real_world_patterns.py +0 -0
  1732. {code_towel-1.732 → code_towel-1.772}/test_examples_expected_output/referential_transparency.py +0 -0
  1733. {code_towel-1.732 → code_towel-1.772}/tests/OBSERVATIONAL_EQUIVALENCE.md +0 -0
  1734. {code_towel-1.732 → code_towel-1.772}/tests/hostile_cases/r133_formfeed.py +0 -0
  1735. {code_towel-1.732 → code_towel-1.772}/tests/hostile_cases/r135_formfeed_line_shifts_splice.py +0 -0
@@ -0,0 +1,2078 @@
1
+ # Changelog
2
+
3
+ All notable changes to Towel are recorded here. This file summarizes releases;
4
+ [docs/RELEASE_LOG.md](docs/RELEASE_LOG.md) keeps the detailed engineering log,
5
+ and [docs/PRODUCTION_READINESS.md](docs/PRODUCTION_READINESS.md) records the
6
+ ecosystem evidence behind each claim. The format follows
7
+ [Keep a Changelog](https://keepachangelog.com/), and the project aims to follow
8
+ [Semantic Versioning](https://semver.org/).
9
+
10
+ ## [Unreleased]
11
+
12
+ ## [1.772] - 2026-09-26
13
+
14
+ Changes since 1.732.
15
+
16
+ A typed run over a large project did not finish. Sphinx, 243 modules with mypy
17
+ and Pyright both strict, ran 3 h 21 min without reaching a fixed point where
18
+ the same run without types takes 11 minutes. Nearly all of that was the mypy
19
+ worker getting slower with every request it served, and most of the rest was
20
+ asking the checkers questions whose answers were already known. On
21
+ September 21 it reached a fixed point in 46 minutes, applying 380 refactorings
22
+ across 105 files, after which Sphinx's own test suite reported exactly what it
23
+ reported before: 2385 passed, 34 skipped, and the same six failures that
24
+ checkout already had. The audit rounds that followed made Towel decline more;
25
+ the release corpus, typed with `--cross-module` on one worker in the release corpus's Docker container, a Linux VM on the same M5 Max given all 18 cores and 8 GB of memory, running four projects at a time,
26
+ took 94 minutes over Sphinx and changed 55 files, its suite again unchanged.
27
+
28
+ Every figure in this file was measured on an Apple M5 Max with 18 cores and
29
+ 128 GiB. This entry's were taken on macOS 26.5.1 with Python 3.12.13, the
30
+ machine otherwise idle, except where a figure says otherwise: the release
31
+ corpus ran in the release corpus's Docker container, a Linux VM on the same M5 Max given all 18 cores and 8 GB of memory, running four projects at a time. The Sphinx figures are Sphinx 9.1.1 at `e44a40e`, 243 modules with
32
+ mypy and Pyright both strict, checked by that project's own venv: **mypy
33
+ 1.19.1 and pyright 1.1.407**. The mypy behaviour described below belongs to
34
+ that version; Towel's own checks run against a newer mypy and do not show it.
35
+
36
+ ### Fixed
37
+ - Resolving which typing form a name denotes grew exponentially with the
38
+ number of functions that alias a name to itself (`tok = tok.next_token`,
39
+ `node = node.parent`), a common way to walk a linked structure. Twelve such
40
+ functions cost 469 million calls and 371 s, and yapf's `--cross-module`
41
+ fixed point took 2,083 s (macOS, one worker, `--no-types --no-format`, the
42
+ run sharing the machine with one other). The resolution is now computed once
43
+ per name and depth, and the same run takes 5 s with byte-identical output. The release
44
+ corpus's yapf and sphinx runs had timed out on it.
45
+ - inline-snapshot tests that differ only in their literal were extracted
46
+ into one helper, so `snapshot()` read a helper parameter at its call site
47
+ and passing tests failed: 68 of rich-click's 151. inline-snapshot's
48
+ readers (`snapshot`, `external`, `snapshot_arg`) are now recognized by
49
+ binding and their calls stay where they stand. Other callees that read
50
+ their caller's frame are reflection, a documented limitation.
51
+ - Region directives opened before a block and closed after it declined it
52
+ only for pylint and coverage.py. Every tool's region directive now does,
53
+ as that tool reads it (ruff, fmt, yapf, isort), and a block holding
54
+ `fmt: off` or `fmt: skip` carries its protected layout into the helper as
55
+ written.
56
+ - A file of the program that does not parse on the Python Towel runs on now
57
+ refuses the run in every mode, before anything is written. It used to be
58
+ skipped, so on 3.11 a 3.12 project's hand-applied decorator, or a test's
59
+ patch of `len` in 3.12 syntax, went unseen. The refusal names each file,
60
+ the newest Python the project declares, and an `--exclude` that clears
61
+ it. `--exclude` takes a file name as well as a directory name; an excluded
62
+ file that parses is still read as evidence, and one that does not parse
63
+ is taken as no part of the program. Fixtures in syntax newer than a
64
+ supported Python are stored as `.pynew`.
65
+ - mypy was taken to look at every file it was handed, so a file its own run
66
+ never checks, one outside `files` or under `ignore_errors`, got annotations
67
+ nothing verified, and typed runs declined whole projects under
68
+ `ignore_errors`. Such a file is now treated as one no checker covers. Each
69
+ mypy configuration group is checked in its own cache over the whole
70
+ candidate; candidate checks honour `--exclude` and the project's mypy
71
+ `exclude` as the baseline does; and a project package named `env` is no
72
+ longer taken for an environment. A check that fails for the project as it
73
+ stands now stops the run at once, naming the cause: sqlmodel ran 15
74
+ minutes and exited 1 with every proposal "not judged".
75
+ - A project decorator that is a plain wrapper was trusted although another
76
+ module rebinds it, as `app.checks.checked = typeguard.typechecked` before
77
+ the decorated module imports it, and code moved out of the instrumented
78
+ function. A decorator is now known only when nothing in the program may
79
+ rebind it, at every module its name passes through. A stacked decoration
80
+ by hand, `f = typechecked(register(f))`, is judged for every call along
81
+ the chain.
82
+ - A star import made every decorator of its module unknown; it now makes
83
+ unknown only the names it may bind, read as it may run (a literal
84
+ `__all__` with the module's public names, since an import cycle can run
85
+ the star import first).
86
+ - A lambda, comprehension or nested function whose parameter is spelled
87
+ like a name that becomes a helper parameter read the helper's parameter
88
+ instead of its own, and silently computed the wrong result:
89
+ `max(orders, key=lambda order: order["total"])` beside a parameter
90
+ `order`. A name in an f-string's format spec (`f"{v:>{width}}"`), and the
91
+ attribute and subscript loads in a `for`, `with` or comprehension target
92
+ (`for box.v in items`), kept the template's spelling and raised NameError.
93
+ Substitution now follows Python's scopes, and the instantiation check,
94
+ which passed all three because it compared names by spelling, compares by
95
+ binding.
96
+ - A call is declined where its thunk would read a closure variable that an
97
+ inlined comprehension in the same function rebinds; Python 3.12 and later
98
+ raise NameError there.
99
+ - A block holding its function's only binding of a name that the function
100
+ reads elsewhere is declined (`moves_only_binding`), unless the call binds
101
+ the name again. Moving it made the name stop being local, so the other
102
+ reads found a module name or builtin where the original raised
103
+ UnboundLocalError. A helper now declares `global` or `nonlocal` every name
104
+ its blocks bind by any construct, and sites whose functions declare a name
105
+ differently share no helper (`scope_declarations_differ`).
106
+ - A new import was written above a `#!` line or an encoding declaration, so
107
+ a script lost its shebang or a Latin-1 module was read as UTF-8, and it
108
+ could land ahead of a borrower's statement that runs at import. Imports
109
+ now go below the shebang, the encoding line, a file-wide `# type: ignore`
110
+ and the leading comments, and after the borrower's leading statements that
111
+ run code. A write that would change how Python decodes a file is refused.
112
+ - `--cross-module` hosted a helper in a module some platform cannot import,
113
+ such as one that imports `msvcrt`, or in one the program imports only
114
+ under a condition. It now requires what CPython's documentation marks as
115
+ platform-only, what a supported Python removed, and what a marker limits,
116
+ and hosts in a conditionally imported module only for a borrower that
117
+ already loads it.
118
+ - Build selections are read for every setting of each listed backend, in
119
+ each distribution's own configuration: Poetry `packages` and `include`,
120
+ PDM `includes`, `source-includes` and `package-dir`, hatch `only-packages`
121
+ and its default, setuptools `py-modules`, uv `module-name` and
122
+ `module-root`, flit's module and scikit-build-core `wheel.packages`. A
123
+ helper was hosted in a module the wheel left out.
124
+ - `--cross-module` no longer makes a module of one distribution import a
125
+ helper from another (`other_distribution`), which failed when the borrower
126
+ was installed against the other's released version.
127
+ - A typed run checked a change to a member with its own pyright
128
+ configuration only from that member's root, so a consumer in the outer
129
+ project was judged against the original text, and a helper that broke it
130
+ was accepted. A change is now checked from every configured root that
131
+ encloses it, and each copy shows the whole change.
132
+ - pyright's configured `venvPath`/`venv` was dropped from Towel's copy, so
133
+ the check silently used Towel's own interpreter: a helper typed `-> int`
134
+ was accepted where the project's library returns `str`. pyright now
135
+ resolves imports in the configured environment, and an environment it
136
+ cannot use refuses the typed run. Search paths that lead into an
137
+ environment name the original.
138
+ - A pyright-typed run was refused whenever any data file was not UTF-8 or a
139
+ directory was linked from outside. Only the files pyright reads as
140
+ configuration are now held to UTF-8, and links are reproduced as links, so
141
+ an import through an alias inside the project sees the candidate.
142
+ - pyright's messages could carry the private copy's paths, so a
143
+ pre-existing import cycle declined every change. The command line's
144
+ errors without a range are accepted. A forked child can no longer disable
145
+ a `PyrightOracle` or delete its copy. A target outside pyright's `include`
146
+ no longer waits 60 s for the language server.
147
+ - Under `--cross-module`, a directory named like a library, such as
148
+ `third_party/click/` holding `utils.py`, was taken for the library when
149
+ Towel's interpreter lacked it, and Towel wrote `from click.utils import`.
150
+ A directory lacking a module that an import from outside it names is now
151
+ in doubt, unless the project's metadata gives the project that name.
152
+ Requirements declared in `[tool.uv]` and `[tool.pdm]` dev dependencies,
153
+ hatch environments, a Pipfile and `*-requirements.txt` files are read
154
+ too.
155
+ - A file that a symbolic or hard link gives a second module name is now in
156
+ doubt, and the report names the link. Before, its import-time code could
157
+ run twice.
158
+ - An import that attests nothing, because it is guarded, type-only, or in a
159
+ file that changes `sys.path`, no longer locates a name or puts one in
160
+ doubt; graphene's `setup.py` refused every `--cross-module` run. An
161
+ import of a module the tree lacks no longer makes a stray
162
+ `src/__init__.py` a used package, and a type-only one no longer leaves its
163
+ file unchanged.
164
+ - A formatter that changed or failed on the code it was given ended a
165
+ directory run with exit 1 and nothing written: Black normalizing a
166
+ docstring, Black unable to parse a snippet, ruff exiting nonzero or timing
167
+ out. The proposal is now declined as "the formatter changed its code or
168
+ failed", and the run goes on.
169
+ - A differing value holding a slice outside its own subscript, such as
170
+ numbagg's `data[0, :50]`, became the argument `lambda: (0, :50)`, which is
171
+ not Python. Such a value is no longer made a parameter.
172
+ - An out-of-place run copied a pending journal into its stage and refused
173
+ its own write there; a journal inside the target was published into the
174
+ output. Journals are left out of both, and the run warns, naming the
175
+ project's journal and `towel recover`.
176
+ - A refusal over a pending journal named `towel recover` even where recovery
177
+ would then fail. The remedy now runs recovery's own checks and names what
178
+ works.
179
+ - One hard-linked file failed a whole in-place run at publication, losing
180
+ every other file's refactoring. Proposals that would write it are now
181
+ declined up front, and the rest is written.
182
+ - `--no-interactive` still asked "Analyze anyway?" for an input without a
183
+ `.py` suffix. A crash inside mypy or a plugin was reported only as
184
+ "SystemExit: 2"; it now names the exception and mypy's last output.
185
+ - Under forked evaluation the rejection trace counted some pairs twice, and
186
+ which pairs were duplicates depended on the split between workers. The
187
+ trace is now the serial run's, line for line.
188
+ - The sdist left out 33 test fixture configuration files.
189
+ - Code moved out of functions whose decorators rewrite or compile their
190
+ bodies. typeguard's `@typechecked` recompiles a function from its source
191
+ with checks added, and numba's `@njit` compiles it. The moved code went
192
+ unchecked or stopped compiling, and typeguard's own
193
+ `test_unpacking_assign_star_no_annotation_fail[typechecked]` failed after
194
+ a root run.
195
+
196
+ Code now moves out of a function, or a helper into one, only when
197
+ everything that can reach that code is known to leave bodies alone:
198
+ - decorators on the function and on every enclosing function and class;
199
+ - decorators applied by hand (`fast = numba.njit(kernel)`,
200
+ `f = typechecked(f)`, `njit(cache=True)(kernel)`);
201
+ - the machinery of every enclosing class: its metaclass,
202
+ `__init_subclass__` and `__getattribute__`.
203
+
204
+ "Known" means one of two things. The decorator or base is on a verified
205
+ list, each entry recording the source it was read in: the standard
206
+ library's decorators, pytest's and click's, `rich.repr.auto`, and about
207
+ forty standard-library bases such as `unittest.TestCase`,
208
+ `asyncio.Protocol` and `logging.Handler`, which the suite re-checks by
209
+ introspection on every interpreter. Or it is a project decorator shown to
210
+ return, register or plainly wrap the function.
211
+
212
+ Anything else declines the pair as `decorator_may_transform_body[...]` or
213
+ `class_machinery_may_transform_methods[...]`, naming the culprit. Across
214
+ ten projects, 672 of 721 refactorings remain.
215
+ - With `--cross-module`, a block holding an `assert` is shared only between
216
+ modules pytest rewrites alike, following pytest's own rules and the
217
+ project's pytest configuration. Otherwise a failing assert would lose the
218
+ explanation pytest adds to its message.
219
+ - A `TestCase` subclass can now take a class-private method helper, which
220
+ neither unittest nor pytest collects as a test.
221
+ - An in-place `dry` refused whenever any `.towel-transaction-*` path
222
+ existed under its target. That included an empty directory, one inside
223
+ `.git`, and a journal naming only untouched files, and for the empty
224
+ directory the suggested `towel recover` failed too.
225
+
226
+ A run now stops only for a pending journal that may name a file it would
227
+ change: one whose manifest names such a file, or which has no readable
228
+ manifest. The refusal names the `towel recover` command, or says why
229
+ recovery would not read the journal and what to do instead. `preview`
230
+ and `rename-helpers` warn by the same rule.
231
+ - `towel recover` accepts a journal named through a symbolic link, such as
232
+ one under macOS's `/tmp`, and each refusal says why.
233
+ - A file's project root, coverage exclusions, builtin evidence and
234
+ declared dependencies were looked up at every call site: 12,205 times
235
+ for one input on a benchmark of 50 functions. They are now looked up
236
+ once per input for the run, and so are a function's own locals, keyed by
237
+ block site. CPU time against 1.732 went from 1.71 to 1.27 times on that
238
+ benchmark, and from 1.37 to 1.08 times on rich.
239
+ - A project's own package named `env` or `venv` is refactored. An
240
+ environment is now recognised by `pyvenv.cfg` or `conda-meta`, whatever
241
+ it is called.
242
+ - The rejection trace names each block's file:
243
+ `REJECT[reason]: path::function@(start, end) <-> ...`. A pair the
244
+ extractor cannot render is traced as `unsupported_extraction`, and one
245
+ found while clustering no longer ends the analysis.
246
+ `return_versus_variables` replaces the mislabelled
247
+ `incomplete_return_coverage_block2`.
248
+ - The tests and scripts shipped in the sdist carry the Apache licence
249
+ header, and `scripts/add_copyright_headers.py --check` enforces it.
250
+ - A typed run on a project reached through a symbolic link now verifies its
251
+ changes. On macOS that includes every temporary directory, since `/var`
252
+ is `/private/var`. mypy prints each file by its resolved path, and Towel
253
+ had matched its answers against the path it was asked about. So every
254
+ probe went unanswered, with three consequences:
255
+ - every proposal was declined as code the checker does not look at;
256
+ - every subtype question read yes;
257
+ - the check for names typed as `Any` saw no answers, so a missing module
258
+ could go unnoticed.
259
+
260
+ File identities now compare one resolved path throughout. The test suite
261
+ fails any typed test whose checker declined everything as unlooked and
262
+ verified nothing, unless that decline is what the test is about.
263
+ - A parallel run could hang for ever. Pair-evaluation workers were forked
264
+ while other threads ran:
265
+ - tqdm's monitor, in every run with the default progress bars;
266
+ - the progress heartbeat, from a directory run's second pass;
267
+ - each warm pyright session's reader.
268
+
269
+ A forked child keeps every lock as it stood. So a worker forked while one
270
+ of those threads was writing to stderr waited on stderr's lock when it
271
+ flushed at exit, and the run waited on the worker. Towel now ends its
272
+ threads for the moment of the fork and starts them again after, so
273
+ workers fork from a process with one thread. A thread Towel does not run,
274
+ or one that does not end within 2 s, keeps evaluation in the process, and
275
+ the run says so once, naming it. Python 3.12 and 3.13 no longer warn that
276
+ a multi-threaded process is forking.
277
+ - A block that rebinds a name bound before it is now declined, whatever
278
+ construct rebinds it: a `for` or `with` target, a `match` capture, a
279
+ nested `def` or `class`, an import, or a walrus. So
280
+ `i = -1; for i in xs: ...; i * 2` keeps its `-1` when `xs` is empty. It
281
+ had raised `UnboundLocalError` in the helper.
282
+ - `count += 1` and `del count` after a block now count as reads of `count`.
283
+ The helper returns it, or the block is declined, instead of the later
284
+ statement raising `UnboundLocalError`.
285
+ - A block that reads a local before binding it, such as
286
+ `scale = scale(n)`, is declined where the call site may not have the name
287
+ bound. The call had succeeded there, using the module's `scale`.
288
+ - Blocks equal up to their binders' names now share a helper only where no
289
+ read can find a renamed binder unbound. So an `UnboundLocalError` still
290
+ names each site's own variable.
291
+ - Definite assignment now unbinds a deleted name for everything the
292
+ deletion may precede: the next loop iteration, a handler or `finally`, a
293
+ `with`, `case` or `else` body, and an `except*` clause. Such a name had
294
+ been passed eagerly, and raised before an effect the original ran first.
295
+ - A block that started at the second statement of a `;` line, or ended at
296
+ the first, was spliced by whole lines, which deleted the other statement.
297
+ `a = n + 1; b = a * 2`, with the block at `b`, lost `a = n + 1`, and a
298
+ trailing `; log.append(...)` was lost the same way. The call now replaces
299
+ exactly the block's text. It keeps the rest of the first and last lines,
300
+ including a one-line compound body's header and a backslash continuation.
301
+ - A thunk was passed eagerly although the helper evaluated something first
302
+ that could run code or raise, so its effect moved ahead of that
303
+ something. The cases were:
304
+ - a lambda's default arguments;
305
+ - a set or dict display hashing its keys;
306
+ - `*` and `**` unpacking;
307
+ - a read of a global that raises `NameError`;
308
+ - dead code after `return` or `raise`.
309
+
310
+ A thunk now goes eagerly only after steps on an allowlist of forms, in
311
+ CPython's evaluation order, that can neither run code nor raise.
312
+ - A pair is declined as `directive_on_shared_line` when its block shares a
313
+ physical line with code that stays at the call site, and that line
314
+ carries a tool directive or is excluded from coverage. The directive
315
+ governs the whole line, and splicing in the call would part it from some
316
+ of that code.
317
+ - The import sorter could reorder a file's own imports, and so the order
318
+ their modules run in. With ruff excluding `pkg/app.py`, Towel rewrote
319
+ `from pkg import zeta` / `from pkg import alpha` as
320
+ `from pkg import alpha, zeta`, and a plugin registry's order flipped.
321
+ httpcore's byte-compared `_sync` twins were re-sorted the same way.
322
+
323
+ Sorting now respects each tool's own file selection, judged for the
324
+ project's file rather than the run's staged copy:
325
+ - ruff: `exclude`, `extend-exclude`, `lint.exclude` and
326
+ `per-file-ignores`;
327
+ - isort: `skip`, `extend_skip`, `skip_glob`, `extend_skip_glob` and
328
+ `skip_gitignore`, skipped directories included.
329
+
330
+ A file is sorted only if its original imports are already in the
331
+ sorter's order, and a result is kept only if the file's own imports keep
332
+ their order. So only the imports Towel added can move, and a file left
333
+ unsorted is reported once. `ruff format` and Black respect their
334
+ exclusions too.
335
+ - `# ty: ignore[...]` and `# pyrefly: ignore` were read as plain comments.
336
+ Towel moved the code they silenced into call-site lambdas and left the
337
+ ignores behind. On griffe, its own `ty check` went from 2 to 23
338
+ diagnostics. These are now directives, as are:
339
+ - `# zuban: ignore` and `# nosemgrep`;
340
+ - Fixit's `# lint-ignore` and `# lint-fixme`;
341
+ - ruff's `file-ignore`, `disable` and `enable`;
342
+ - pyrefly's `ignore-errors`.
343
+
344
+ An ignore on a line of its own governs the next line of code, as those
345
+ tools read it.
346
+ - With `--cross-module`, Towel took a project directory named like a
347
+ library it requires for that library, when its own interpreter lacked
348
+ the library. For example, `click/` sharing one module with click, while
349
+ the project depends on `click>=8`. The import it wrote then failed once
350
+ the package was installed. A requirement declared in any of these now
351
+ puts the name in doubt: pyproject.toml, setup.cfg, a uv, Poetry, PDM or
352
+ Pipenv lockfile, or requirements*.txt.
353
+ - With `--cross-module`, a library installed in an environment inside the
354
+ project root, such as the project's own `.venv`, was taken for the
355
+ project's own code. It now counts as installed outside the project.
356
+ - With `--cross-module`, Towel could host a helper in a module the build
357
+ leaves out of the wheel, so the installed package failed to import. It
358
+ now reads the declared exclusions of hatch, setuptools, MANIFEST.in,
359
+ Poetry, PDM, uv, flit and scikit-build-core, and every `.gitignore`. It
360
+ uses them only to put a host in doubt, never to name a module. An import
361
+ inside a function no longer shows that a directory ships.
362
+ - With `--cross-module`, a top-level module inside a package, imported by
363
+ its bare name from beside it, went unrefused. The borrower was given a
364
+ relative import that fails where the original works. It now refuses.
365
+ `from . import x` and `from pkg import x` of a missing submodule are now
366
+ reported, and their file is left unchanged.
367
+ - Each import problem's remedy now fits its kind:
368
+ - `--exclude` for a stray copy in the tree;
369
+ - an environment where the name is this tree, or one without it, for an
370
+ installed copy;
371
+ - a rename, `--exclude` or a dropped requirement, for a required
372
+ distribution.
373
+ - Typed mode now reads per-module `follow_imports` as mypy does. An error
374
+ in a module its own section follows was dropped, and a helper the
375
+ project's mypy rejects was let through.
376
+ - A typed run over part of a project (`towel dry tests tests`) found the
377
+ project's own packages in a stale installed copy, and wrote annotations
378
+ for the installed API. It now finds them in the tree.
379
+ - Probes now name modules as mypy does under `explicit_package_bases`,
380
+ `mypy_path` and namespace packages. A probe build that fails is now a
381
+ checker failure. It had been read as unreachable code, which ended PEP
382
+ 420 projects at a false fixed point with exit status 0.
383
+ - A changed file outside the configured `files` is named as its importer
384
+ names it, so mypy no longer refuses the build with "found twice".
385
+ - A mypy configuration that mypy only warns about no longer refuses the
386
+ typed run, and the warning is passed on. Examples are an unknown option,
387
+ a global option in an override, or a dropped Python version.
388
+ - A typed run accounted for errors on the lines a change wrote by message
389
+ alone. Merging two copies of a block then freed the second copy's errors
390
+ to hide a new error with the same message. A helper typed `int | str`,
391
+ whose `p + p` repeated two pre-existing messages, was accepted, and the
392
+ project's mypy rejected it.
393
+
394
+ Each error is now accounted for by an error of the original that stood
395
+ where it stands:
396
+ - in the helper, the same message at the same statement of one copy of
397
+ the block, each error once;
398
+ - at a call site, its own copy's.
399
+ - A file whose imports the checker cannot resolve is now left alone even
400
+ when the configuration silences that report. Examples are
401
+ `ignore_missing_imports` and pyright's `reportMissingImports = "none"`.
402
+ Every checker is asked what each import binds. A subtype question about
403
+ a type the checker sees as `Any` answers unknown, so it no longer folds a
404
+ union.
405
+ - A file that cannot be decoded is left out of a typed run and left
406
+ unchanged, as without types, instead of refusing the run.
407
+ - Files that pyright's configuration excludes or ignores are known before
408
+ the run, and pyright is not asked about them. That removes a minute-long
409
+ stall. Another checker that checks them settles them, rather than their
410
+ being declined as unreachable. Where no checker covers a file, its helper
411
+ takes only the annotations its sites declare.
412
+ - A class the checker names by its whole path, which the helper's module
413
+ does not import, is now imported under `TYPE_CHECKING` and named
414
+ directly, as documented. It had been written `Any`.
415
+ - A verdict that nothing rebinds a name a block reads was memoized under the
416
+ structure of the block and its function. It then answered for identical
417
+ code whose enclosing function rebinds that name with `nonlocal`, in the
418
+ same module or in another. The name was passed to the helper as an
419
+ argument, and so read before the rebinding.
420
+
421
+ The same key let a top-level copy of a block lend its bindings and escape
422
+ verdict to an identical copy inside a loop. That copy was then extracted
423
+ without returning the variable the loop reads next.
424
+
425
+ Block guards and per-block analyses are now memoized per block site: the
426
+ module's source, and the positions of the function and the block. On
427
+ rich, one core, analysis takes about 6% longer, and the output is
428
+ unchanged. A test runs the analysis with every memo disabled and requires
429
+ the same verdicts, so a cache key that leaves out a dependency fails the
430
+ suite.
431
+ - A block that spells a class-private name only as a parameter, or in an
432
+ import's module or member, was moved out of its class. There the name is
433
+ not mangled:
434
+ - `(lambda __p=0: 7)(__p=y)` raised `TypeError` in a method and returned 7
435
+ in a module-level helper;
436
+ - `import __tool` inside `class A` imports `_A__tool`, but `__tool` in a
437
+ helper.
438
+
439
+ Every position CPython mangles now counts. This is checked against the
440
+ interpreter's own symbol tables for all 37 identifier positions in its
441
+ grammar. A call's keywords are compared with the parameters as the class
442
+ stores them, so the same call is declined even when the helper stays in
443
+ the class, where its `TypeError` would name the helper.
444
+ - A typing name that a helper's annotation needed was imported publicly
445
+ (`from typing import Callable`). That replaced a same-named binding in
446
+ every module that star-imports the host. It also replaced one in the host
447
+ itself, where a star import, a `try` or an assignment had bound the name.
448
+ `case Callable():` then raised `TypeError`, and a package's own `Any`
449
+ became `typing.Any`. On click, rich and packaging, 13 modules gained a
450
+ public name.
451
+
452
+ Typing names are now reached through a private `import typing as
453
+ _typing`, or through the module's own sole `import typing [as t]`.
454
+ - The `from typing import TYPE_CHECKING` written for a new guard rebound a
455
+ module's own `TYPE_CHECKING`, for example
456
+ `from pkg.flags import DEBUG as TYPE_CHECKING`, and turned its flag from
457
+ True to False. A new guard now reads `if _typing.TYPE_CHECKING:`, which
458
+ mypy and pyright both recognise, unless the module's sole binding of that
459
+ name is typing's own.
460
+ - A class imported for the checker is bound under a private alias
461
+ (`from pkg.models import Item as _Item`, spelled `"_Item"`), since
462
+ checkers carry that import into every star-importer.
463
+ - A new guard carries `# pragma: no cover` only where the project's
464
+ coverage exclusions would not otherwise match it, so coverage is
465
+ unchanged.
466
+ - A typing import was skipped when the same line appeared anywhere in the
467
+ file, even inside a function, and the helper's annotation then raised
468
+ `NameError` at import.
469
+ - On Python 3.11 and 3.12, Towel compared the syntax trees it builds with
470
+ parsed ones by `ast.dump`. Before 3.13, `ast.dump` spells a field the
471
+ constructor was not given differently from one set empty. On 3.12 the
472
+ helper's `FunctionDef` has no `type_params`, so nearly every helper was
473
+ declined as "could not be rendered". Other comparisons could also miss:
474
+ - the subtype relation that needs no checker;
475
+ - union normalisation;
476
+ - the instantiation check;
477
+ - structural keys and proposal identities.
478
+
479
+ Each miss cost a refactoring or precision, never soundness. Every such
480
+ comparison now uses one spelling on every supported Python, the one
481
+ 3.13's `ast.dump` writes. On 3.12 this recovers 5 typed refactorings in
482
+ the corpus: packaging 2, mistune 2 and nox 1.
483
+ - The test suite passes on Python 3.11, 3.12 and 3.13. Fixtures in PEP 695
484
+ syntax, and tests of `typing.override`, are skipped where the running
485
+ Python lacks them.
486
+ - The fallback annotations of a typed run are built from the checker's
487
+ answer. Under strict mypy, the rung that made every annotation `Any` had
488
+ verified 4 times in 46, and 32 of its refusals were "Returning Any". Now
489
+ `Any` goes only where the errors point:
490
+ - a parameter read on a failing line;
491
+ - `Callable[[...], Any]` for a thunk whose result is at fault;
492
+ - the parameter an "Argument N" error names.
493
+
494
+ A return is loosened least, to `<declared> | Any`, which is also how a
495
+ helper returning `NotImplemented` is typed. Under mypy's
496
+ `warn_return_any`, the every-`Any` rung is not tried. The unannotated
497
+ helper is not tried under `disallow_untyped_defs`, or in a module whose
498
+ every function is annotated. Such a proposal is declined saying so;
499
+ before, idna's output failed its `mypy --strict` CI with 4 errors.
500
+ - An extraction that no helper signature can type is declined early, under
501
+ a named reason in the run's summary:
502
+ - it narrows what its caller reads after the call;
503
+ - it narrows what a call-site lambda reads;
504
+ - it declares its class's attributes;
505
+ - it completes its caller's partial type.
506
+
507
+ Two of these are decided from the proposal alone, without asking the
508
+ checker.
509
+ - A declined proposal is replayed at a rehearing, rather than checked
510
+ again, only while nothing it could depend on has changed: its rendered
511
+ files, the files its errors lie in and what they import, and the
512
+ baseline's record of known errors. On packaging, checks that exactly
513
+ repeated an earlier one went from 16 to 0.
514
+ - A number of annotation fixes:
515
+ - outside a class, a `Self` the sites revealed becomes a type variable
516
+ bound to their classes;
517
+ - no annotation is written as the string `"None"`, which mypy 2 rejects
518
+ under `native_parser`;
519
+ - a site revealed as `Any` no longer erases another site's type in a
520
+ join;
521
+ - a copied declaration gives way to the narrower type the block saw;
522
+ - a method helper is placed where the attributes it assigns keep their
523
+ declarations;
524
+ - every rung writes only the type-only imports its own annotations use.
525
+ - A class name shortened inside a subscripted annotation (`dict[Item, int]`)
526
+ was left unquoted while `Item` was imported only under `TYPE_CHECKING`.
527
+ mypy accepted it, but the module raised `NameError` on import.
528
+ - Coverage exclusions set in a project's configuration now count the way a
529
+ `# pragma: no cover` comment does. Examples are `if __name__ ==
530
+ .__main__.:`, `raise NotImplementedError` and `@overload` in
531
+ `exclude_lines` or `exclude_also`. Before, only the comment counted, so
532
+ such an entry could move excluded code into a helper that coverage
533
+ measures. Towel reads the configuration as coverage.py 7.16 does: from
534
+ `COVERAGE_RCFILE`, `.coveragerc`, `.coveragerc.toml`, `setup.cfg` or
535
+ `tox.ini` `[coverage:report]`, or `pyproject.toml`
536
+ `[tool.coverage.report]`. Its three default patterns count as well. Where
537
+ `exclude_lines` leaves the pragma out, a `# pragma: no cover` comment
538
+ excludes nothing, so it no longer declines a pair.
539
+ - A type-only import now joins the module's existing `TYPE_CHECKING` guard,
540
+ recognised by binding as the import graph recognises it. Towel had found
541
+ the guard by its exact text, and indented the new import four spaces:
542
+ - packaging's `if TYPE_CHECKING: # pragma: no cover` got a second guard;
543
+ - a guard body indented two spaces or with a tab raised `IndentationError`;
544
+ - in a module that rebinds `TYPE_CHECKING = True`, the import was joined
545
+ to a block that runs.
546
+
547
+ The import now takes the indentation of the guard's own body.
548
+ - A `--cross-module` run refuses only for a problem that leaves a name of the
549
+ code being refactored in doubt. Such problems are:
550
+ - an ambiguous name, such as a stale `build/lib` copy of the package, which
551
+ `--exclude build` still clears;
552
+ - a file reachable under two names;
553
+ - a top-level name found inside a package;
554
+ - a relative import that climbs out of its package.
555
+
556
+ An import of a module the tree lacks refuses nothing, whether the run is
557
+ from the root, on a package or on a sub-package. Such imports had stopped
558
+ every run on sphinx, astroid and prompt-toolkit, and on seven packages
559
+ whose modules import the `_version.py` their build generates, which a
560
+ fresh clone lacks. With the refusal set aside, the package's name was
561
+ flagged, so every helper its modules could share was declined: 3,858 of
562
+ the 3,860 cross-module pairs on sphinx.
563
+
564
+ Over 140 corpus projects, runs on the package refused 14 before and 4
565
+ after. Runs from the root, measured only after the first of these changes,
566
+ refused 24 and now refuse 18. Every remaining refusal is for a name in
567
+ doubt, such as the four module names sphinx's `tests/roots` each defines
568
+ more than once.
569
+ - In a `--cross-module` run, a file making such an import is left exactly as
570
+ it was. It hosts no helper, borrows none, and none of its own duplicates
571
+ are extracted. The run names each such file, with a remedy that fits:
572
+ test data or an example can be left out with `--exclude`, and a package's
573
+ generated module appears once the project is installed. When the file is
574
+ a package's `__init__.py`, that package's modules still share helpers
575
+ among themselves, and the run says in one line that they host none for
576
+ modules outside the package.
577
+ - A run on a sub-package now refuses, instead of going on with every helper
578
+ declined, when a relative import elsewhere in the package climbs out of
579
+ it.
580
+ - An import below a module file names the module it lacks: `util.gone`, not
581
+ `util`, when `util.py` exists. A module file that registers submodules at
582
+ run time, as `six.py` registers `six.moves`, is not reported as missing
583
+ them, wherever it sits.
584
+ - A typed run no longer changes code that the type checker does not look
585
+ at. That is code the checker takes to be unreachable on the platform and
586
+ Python version it checks for, and there it reports nothing. On darwin,
587
+ mypy skips a module guarded by `assert sys.platform == "win32"`, so
588
+ Towel's check of changes there was vacuous. trio's CI, which checks linux,
589
+ darwin and win32, found 14 errors in output Towel had accepted.
590
+
591
+ Every configured checker is now asked, by a `reveal_type` probe, whether
592
+ it looks at the lines a change writes. A change it does not look at is
593
+ declined as not verifiable. The regions are named before the run. trio's
594
+ CI now passes on all three platforms.
595
+ - An import under `if TYPE_CHECKING:` never runs, and the cycle guard no
596
+ longer follows it. Modules that name each other's classes only for the
597
+ checker can now share a helper. A type-only import that Towel writes no
598
+ longer refuses later pairs between the same two modules; on mistune it
599
+ had cost two typed refactorings.
600
+
601
+ The guard is recognised by resolving the name: typing's or
602
+ typing_extensions' `TYPE_CHECKING`, however imported or aliased, or the
603
+ module's own `TYPE_CHECKING = False`. Its `else` branch still counts as
604
+ an import that runs, as do `if not TYPE_CHECKING:` and a shadowed or
605
+ unresolvable name. A module's requirements now include the imports in the
606
+ guard's `else`, which had been dropped.
607
+ - A type variable over classes from two modules now imports the one its host
608
+ lacks, under `TYPE_CHECKING`, spelled as the program's own imports spell
609
+ it. packaging's validation and required-key errors, and mistune's
610
+ renderer `__call__`, are now typed without `Any`.
611
+ - Comments inside a moved block were lost with it, because the helper was
612
+ rendered from its syntax tree. That included tool directives, such as
613
+ asyncstdlib's and mashumaro's `# type: ignore`, as well as
614
+ `# pyright: ignore`, `# noqa` and `# pragma: no cover`, and every
615
+ explanatory comment. A lost directive changes what a checker, linter or
616
+ coverage tool reports.
617
+
618
+ The helper now carries each comment beside the code it was written for,
619
+ in order. It also keeps the grouping parentheses and trailing commas that
620
+ held a list split across lines, so Black and ruff reproduce the source
621
+ layout. Where a formatter would move a directive off its code's line,
622
+ that helper is written unformatted. Comments above or below a moved block
623
+ stay with its call, and `preview` shows the helper's comments.
624
+ - A typed run no longer refuses a project whose type check already reports
625
+ errors. It reports them first, and rejects a change only for an error they
626
+ do not account for:
627
+ - In a file no change touched, an error must match one at the same line.
628
+ - In a file a change touched, the two texts are aligned by a line diff. An
629
+ error on a line the change left alone must match the original's error
630
+ on that line. An error on a line the change wrote must match the
631
+ original's error where that code stood, as described above.
632
+ - A message that names a line fails closed.
633
+ - Each written change's check becomes the reference for the next.
634
+ - The cold confirmation compares the same way.
635
+
636
+ In a study of 20 corpus projects in their own environments, the old rule
637
+ let 5 through. 19 are now refactored with types, for 125 refactorings, 77
638
+ of them in the 14 newly admitted projects.
639
+ - With mypy and pyright both configured, the combined check stopped at the
640
+ first checker that reported anything. Against pre-existing mypy errors,
641
+ pyright was then never asked about any candidate. Every checker is now
642
+ asked until one reports a new error.
643
+ - A file where the original check leaves a name the checker cannot type is
644
+ not changed. Such names come from an unresolved or untyped import, a
645
+ missing stub, an untyped decorator or an `Any` base class. A change there
646
+ would be checked against `Any`, and could not fail. The run names these
647
+ files up front and counts their proposals as not verifiable.
648
+ - The cold confirmation checks the original cold before it treats as new an
649
+ error that only it reports. On trio, pyright's language server and command
650
+ line disagreed about files no change touched, so every typed run failed at
651
+ the end.
652
+ - Only a checker that cannot run still refuses a typed run. The corpus
653
+ harness reruns a project without types only in that case, and records
654
+ every other project's pre-existing errors.
655
+ - Typed runs read every type spelling that mypy 1.x, mypy 2.x and pyright
656
+ print. A regular expression took the last `) -> ` as the end of a
657
+ parameter list, so `lambda: parse_extras` in packaging's `_parser` was
658
+ annotated as the function the lambda returns. Now read correctly:
659
+ - a function returning `None` (`def (builtins.int)`), a function
660
+ returning a function, and a generic function;
661
+ - a named tuple or typed dict, and its constructor;
662
+ - `Union` and `Optional` beside `|`, and a literal whose text holds `<`.
663
+
664
+ Only a literal type the checker inferred is widened, and a declared one is
665
+ kept. `Union`, `Optional` and `Literal` are imported where they are
666
+ written.
667
+ - The type-variable rung recognises the program's own classes. It had
668
+ identified a class by where its import points or where it is defined,
669
+ which never matches the checker's `packaging.version.Version`. So the
670
+ row, and every type variable in it, was lost: rows formed in 13 of 81
671
+ attempts across packaging, rich, mistune and nox.
672
+
673
+ A class is now named by the absolute name the import model gives its
674
+ module. Where the program's imports name no module, the rung uses mypy's
675
+ `__init__` chain instead. Rows now form in 58 of 66 attempts. Along with
676
+ that:
677
+ - a type only a call site can name may stand inside a type variable;
678
+ - `Any` nested in a type is kept;
679
+ - the signature brings its typing imports;
680
+ - a parameter the body never reads is `object`;
681
+ - union members line up by structure;
682
+ - a class passed directly or through a thunk (`lambda: Row`) is
683
+ `type[Row]`, and a returned variable bound to a parameter takes that
684
+ argument's type.
685
+ - Sites in different modules that agree on every type get their common
686
+ signature, naming the host's classes. Before, the ordinary signature wrote
687
+ only builtins and fell back to `Any`, and mistune's block quote and spoiler
688
+ helpers were declined.
689
+
690
+ Together with the ladder and baseline changes above, typed refactorings
691
+ rise as follows, measured on Python 3.12.13 with `--cross-module` from
692
+ `eaa3882` to `336c890`:
693
+
694
+ | Project | Typed refactorings | Project checks per run |
695
+ |---|---|---|
696
+ | packaging | 10 to 20 | 68 to 36 |
697
+ | rich | 18 to 22 | 45 to 30 |
698
+ | mistune | 15 to 19 | 31 to 19 |
699
+ | nox | 5 to 5 | 9 to 7 |
700
+ | idna | 3 to 1 | 5 to 5 |
701
+
702
+ idna's output now passes its CI's `mypy --strict idna`. Every output
703
+ passes its project's own mypy and test suite exactly as the unchanged
704
+ project does, and every module imports as it did.
705
+ - Pyright's language server checks a project as `pyright` itself does. Towel
706
+ sent it a `python.analysis` section without `autoSearchPaths`, which the
707
+ server then leaves off and the command line always sets, so a src
708
+ layout's package was analysed as `src.<pkg>` and consumers outside `src`
709
+ imported the installed copy, which for an editable install is the user's
710
+ own tree. A candidate that removed `edit` from click's exports broke six
711
+ lines of its typing tests and was reported clean, and trio reported 1989
712
+ errors where its own pyright reports 1870. The server's settings are now
713
+ stated in full and match the command line's in every configuration
714
+ tested; on click, trio, jinja2, markupsafe, attrs and structlog the two
715
+ report identical errors. A consumer that imports its package through an
716
+ editable install is judged against the candidate wherever the package
717
+ lives (`python/`, `lib/`, `packages/<name>/src`, or configured
718
+ `extraPaths`), and pyright's command line is told the project it checks,
719
+ so its answers no longer depend on where the temporary directory lives.
720
+ - `s = super; s()` shared by sibling classes was moved into a module
721
+ function, where it raised `RuntimeError: super(): __class__ cell not
722
+ found`; `super()` reached through another name now stays where it is.
723
+ - A frame handle taken earlier in the loop body or branch that holds a
724
+ duplicated block (`frame = sys._getframe()`, read later as
725
+ `frame.f_locals`) no longer lets the block move; the check had stopped
726
+ walking at the block and never saw the handle.
727
+ - A directory run ends when a proposal renders exactly the bytes its files
728
+ already hold. The directory driver had counted such a proposal as applied,
729
+ so the next analysis found it again and, with no iteration bound, the loop
730
+ never stopped; the single-file driver has always had this guard. The
731
+ proposal is now declined and remembered until the project changes.
732
+ - Type probes at adjacent extraction sites retain their lexical scope even when
733
+ they share a source line. Conflicting mypy specialization notes for one probe
734
+ are treated as ambiguous evidence instead of silently retaining the last type.
735
+ - An extraction no longer gives a method a receiver it never needed. A method
736
+ reached through its class is an ordinary call with nothing bound, so
737
+ `Formatter.as_dollars(None, 1.5)` runs for as long as the body reads no
738
+ attribute of `self`, and routing the block it shares through
739
+ `self._extracted_func_0(...)` made that an `AttributeError` while every
740
+ genuine instance kept its answer and no checker said a word. Such a helper,
741
+ and one shared by static methods or by classmethods that never read `cls`,
742
+ is now a module-level function. An intermediate candidate made it a
743
+ `staticmethod` reached as `ClassName._extracted_func_0`, and the class's
744
+ name does not always reach the class: a parameter of that name, `del`,
745
+ `global` rebinding, a mangled `class __C`, a decorator returning something
746
+ else, or a call while the class body still runs each broke it. `__class__`
747
+ would reach it, but mypy does not accept it. Over packaging, click and rich
748
+ nothing is declined; click goes from 11 helpers to 10, one module function
749
+ serving a third class.
750
+ - An import Towel writes names a module the installed project has, because
751
+ its name is taken from the program's own imports instead of computed from
752
+ packaging metadata. Checked against built wheels, the old layout readers
753
+ named 102 files of seven corpus projects wrongly (a `setup.cfg` or
754
+ `setup.py` src layout, never read, became `src.foo.a`), 937 more when a
755
+ project directory shares its package's name (`foo.src.foo.a`), and
756
+ waitress' `src/waitress/task.py` as `src.waitress.task`. Without a project
757
+ mypy configuration the checker named modules from the same root, so it
758
+ accepted those imports, and the adopted output failed with
759
+ `ModuleNotFoundError`. Within a package Towel writes the spelling the
760
+ importing module already uses for its neighbours, or a relative import.
761
+ Across top-level packages it writes an import only where the importing
762
+ package already imports the other one, spelled as it spells it. It enters
763
+ a directory only where its own side already imports from it, so a library
764
+ never borrows from a test package its wheel leaves out. A relative import
765
+ never climbs above the importer's top package. What an import runs, for the
766
+ cycle guard and the import-time checks, is read the same way; the old
767
+ graph had matched filenames case-insensitively (`pygments/Lexer.py`) and
768
+ let eight pygments lexers import others at load time. An import into an
769
+ excluded directory, such as pip's `_vendor`, is treated as unknown rather
770
+ than empty. `rename-helpers` names modules the same way, so it works on src
771
+ layouts. The packaging readers are gone; `--prefer-absolute-imports`,
772
+ `--pep420` and their engine keywords are accepted as deprecated no-ops, and
773
+ `ProjectLayout` is removed. The new reject reason `unproven_import`
774
+ replaces `unknown_layout`.
775
+ - `towel dry TARGET OUT` refactors TARGET inside a private copy of its whole
776
+ project and writes only TARGET to OUT, when the run succeeds. It refactored a
777
+ copy of TARGET alone, so everything read during the run saw a directory with
778
+ no packaging metadata and no rest-of-project: the layout readers were never
779
+ reached (every import came out relative, which hid the defects above), and
780
+ an import cycle or an import-time effect through a module outside TARGET was
781
+ invisible, leaving the adopted package unimportable where the same run in
782
+ place was correct. Out-of-place and in-place runs now make the same
783
+ decisions; staging click, rich and pygments (460 files) takes 0.07 s. A
784
+ project root holding more than 20,000 Python files is refused with a
785
+ message. A failed or interrupted run leaves no OUT behind.
786
+ - The complete mypy check no longer calls a broken consumer clean. The
787
+ consumer scan cached an answer computed with the baseline's packages left
788
+ out and reused it for later sparse requests, so after the first check of
789
+ `towel dry . .` a sibling analysed package went unchecked; it never noticed a
790
+ file an applied refactoring had made into a consumer; it named a PEP 420
791
+ module `lib` where its importers say `nsp.lib`; it read `from app import
792
+ helpers` as an import of `app` alone; and it resolved an `__init__`'s
793
+ relative imports against the package's parent. It now keeps the whole import
794
+ graph, re-reads only files whose stamp changed, and applies exclusions per
795
+ request; at its file limit it fails the check instead of returning a partial
796
+ list. A mypy run that applied anything ends with one complete check from an
797
+ empty cache and a fresh scan, as a pyright run already did (0.6 s on click).
798
+ An error mypy reports without a line is counted, and a crashed worker's
799
+ standard error is quoted.
800
+ - A helper is placed only where it stays a plain member. Never in a
801
+ `Protocol`, where it became a protocol member and turned runtime-checkable
802
+ `isinstance` checks false; never in a class written on one line
803
+ (`class E(Exception): pass`), where it was written at column zero; and only
804
+ in a class, or through a base, whose every decorator is known to keep the
805
+ namespace (`dataclass`, `total_ordering`, `final`, `enum.unique`), since a
806
+ decorator that rebuilds or wraps it dropped or wrapped the helper. A module
807
+ helper is no longer placed after an assignment, decorator or class body that
808
+ could call it at import.
809
+ - A borrower is never made to import a host that requires a module the
810
+ borrower does not: `import tornado` is inert as a statement, but a helper
811
+ hosted in gunicorn's tornado worker made the sync worker unimportable
812
+ wherever tornado was absent. The standard library, declared dependencies,
813
+ and imports inside `try` require nothing. Over rich, click, packaging and
814
+ pygments the output is byte-identical.
815
+ - A call site whose generated thunk would read a local that may be unbound is
816
+ declined: the thunk raised `NameError` where the original raised
817
+ `UnboundLocalError`, and `except UnboundLocalError` stopped matching.
818
+ - Generated helper names avoid every name another source under the project
819
+ root defines as a class member or attribute, so a subclass outside the
820
+ target can no longer override a new helper.
821
+ - An escape in a string literal survives extraction into a file whose
822
+ encoding cannot hold the character it denotes. Rendering spells a constant
823
+ by value, so `"\u20ac"` in a Latin-1 file came back as `€`, which the file
824
+ cannot hold, and the run aborted. The escape is now written back, inside
825
+ the literal only and kept only when the syntax tree is unchanged by it; a
826
+ character no escape can spell is declined instead. A file nested too deeply
827
+ to analyze is skipped with a warning instead of ending directory mode with
828
+ `RecursionError`.
829
+ - A run whose every candidate the type checker could not judge (timeout or
830
+ crash) exits non-zero and says so, instead of printing "No refactorings
831
+ found!"; a run that applied some reports how many went unjudged. The summary
832
+ shows the run's actual termination reason.
833
+ - `rename-helpers` refuses a batch that would leave an import of the old name
834
+ behind, as it did on an out-of-place output whose imports name the package
835
+ rather than the output directory. Rename after adopting.
836
+ - A project whose own mypy configuration excludes files (`exclude`, or `files`
837
+ not naming them) is no longer refused for errors in files its own mypy run
838
+ never checks; an excluded file that checked code imports is still checked.
839
+ - A block passes the caller's names that only a definition inside it reads
840
+ where it stands: defaults and keyword-only defaults, decorators and
841
+ annotations, class bases, keywords and bodies. The helper raised
842
+ `NameError` before its first call. A bare `x: int` no longer counts as
843
+ binding `x`, which raised `UnboundLocalError`.
844
+ - A `match` pattern's class, dotted value and mapping keys are read from the
845
+ caller, and nothing in a pattern becomes a parameter except a class
846
+ pattern's class or the root of a dotted name. `case [1, x]` against
847
+ `case [2, x]` had become a capture that matched every pair, and
848
+ `case Color.RED` a class pattern that raised `TypeError`.
849
+ - A `type` statement's value and a type parameter's bound are closures
850
+ evaluated when first read, and are declined wherever the caller or the
851
+ block rebinds what they read, as lambdas are.
852
+ - A block is declined when a function, lambda, generator or class it creates
853
+ could be observed other than by calling it. Such an object would carry the
854
+ helper's `__qualname__`, and a unified lambda the template's parameter
855
+ names. It may still be called in the block, be the `key=` of `sorted`,
856
+ `min` or `max`, or be the function of a `map` or `filter` consumed there.
857
+ Rich makes 27 helpers instead of 28; packaging, click and pygments are
858
+ unchanged.
859
+ - Constants that compare equal but differ in type (`0`, `0.0` and `False`;
860
+ `1` and `True`) are different constants, extracted as arguments where the
861
+ instantiation check used to decline them. Pygments makes 74 helpers
862
+ instead of 72, and its lexers and formatters produce identical output over
863
+ 12,679 observations.
864
+ - An integer literal too wide for decimal conversion no longer ends the run;
865
+ a block holding one of more than 640 digits is declined.
866
+ - A checker the project configures that is not installed refuses the typed
867
+ run before anything is written, naming the checker and its configuration.
868
+ It had been replaced by the other checker, or by none after a one-line
869
+ note, so a pyright-only project was refactored unverified with exit
870
+ status 0.
871
+ - mypy checks with the project's configured plugins, loaded as its own mypy
872
+ loads them; a plugin that cannot load refuses the run, quoting mypy's
873
+ error. Without them Towel accepted a helper the project's own mypy
874
+ rejected.
875
+ - A module that ships its own stub is checked through the stub, as mypy
876
+ checks it, so an importer of a name the implementation gained and the stub
877
+ lacks is refused rather than accepted.
878
+ - An in-place run refactors a private copy of the project and writes the
879
+ result once, as one journaled change, after its final confirmation
880
+ succeeds. A refused confirmation or an interruption leaves the project
881
+ unchanged, and a file edited during the run refuses the write instead of
882
+ losing the edit. Over click the output is byte-identical and 1.4% slower.
883
+ - A pyright configuration is read with pyright's own grammar, so a file
884
+ pyright rejects refuses the run before anything is written, naming the
885
+ file and position; the language server would otherwise have checked with
886
+ default settings. pyright's failures quote its standard error, and its
887
+ command-line probes are written only into Towel's private copy.
888
+ - A generated annotation that uses syntax newer than the project's oldest
889
+ Python (`int | None` before 3.10, `list[int]` before 3.9) is written as a
890
+ string in a module that does not postpone annotations. The oldest Python
891
+ comes from `requires-python`, else mypy's `python_version` or pyright's
892
+ `pythonVersion`. A project declaring `>=3.9` got a helper that raised
893
+ `TypeError` at import under 3.9.
894
+ - `dry` reports what it declined, counted by reason, instead of "No
895
+ refactorings found!" alone; a type-check refusal is no longer reported as
896
+ "could not be rendered"; and the `.towel-helpers.json` sidecar lists only
897
+ helpers present in the output.
898
+ - A cross-module helper no longer reads a builtin that one of its modules may
899
+ hold differently. The helper had read builtins in its host, so `len`
900
+ shadowed only in the borrower was read as the builtin, and a test patching
901
+ `open` into the borrower (`mock.patch("pkg.mod.open", ..., create=True)`,
902
+ the documented idiom) stopped reaching the moved code. The pair is now
903
+ declined when a participating module binds the name, may bind it through a
904
+ star import, rebinds `__builtins__` or writes its own namespace at run
905
+ time, or when the project's code or tests patch the name into one of them
906
+ (`mock.patch`, `patch.object`, `monkeypatch.setattr`, and targets they
907
+ compute). No helper takes a builtin as a parameter. A patch applied from
908
+ outside the project is not seen. A relative import inside an extracted
909
+ block stays within its own package.
910
+ - No helper takes a builtin as a parameter within one module either. Where
911
+ one function binds `len` (`second(rows, name, len=...)`) and its duplicate
912
+ reads the builtin, the helper had taken `len`, and the first function
913
+ passed it the builtin; a block reading the builtin had also joined a
914
+ helper whose other sites passed their own `len`, and `__import__` or a
915
+ tuple of builtins could be passed as a value. Each is declined
916
+ (`builtin_argument`); where both functions bind the name, each still
917
+ passes its own.
918
+ - One predicate now decides what importing a module runs, for choosing a
919
+ host and for placing a helper. Decorators, defaults, annotations,
920
+ metaclasses and `__init_subclass__` count as code; a `TYPE_CHECKING` block
921
+ and a short list of callables verified at run time do not. A registration
922
+ decorator in the host had run when the borrower was imported.
923
+ - A helper is hosted only in a module that ships with the module importing
924
+ it, never in `tests/` or `examples/` for the package to import. A module
925
+ with a stub (`.pyi`, `typings/`, `<pkg>-stubs`) never hosts a cross-module
926
+ helper, and a module that can run as a script (`__main__.py`, a main guard
927
+ in any form, a `#!` line) stays runnable by its path. Dependencies
928
+ declared by Poetry or in setup.cfg `install_requires` count as declared.
929
+ - One existing function is never rewritten to call another. When duplicates
930
+ are whole function bodies, every site calls one new helper, so patching
931
+ `mod.f1` in a test no longer changes `f2`. `reuse_existing_functions` no
932
+ longer has any effect.
933
+ - A class keeps a method helper only when its receivers are unannotated or
934
+ annotated as the class, `Self` or a type variable bound to it, and its
935
+ metaclass and every `__init_subclass__` it runs are known to leave an
936
+ added function alone. Otherwise the helper is a module-level function
937
+ taking the receiver.
938
+ - A helper goes into a class only when every duplicate it replaces is a
939
+ method of that class; blocks shared by different classes become
940
+ module-level functions taking the receiver. Towel had hoisted method
941
+ helpers into ancestors that held none of the code, among them click's
942
+ `UsageError`, rich's `JupyterMixin`, packaging's `BaseSpecifier` and
943
+ pygments' `Formatter` and `Lexer`. These are public bases that other code
944
+ subclasses, where any subclass could take the helper's place
945
+ (`docs/DECISIONS.md` gives the count measured when the rule was chosen).
946
+ Whether such a function belongs in a class is left to whoever reviews the
947
+ output (see `docs/DECISIONS.md`). pygments makes 71 refactorings instead
948
+ of 77 and rich 30 instead of 33; click and packaging are unchanged, and
949
+ all four behave identically on smoke programs.
950
+ - A method helper is class-private (`__extracted_func_N`, stored as
951
+ `_Cls__extracted_func_N`), so no subclass in or outside the project can
952
+ override it; a class that answers unknown attribute names through
953
+ `__getattr__`, as tinydb's `Query` does, keeps answering the helper's old
954
+ name. A class whose hierarchy defines `__getattribute__` gets a
955
+ module-level helper instead, since it intercepts every lookup.
956
+ - `rename-helpers` renames a class-private helper by its class
957
+ (`path.py:Class.__extracted_func_N`) to another class-private name; a
958
+ non-private new name, or the stored name spelled elsewhere, refuses the
959
+ batch. `reuse_existing_functions` is still accepted, as a documented
960
+ no-op.
961
+ - A project that configures no mypy is checked as its own `mypy` would check
962
+ the same files, with mypy's defaults. Towel had forced
963
+ `check_untyped_defs`, `ignore_missing_imports` and `explicit_package_bases`
964
+ on it, and each changed the verdict: dacite, clean under `mypy .`, was
965
+ refused for 53 errors inside unannotated tests, and a root-relative
966
+ `src.foo.a` import was accepted. Probes that infer a helper's types still
967
+ check unannotated bodies, in a cache of their own. Such a project is now
968
+ refused a typed run wherever its own `mypy` fails; configure mypy or pass
969
+ `--no-types`.
970
+ - A check resolves a module's imports where the project's own mypy does. The
971
+ project root had been searched first, so a test's `import helpers` was
972
+ checked against a root `helpers.py` instead of the one beside it.
973
+ - A literal that a tool reads without running the program stays where the
974
+ tool reads it. python-statemachine's `_("There should be exactly one
975
+ initial state: {!r}")` had become `_(__param_1)`, and pybabel extracted
976
+ neither message; click had lost four of its 71 the same way. The message
977
+ and context arguments of Babel's, Django's and Flask-Babel's markers, and
978
+ of keywords a project configures, are never parameters; nor are the names,
979
+ fields and types of the functional typing forms, `Literal[...]`, or the
980
+ type argument of typing's `cast` and `assert_type`, where no annotation
981
+ makes a parameter acceptable to mypy or pyright. A typing form is
982
+ recognized by what its callee is bound to, however it is spelled
983
+ (`from typing import TypeVar as TV`, `typing.cast`, `typing_extensions`,
984
+ a project module that re-exports it), so a project's own `cast`, such as
985
+ sqlglot's `exp.cast(column, to)` or SQLAlchemy's, stays an ordinary call.
986
+ Only click changes over the four reference packages: 10 refactorings
987
+ instead of 11, with every message still extracted.
988
+ - A helper that computes nothing is no longer extracted, whatever
989
+ `skip_trivial_helpers` says: two unrelated `return` statements had become
990
+ `def _extracted_func_24(__param_0, self): return __param_0`, which rich's
991
+ `Tag.markup` and `MofNCompleteColumn.render` called across two modules;
992
+ packaging and pygments had one each, and those are exactly what is gone.
993
+ - The fixed point ends for a stated reason rather than an incidental one. On
994
+ sqlglot it once extracted 597 helpers, each forwarding to the previous one
995
+ with its tuple permuted, until ordinary SQL raised `RecursionError`. A
996
+ helper that only calls generated helpers and repacks their results is now
997
+ declined in every configuration, forwarding is recognized whatever order
998
+ results come back in, and a lambda handed on whole is passed through
999
+ rather than wrapped again. With the escape guard switched off, sqlglot's
1000
+ DuckDB generator went from no fixed point in 900 s to one in 18 s.
1001
+ - A base-class name is resolved as the binding in effect where the class
1002
+ statement runs, not by finding a class of that qualname anywhere in the
1003
+ file. Python binds globals as a module executes, so `Base = object`
1004
+ written between two subclasses gives them different bases. The checks on
1005
+ a host class's bases (its metaclass, `__init_subclass__` and
1006
+ `__getattribute__`) therefore read the base the class really has. A
1007
+ binding that cannot be established there counts as unknown, and the
1008
+ helper is a module-level function.
1009
+ - A copied annotation is no longer evaluated a second time. An annotation is an
1010
+ expression, and the copy runs at the helper's own `def`:
1011
+ `Annotated[int, mark('a')]` called `mark` again at import, and an annotation
1012
+ the source had quoted was unquoted into one that called it for the first
1013
+ time. An annotation is written bare only when evaluating it runs none of the
1014
+ program's own code; anything holding a call, a lambda, a comprehension, a
1015
+ conditional or an f-string is written as a string, which a checker reads
1016
+ identically and the interpreter never evaluates.
1017
+ - A complete project check covers the modules that import the change. mypy
1018
+ follows imports out of the files it is given and reaches no caller, so a
1019
+ subclass in another package, unchanged and never imported by the package it
1020
+ extends, was broken by a helper whose name it already used while the check
1021
+ reported clean. Those modules are found by one `ast` pass per project. The
1022
+ project root is not walked in its place: repositories hold files no checker
1023
+ can build, and one of them fails the build and refuses the project.
1024
+ - A rejected candidate no longer answers for the project that follows it.
1025
+ Its text was checked, so mypy cached each of its modules against the real
1026
+ file's mtime and size, which mypy trusts without hashing. Those paths were
1027
+ given text again only when the next request named them, and the next request
1028
+ is sparse, so a provider left speculatively returning `str` went on answering
1029
+ `str` while its file returned `int`. Such a path is now given what its file
1030
+ holds, named or not. On Sphinx this costs 3.5% and changes no output.
1031
+ - Two unrelated files that infer one module name are no longer collapsed into
1032
+ one, which dropped the second file's errors and reported the project clean.
1033
+ Only a stub and the implementation beside it collapse; anything else keeps
1034
+ mypy's duplicate-module refusal.
1035
+ - A project whose checker cannot start at all -- a config naming a Python
1036
+ version mypy has dropped -- is told how to proceed, as a project with
1037
+ pre-existing errors already was.
1038
+ - The mypy worker's cost per request no longer grows over a run. Successive
1039
+ `build.build` calls in one process keep every rechecked module's tree alive
1040
+ (mypy 1.19; about 250,000 objects per build of `sphinx.application`), and
1041
+ each build opens with a full collection over all of it, so requests slowed
1042
+ linearly and a run quadratically: 14 s per request and a 14 GB worker after
1043
+ 80 minutes. Each build now runs in a forked child that exits when it has
1044
+ answered. The same 117 recorded requests take 86.5 s instead of 855 s, at a
1045
+ flat 0.75 s and 60 MB, with identical diagnostics.
1046
+ - A complete project check no longer makes mypy recheck every analyzed module.
1047
+ mypy consults its cache only for a module it reads itself, and every module
1048
+ was supplied as text. Text a file already holds is now withheld. A path that
1049
+ has once been supplied with differing text stays supplied for the life of
1050
+ the cache, because mypy records the file's mtime and size beside the text's
1051
+ hash and would otherwise answer for the file with its verdict on the text.
1052
+ - The Pyright language server's silence is taken as a verdict only after it
1053
+ has answered for the change. A server slow to begin is as silent as one that
1054
+ has finished, and a candidate that broke three consumers read as clean when
1055
+ the server was delayed past the 0.35 s quiet period. Each check now carries a
1056
+ marker the server must publish first.
1057
+ - A candidate is checked against the project as it now stands, which is
1058
+ decided by the content of each checker input rather than by its size and
1059
+ timestamp, so an edit by something other than Towel cannot leave a verdict
1060
+ standing against a project the copy no longer matches. The language
1061
+ server's private copy restored every file a candidate did not supply to the
1062
+ bytes it first saw, so in-place, single-file and library runs lost every
1063
+ refactoring already applied: a valid follow-up was rejected and a breaking
1064
+ candidate accepted. The copy now follows the project and tells the server
1065
+ exactly which files were created, changed or deleted.
1066
+ - A supplied-text record cut short by an interrupted append is repaired instead
1067
+ of failing every later request, and stopping the mypy worker stops the build
1068
+ it was waiting for.
1069
+
1070
+ - An extraction never separates a narrowing test from code that depends on it.
1071
+ A checker narrows a variable along a control-flow region, so moving the test
1072
+ into a helper while an expression that needs it stays at the call site left
1073
+ each half well typed and the pair not. Declined where the proposal is built,
1074
+ before any checker is asked, so the same extractions happen whether or not
1075
+ type checking is on.
1076
+ - A method's receiver is no longer annotated from its call sites. It was the
1077
+ union of the classes that happened to call it, which a checker rejects,
1078
+ since an explicit receiver must be a supertype of its own class, and which
1079
+ understated the method's domain in any case: a helper on a base class is
1080
+ inherited by every subclass. One such annotation was enough to send a whole
1081
+ helper down to the all-`Any` rung, so signatures that had lost every type
1082
+ recover them.
1083
+ - A class passed to a helper is typed `type[C]`. A checker shows a reference
1084
+ to a class as its constructor's signature, which reads as something merely
1085
+ callable and is rejected where a type is wanted, by `isinstance` among
1086
+ others.
1087
+ - An inferred annotation that names a class the module cannot reach imports it
1088
+ under `TYPE_CHECKING`. An ordinary import would often close a cycle, the
1089
+ extraction having just made the other module import this one, and a name
1090
+ wanted only by an annotation need not exist at run time.
1091
+ - An imported helper is named after the project it belongs to, not after the
1092
+ directory a run is staging its output in. A module named after a scratch
1093
+ path cannot be resolved by the checker and breaks as soon as the reviewed
1094
+ output is adopted into the place it was written for.
1095
+
1096
+ ### Changed
1097
+ - The default suite now runs the round-3 semantic auditor's generator of
1098
+ near-duplicate code, seeded: 406 cases in about 45 s. The hostile
1099
+ batteries hold every case that audit reported as a P1, each pinned in the
1100
+ state its fix leaves it, and a sample of the cases it found sound.
1101
+ `just fuzz` runs the generator over thousands of fresh seeds as a
1102
+ release-candidate step, and writes each failure as a hostile fixture
1103
+ ready to commit.
1104
+ - A coverage configuration that coverage.py could not read is reported once
1105
+ per project root, and coverage.py's defaults are used in its place.
1106
+ - A pair whose tool directives cannot move soundly is declined:
1107
+ - directives that differ between its sites (`directives_differ`): the
1108
+ helper has one line where the sites had several, so an ignore only one
1109
+ site needed would either silence the other or be lost;
1110
+ - any tool directive that reaches code, other than a name or a literal,
1111
+ which would become a call argument (`directive_on_argument`). Checker,
1112
+ linter, coverage, bandit and formatter directives all count. The
1113
+ argument would sit outside the directive's reach. jinja2's `as_const`
1114
+ extraction had put two `attr-defined` errors at its call sites this way.
1115
+ No directive is copied onto a call;
1116
+ - a region or file directive that would reach past the moved code
1117
+ (`directive_outlives_block`);
1118
+ - a coverage pragma or pylint `disable` around every site's block that
1119
+ would not reach the helper (`directive_around_block`). A module helper
1120
+ for pygments' `__main__`-only functions took `_lua_builtins.py` from
1121
+ 100% to 40% line coverage;
1122
+ - a block whose first statement coverage excludes, since the call that
1123
+ replaces it would be measured (`excluded_block_start`).
1124
+
1125
+ Explanatory comments from every site are kept, the first site's first.
1126
+ - Helpers are shared across modules only with `--cross-module`
1127
+ (`cross_module_helpers=True` for library use), because a user
1128
+ deduplicating a package may not expect Towel to add imports between its
1129
+ modules. By default duplicates pair only within a module, the pair budget
1130
+ is spent only on pairs a run can form, and Towel writes no import between
1131
+ the project's modules that runs; an annotation that needs another module's
1132
+ type still gets a type-only import under `if TYPE_CHECKING:`. With
1133
+ `--cross-module`, `dry` and `preview` name the import problems they meet,
1134
+ with the `--exclude` remedy, and refuse a run when one concerns the package
1135
+ being refactored, such as a stale `build/lib` copy of it; `preview` accepts
1136
+ `--exclude`. Over click, rich, packaging and pygments, the default drops
1137
+ exactly the cross-module helpers (0, 3, 5 and 13 of them).
1138
+ - Code that calls zero-argument `super()` is extracted into a method helper
1139
+ of the class that holds both duplicates, where it resolves as it did,
1140
+ diamonds and subclass overrides included: the helper has the class's own
1141
+ `__class__` cell and the method's receiver. Where no such helper can be
1142
+ written the pair is declined, never given a module function
1143
+ (`needs_class_body`, `super_in_call`). Output over click, rich, packaging
1144
+ and pygments is unchanged.
1145
+ - A long run says what it is doing. Verifying a proposal the project rejects
1146
+ advances no counter, so the progress bar could stand still for minutes,
1147
+ which is indistinguishable from a hung run; the display is now redrawn while
1148
+ a proposal is being weighed and names the proposal it is weighing.
1149
+ - `towel dry` reports what a typed run involves before asking to proceed: how
1150
+ many files a candidate is verified against, how many third-party packages
1151
+ their import graph pulls in, and that the number of checks depends on how
1152
+ many proposals the project rejects. It states no duration, that being
1153
+ governed by the rejections and so unknown in advance.
1154
+ - Pyright is consulted through one long-lived language server per project and
1155
+ a persistent private copy, instead of a fresh `pyright --outputjson` and a
1156
+ fresh copy per check. The command line remains the fallback and reaches the
1157
+ same verdicts.
1158
+ - Every configured checker must accept a candidate, so the first rejection is
1159
+ the verdict and the remaining checkers are not asked. Verdicts are remembered
1160
+ per candidate and project state.
1161
+ - An unannotated helper is checked only when every error of the all-`Any`
1162
+ rejection lies inside the helper's own definition. To a caller the two are
1163
+ the same function, so an error anywhere else survives the change.
1164
+ - A proposal the project declines is remembered for the whole run instead of
1165
+ until the next analysis. When nothing more applies and something was
1166
+ declined that the project has changed under since, the run holds a
1167
+ rehearing: the memory is cleared and the whole project analysed once more,
1168
+ so every declined proposal is heard against the project as it now stands.
1169
+ Only a rehearing that applies nothing ends the run, which is a stronger
1170
+ guarantee than before, when the terminating analysis was restricted to the
1171
+ files that had changed. Of the 382 extractions an uncapped Sphinx run
1172
+ considers, 24 had every annotation variant refused and not one of those was
1173
+ ever accepted at a later analysis.
1174
+ - A typed run that was checked through a language server is confirmed once
1175
+ more at the end by a pyright started from nothing, sharing none of the
1176
+ session's state. A session that fails part way through a run still earns
1177
+ that confirmation, being exactly when the verdicts already given are worth a
1178
+ second opinion, and a confirmation that cannot run refuses the run rather
1179
+ than reporting success. A language server answers when it has gone quiet, and
1180
+ while a marker it must publish first keeps silence from being read as a
1181
+ verdict before it has begun, one cold check over the finished project turns
1182
+ any residue of that kind from a silent wrong answer into a loud one. About
1183
+ ten seconds on Sphinx, and no flag: a guarantee that holds only when asked
1184
+ for is not one.
1185
+
1186
+ ### Preserving type relationships
1187
+
1188
+ An extracted helper used to lose the relationships among its types. Where two
1189
+ call sites passed `int` and `str`, the shared parameter became `int | str` and
1190
+ so did the return, which says nothing about the two travelling together: the
1191
+ helper could be read as taking an `int` and returning a `str`. Towel now
1192
+ anti-unifies the types alongside the code, so the helper's signature carries
1193
+ the correlation the call sites had.
1194
+
1195
+ ### Added
1196
+ - A library API for errors and reports:
1197
+ - exceptions in `towel.unification.exceptions`, based on `TowelError`:
1198
+ `CheckerUnavailableError`, `UntypeableExtraction` (with `.reason`, an
1199
+ `Untypeable`, and `.detail`), `UnverifiableChangeError` and
1200
+ `UncheckedCodeError`, `AmbiguousImportsError` and
1201
+ `ProjectScanLimitError`; `UnsupportedLayoutError` is kept, though no
1202
+ longer raised;
1203
+ - `engine.run_report`, a frozen `RunReport` whose `declined_pairs` and
1204
+ `declined_proposals` count declines by reason; `engine.declined_pairs`
1205
+ for the latest analysis; and `engine.checker_failures`, how many
1206
+ proposals the last run dropped because the checker could not run.
1207
+ - `--parameterize-builtins` (`parameterize_builtins=True`), off by default:
1208
+ where a builtin that duplicated code reads may differ between its sites (a
1209
+ function's own `len` against the builtin, or with `--cross-module` a module
1210
+ that may hold the name), each site passes its own binding to the helper as
1211
+ a parameter instead of the pair being declined. A builtin every site reads
1212
+ alike is still read directly. In typed code the parameter keeps the
1213
+ checker's signature where it can be written, else `type[C]` for a class
1214
+ (`type[str]`) or `Callable[..., R]` for a callable (`Callable[..., int]`
1215
+ for `len`).
1216
+ - Extracted module-level helpers can preserve relationships among argument and
1217
+ return types through type anti-unification. Nested containers, multiple type
1218
+ parameters, and supported existing generic binders receive fresh helper type
1219
+ parameters. Concrete disagreements can use constrained `TypeVar` declarations
1220
+ when an unrestricted generic body does not type-check.
1221
+ - Generic signatures, their imports, and their declarations are checked with
1222
+ the complete prospective project and committed as one transaction. Failed
1223
+ attempts leave no declarations behind. Generated syntax remains compatible
1224
+ with Python 3.11; existing PEP 695 input requires Python 3.12 or newer.
1225
+ - Generic instance and class helper methods preserve argument/result
1226
+ relationships and retain the host's type parameters. A module-level helper
1227
+ shared by methods that never read their receiver infers fresh parameters
1228
+ from its explicit arguments. Fresh declarations precede the host class at
1229
+ module scope, preserving descriptor behavior and declaration rollback.
1230
+
1231
+ ### Fixed (release harness)
1232
+ - The corpus runs Towel the way a user runs it: as `towel` inside each
1233
+ project's own environment. That environment holds the project installed
1234
+ editable from the tree under test, Towel's `format` and `types` extras,
1235
+ and one candidate wheel, checked against `--towel-src` in every
1236
+ environment. mypy, pyright, Black, ruff and isort are taken at the
1237
+ versions the project pins: its lock file first, then the `rev` of the
1238
+ tool's `.pre-commit-config.yaml` hook, then an exact pin in a requirements
1239
+ file (httpx's `requirements.txt` has `mypy==1.17.1`). The environment also holds whatever the project declares its
1240
+ own type check needs: typing-named dependency groups and extras, the
1241
+ dependencies of tox environments and nox sessions that run a checker, its
1242
+ pre-commit mypy and pyright hooks' `additional_dependencies`, and
1243
+ requirements files named for typing. That install only adds; nothing
1244
+ already there may change. Towel had run in the harness's own interpreter,
1245
+ which held Towel's tools and none of the project's dependencies. mypy
1246
+ therefore reported a project's third-party imports as errors: packaging's
1247
+ 1158 errors are 14 in its own environment and none once its typing
1248
+ dependencies are installed, and its typed run now completes. The import
1249
+ model found the tools' own copies of click and packaging, and called those
1250
+ projects' names ambiguous. Each result records the interpreter, the
1251
+ candidate, every tool's version and what chose it, and what was installed
1252
+ for typing.
1253
+ - The project's editable install follows the tree each test run exercises:
1254
+ the clone for the baseline, the refactored copy for Towel and the run
1255
+ after it, and the original package for a retest of the original. A test
1256
+ that imports the installed copy therefore tests the same code as the rest
1257
+ of its run. The run after refactoring had reached the unrefactored clone
1258
+ that way, so a regression there could pass as flaky.
1259
+ - Every corpus refactor passes `--cross-module`. A manifest entry turns it
1260
+ off only with a `cross_module_reason`, and the summary names each such
1261
+ project.
1262
+ - The sdist no longer ships a built wheel: `recursive-include scripts *`
1263
+ picked up the corpus image's copy whenever one had been built there.
1264
+ - Two runs that failed *different* tests can no longer both count as a PASS. A
1265
+ failure line is `FAILED <node id> - <message>`, and the id was taken by
1266
+ splitting at the first `" - "` -- which a parametrized id may contain, so
1267
+ `test_case[same - before]` and `test_case[same - after]` both truncated to
1268
+ `test_case[same`. The harness compares those sets to award PASS, so a failure
1269
+ Towel introduced could hide behind a pre-existing failure in the same
1270
+ parametrized test. Only a separator outside brackets ends the id now.
1271
+ - A phase that times out takes its descendants with it. Killing the immediate
1272
+ process left a suite's own workers, a server it started or a build it spawned
1273
+ running, writing into the scratch tree that the project's later phases read.
1274
+ Each phase leads a process group ended with it, and the worker watchdog ends
1275
+ those groups too.
1276
+ - The corpus runs the default type policy and reruns without types only the
1277
+ projects Towel declines to verify, naming them and their reason in the
1278
+ report; their verdicts are evidence about the untyped path alone. The refusal
1279
+ is first held to its wording -- the count, a diagnostic naming its file, and
1280
+ the way forward -- and one that fails that is a verdict of its own rather
1281
+ than a project quietly skipped.
1282
+ - The ecosystem check gives each corpus project its own output directory.
1283
+ Towel writes its recovery journal to the common parent of the files a
1284
+ transaction changes and refuses to start beneath a pending journal that may
1285
+ cover its targets. A one-module project's single output file put that journal
1286
+ directly in the shared work directory, an ancestor of every other project, so
1287
+ concurrent projects refused each other at `--workers 4`. The runtime is
1288
+ unchanged; its refusal was correct.
1289
+
1290
+ ### Notes
1291
+ - Towel keeps a precise ordinary signature when one verifies against the whole
1292
+ project, and reaches for a generic signature only when that ordinary
1293
+ signature does not. A generic candidate is therefore an alternative to losing
1294
+ the annotation, not a replacement for an exact one.
1295
+ - Declined by design, with no weakening of the annotations that do ship: a
1296
+ helper hosted inside a function, variadic type parameters, a source method
1297
+ whose `self` or `cls` carries an explicit annotation, and evidence that
1298
+ resolves to `Any` or an unknown name.
1299
+ - A fresh parameter whose bound mentions another helper parameter (`U` bounded
1300
+ by `list[T]`) is declined; an ordinary module `TypeVar` bound cannot depend on
1301
+ another function's binder. Where a constrained generic function makes mypy
1302
+ report several specializations for one probe, the evidence is treated as
1303
+ ambiguous rather than resolved arbitrarily. Both are recorded in
1304
+ [the type-parameter design](docs/proposals/type-parameters.md).
1305
+
1306
+ ## [1.732.post1] - 2026-09-19
1307
+
1308
+ ### Fixed
1309
+ - README documentation links use absolute URLs pinned to the release tag, so
1310
+ they resolve from PyPI as well as GitHub. The Python implementation is
1311
+ unchanged from 1.732.
1312
+ - Regression checks reject repository-relative README links and stale release
1313
+ tags in documentation URLs. The release procedure verifies GitHub source,
1314
+ documentation, and CI before uploading packages to PyPI.
1315
+
1316
+ ## [1.732] - 2026-09-19
1317
+
1318
+ Changes since 1.618.
1319
+
1320
+ ### Added
1321
+ - A duplicate that is the whole body of a plain module-level function now
1322
+ calls that function instead of extracting a helper. Two identical functions
1323
+ no longer become a helper plus two forwarders: the first-defined one is kept
1324
+ and the other calls it, and a matching block inside a larger function calls
1325
+ the existing function directly. Arguments follow the function's parameter
1326
+ order; names the body reads from its own module (functions, classes,
1327
+ absolute imports) are not passed. Across files the call is imported like a
1328
+ helper and refused when it would close an import cycle; a decorated, async,
1329
+ variadic, shadowed, or rebound function falls back to ordinary extraction.
1330
+ A function whose body is the block followed by a plain `return` of the
1331
+ block's live variables is reused too; the other sites unpack its result in
1332
+ its order. Construct the engine with `reuse_existing_functions=False` to
1333
+ restore the old behavior.
1334
+ - A helper that returns live variables now admits every further same-file
1335
+ occurrence whose call assigns them, so twelve identical functions become
1336
+ one helper with twelve calls on the first pass rather than a pair per
1337
+ pass. Repeated passes used to stack such helpers into a chain of
1338
+ forwarders, each returning the next one's result; a proposal that would
1339
+ reduce a helper an earlier pass inserted to a one-line forwarder is now
1340
+ declined instead (part of `skip_trivial_helpers`).
1341
+ - `towel dry` formats the code it inserts with the formatter the project
1342
+ configures (`pip install "code-towel[format]"`): `ruff format` when
1343
+ `[tool.ruff]`, `ruff.toml` or `.ruff.toml` is present and ruff is
1344
+ installed, otherwise Black with the project's own line length and string
1345
+ quoting. Only the
1346
+ generated helper and the rewritten call statements are formatted, never
1347
+ the surrounding file, and each snippet is checked to have the same syntax
1348
+ tree before and after. Inserted imports are sorted the way the project
1349
+ sorts them, with ruff's `I` rules when selected or isort when configured;
1350
+ a sorter may only reorder or merge the import statements, which is
1351
+ verified. Pass `--no-format` to insert the unformatted rendering; without
1352
+ a formatter a note says so. Library callers pass any `snippet_formatter`
1353
+ and `file_finisher` callables to the engine.
1354
+ - Black's line length for generated code is the limit the project declares
1355
+ anywhere: `[tool.black]`, `[tool.ruff]`, `[tool.pycodestyle]`, or a
1356
+ `[flake8]`/`[pycodestyle]` section in `setup.cfg`, `tox.ini` or `.flake8`
1357
+ (pycodestyle's own dog-food test failed on an 86-character generated call
1358
+ under Black's default of 88 against its declared 79).
1359
+ - Extracted helpers carry the type annotations their call sites declare. A
1360
+ parameter is annotated when every site passes an annotated, never-rebound
1361
+ parameter of its enclosing function, or a literal of one builtin type, and
1362
+ the sites agree; the return is annotated from the sites' declared return
1363
+ type, from annotated locals the helper returns, or as `None` for a helper
1364
+ that returns nothing. Nothing is inferred. Annotations are copied unquoted
1365
+ only where they cannot fail to resolve (builtins, deferred annotations, or
1366
+ names bound by a module-level import) and as strings otherwise; across
1367
+ modules only builtin names are used. Code without annotations stays that
1368
+ way. Construct the engine with `annotate_helpers=False` to disable it.
1369
+ - When a type checker is installed (`pip install "code-towel[types]"`), the
1370
+ argument expressions and return values those copied annotations could not
1371
+ name are typed by it. The checker is the one the project configures: mypy
1372
+ for a project with `[tool.mypy]`, `mypy.ini`, `.mypy.ini` or a `[mypy]`
1373
+ section in `setup.cfg`, pyright for one with `[tool.pyright]` or
1374
+ `pyrightconfig.json`; for a project configuring both,
1375
+ mypy infers while both verify the prospective project for new errors.
1376
+ For inference, Pyright probes a temporary sibling copy of the module;
1377
+ mypy reveals expressions in an in-memory copy of the site's module, at
1378
+ the point where the call will stand, using an owned worker with incremental
1379
+ caches. Verification checks the modified files together with unchanged
1380
+ consumers under the project's configured scope. A revealed type is written only
1381
+ when it is not bare `Any` and every name in it resolves where the helper
1382
+ is defined. `--no-types` leaves helpers unannotated; library callers pass
1383
+ a `type_oracle` to the engine.
1384
+ - Sites that disagree on a parameter's type, or on a revealed return type,
1385
+ join into a union (`int | None`, `int | str`); sites that declare
1386
+ different return types take the meet of the declarations, found through
1387
+ the checker's subtype relation. Once a helper carries any annotation,
1388
+ whatever is still bare becomes `Any`, so the signature is complete (a
1389
+ partial one is an error under mypy's `disallow-incomplete-defs`), and
1390
+ `from typing import Any` is added to the host when it lacks it. Code with
1391
+ no annotations stays bare.
1392
+ - With mypy installed the subtype relation is mypy's own, asked through
1393
+ probe functions appended to an in-memory copy of the host module: unions
1394
+ are normalized by it (`int | bool` is `int`, `float | int` is `float`, a
1395
+ subclass under its base disappears), the declared return types' meet is
1396
+ found through it, and the helper's revealed return type is written only
1397
+ when mypy confirms it is a subtype of every site's declared return type,
1398
+ which is what keeps the sites type-checking. Without mypy Towel copies
1399
+ and does not reason: unions are written unreduced and the meet needs
1400
+ identical declarations, since there is no second implementation of the
1401
+ subtype relation.
1402
+ - Thunk and callee arguments get `Callable` annotations from mypy's callable
1403
+ spelling (`Callable[[], int]`, `Callable[[int, str], bool]`,
1404
+ `Callable[..., T]`), with `from typing import Callable` added as needed.
1405
+ - A module-level helper whose annotations name classes or functions defined
1406
+ in the module is placed after the last such definition, so those names are
1407
+ written bare instead of as quoted forward references; it stays at the top,
1408
+ with quotes, when any statement before that point could run code at
1409
+ import time.
1410
+ - A subscripted annotation is written bare only when it evaluates at
1411
+ definition time: a PEP 585 builtin generic, a name from `typing` or
1412
+ `collections.abc`, or a module that defers annotations. Anything else is a
1413
+ string annotation: `memoryview[int]`, copied from tornado's own signatures,
1414
+ raised `TypeError` at import on an interpreter where `memoryview` is not
1415
+ generic.
1416
+ - With a clean original type-checking baseline, helper extractions and existing
1417
+ function reuse check all modified files together in the prospective project,
1418
+ including unchanged consumers. New errors make generated helper annotations
1419
+ fall back to `Any`, then to none; every variant must pass. Reused functions
1420
+ keep their existing signatures, and incompatible calls decline the reuse.
1421
+ Checker failure or remaining new errors decline the proposal. Cross-file
1422
+ helper imports no longer lose valid annotations because their host was stale.
1423
+ - Before oracle inference or verification, the original complete project is
1424
+ checked. If it already has type errors, Towel aborts and asks the user to
1425
+ fix them or explicitly rerun with `--no-types`. That option preserves
1426
+ existing source annotations and generates unannotated helpers. Checker
1427
+ crashes and timeouts remain distinct failures; checking is never silently disabled.
1428
+ A clean baseline keeps verification enabled for subsequent proposals.
1429
+ - A [proposal for type-parameter inference](docs/proposals/type-parameters.md)
1430
+ records how generic helpers could preserve relationships among arguments
1431
+ and return values. This work is deferred beyond 1.732.
1432
+ - Property-based tests (hypothesis, in the `dev` extra) check that alpha-variant
1433
+ blocks unify with a renaming-only substitution, that definite assignment
1434
+ agrees with a path-enumerating reference, and that every generated helper
1435
+ passes the instantiation round trip; `visitors.py` has direct tests; the
1436
+ cross-file hostile battery pins which packages must transform; ten
1437
+ cross-file integration tests fail instead of skipping when a fixture is
1438
+ missing; six assertion-free tests assert what their names claim.
1439
+ - The suite now drives the forked pair-evaluation pool on a small project and
1440
+ checks it against the serial path, including the fallback when a worker
1441
+ raises or dies; corrupts the recovery journal one guard at a time and
1442
+ round-trips interrupted transactions under Hypothesis; drops a proposal
1443
+ that goes stale mid-run and covers the three progress displays; reaches
1444
+ the return-coverage and free-variable-lifetime rejections by name (and
1445
+ pins that the first block's coverage guard is shadowed by block
1446
+ enumeration); and adds idempotence, byte-convention and observational
1447
+ equivalence properties, with `with` in the flow grammar. The cross-file
1448
+ timing test no longer measures the machine, the watchdog test finishes
1449
+ in a fraction of a second, and both hostile batteries execute fixtures
1450
+ the same isolated way. Three hostile fixtures pin the closure, `dir()`
1451
+ and unbound-global cases below, and a forwarder-chain suite pins that
1452
+ twelve identical functions yield one helper.
1453
+ - CI runs `pip-audit --strict` against the locked dependency set, and
1454
+ Dependabot proposes weekly, grouped minor/patch updates for the workflow
1455
+ actions and the `uv`-managed Python dependencies.
1456
+ - The candidate pairs an analysis evaluates are bounded by a configurable
1457
+ candidate-pair budget (`--max-pairs`, default 20,000,000; past it the
1458
+ largest groups of similar blocks are left out with a warning), and the
1459
+ minimum block length (`--min-lines`) and helper parameter limit
1460
+ (`--max-parameters`) are exposed on the command line.
1461
+
1462
+ ### Changed
1463
+ - Boolean options come in `--x/--no-x` pairs with the default on:
1464
+ `--types/--no-types`, `--format/--no-format`, `--interactive/--no-interactive`,
1465
+ and the tri-state `--prefer-absolute-imports/--no-prefer-absolute-imports`
1466
+ and `--pep420/--no-pep420` (unset lets the project decide). `--max-refactorings N`
1467
+ names what `--max-iterations` always did, and `rename-helpers --preview`
1468
+ replaces `--dry-run`, since "dry" already means DRY here.
1469
+ - A helper whose body only binds parameters and literals to names and returns
1470
+ them is not proposed: the call that unpacks the tuple is longer than the
1471
+ assignments it replaces and shares no logic. The trivial-forwarding filter
1472
+ also recognizes `name = call(...)` followed by `return name`, and the tuple
1473
+ form `a, b = call(...)` then `return (a, b)`; without it, once Black
1474
+ wrapped such a body over the three-line minimum, two generated helpers of
1475
+ that shape paired with each other and extracted a third, without end (h2).
1476
+ - In directory mode, a global re-pass after the first re-pairs functions in
1477
+ rewritten files and files with deferred verification refusals. Deferred
1478
+ proposals are retried only after the project changes, since verification can
1479
+ depend on unchanged consumers elsewhere in the project. Other unchanged
1480
+ pairs retain their verdict under the conditions explained in
1481
+ `docs/ARCHITECTURE.md`; `incremental_global_passes=False` restores full
1482
+ re-pairing.
1483
+ - Analysis facts are computed once per function instead of once per candidate
1484
+ block (definite assignment, locally bound names, nested scopes), and the
1485
+ same-file clustering pass applies its constant-time filters before the
1486
+ semantic guards. Together these remove about half of the AST traversal on
1487
+ a 16k-line project with an identical proposal list.
1488
+ - Candidate blocks are bucketed on their whole statement-type sequence,
1489
+ which the unifier requires equal, so far fewer pairs reach the filter; the
1490
+ unifier's bound-variable search caches each node's source text instead of
1491
+ re-rendering it per query; the value-producing check is memoized per
1492
+ block; and five visitor classes that were rebuilt on every call are
1493
+ module-level. On Towel's own source these remove a further 28% of all
1494
+ function calls (about half in total since 1.618) with an identical
1495
+ proposal list.
1496
+ - The engine is assembled from mixins, one module per responsibility
1497
+ (`pair_evaluation`, `placement`, `reuse`, `insertion`,
1498
+ `annotation_wiring`, `materialize`, `clustering`, `parallel`,
1499
+ `fixed_point`), over an `EngineState` that declares the state and
1500
+ operations each may rely on; `refactor_engine.py` went from 5,600 lines
1501
+ to 1,400 and the 871-line pair decision is eleven typed stages. The
1502
+ outputs are byte-identical on the exactness baselines.
1503
+ - A second pass over the same seams: the per-block analyses are a
1504
+ `BlockAnalysis` mixin of their own and the engine core is about 800 lines;
1505
+ the unifier is split the same way (`UnifierState` under constant consistency,
1506
+ parameterization and literal promotion, with `Substitution` and the
1507
+ binding-context finder in leaf modules); every state stub names the class
1508
+ that implements it. The functions of an analysis are indexed once
1509
+ (`FunctionIndex`: by file, by name, by enclosing range) instead of scanned
1510
+ per pair, and the engine's per-function maps are weak. Materialization,
1511
+ the directory driver's progress reporting and the rename planner are
1512
+ each a set of named steps rather than one long function. Blocks are
1513
+ narrowed by checks rather than casts, and the `dry` change sidecar is
1514
+ validated on read.
1515
+ - A third structural pass: the block signatures, clustering guards, cache
1516
+ keys, pair context and stage records are typed; the remaining long
1517
+ functions (materialization, the drivers, layout discovery, the pairing
1518
+ loop, the call generator, the clustering pass, the rename planner, the
1519
+ compound-node unifier, the thunk walker) are named steps or dispatch
1520
+ tables; every function and closure with no caller is removed; mypy runs
1521
+ with `warn_unreachable`, and flake8 checks `tests/` for pyflakes errors.
1522
+ One `BoundedCache` replaces three LRU implementations and the parse cache
1523
+ is the engine's own; `FunctionNode` is the one name for a function
1524
+ definition node; the defaults live in `towel.unification.defaults`;
1525
+ `towel.source_text` decodes and re-encodes sources.
1526
+ - The engine computes what it can once per statement instead of once per
1527
+ block: block signatures, return checks, the frame-sensitivity and
1528
+ name-binding guards, structural digests, the unifier's per-statement
1529
+ facts and the substitution's keys are memoized per AST node and folded
1530
+ over each block, the bound-variable query is memoized per block and
1531
+ target text, a pair's structural ids are resolved once and passed to its
1532
+ guards, and each class's file path is resolved once per run. On the
1533
+ Towel source of that day (`towel dry src/towel`, one worker, 45 applied)
1534
+ a run took 6.4 s instead of 10.9 s with `--no-types --no-format` and
1535
+ 10.4 s instead of 15.0 s with the defaults; the profiled run went from
1536
+ 32.7 s and 293 million calls to 20.3 s and 154 million. The current
1537
+ figures, on a smaller source, are in `docs/KNOWN_LIMITATIONS.md`. Every
1538
+ proposal and rendering is byte-identical on the exactness baselines. The
1539
+ structural-id and source-digest caches, and the definite-assignment
1540
+ facts, no longer keep a re-parsed file's old tree alive; peak memory fell
1541
+ from 186 MB to 179 MB (1.11 GB to 1.06 GB with type checking).
1542
+ - A fourth pass, after an audit that found the same concepts written
1543
+ several ways: the local name an import alias binds, the names a
1544
+ statement binds (`bindings_of`, replacing five collectors that
1545
+ disagreed), the names an expression reads, span containment, the
1546
+ method-kind literal, the helper-home and cluster-context records and
1547
+ package-chain ascent each have one implementation; the unifier's
1548
+ dispatch table holds methods rather than their names; blocks are
1549
+ sequences of statements throughout, so the casts that the wider
1550
+ annotation forced are gone; two typing helpers (`all_instances`,
1551
+ `visit_as`) replace most of the rest; each command reads its arguments
1552
+ once into a typed record; the rename command decodes sources the way
1553
+ the refactoring commands do; and comments that restated the next line
1554
+ are removed. The engine's mixins now inherit the mixins they call, so
1555
+ the forty-nine one-caller stubs on `EngineState` are gone and it declares
1556
+ only the shared attributes and the eleven operations the core provides.
1557
+ The import-graph resolution is its own module (`import_graph.py`);
1558
+ parsing yields a `RawModule` and scope analysis a `ParsedModule` whose
1559
+ analyzer and root scope are required rather than Optional; the
1560
+ `verbose` flag threaded through eleven signatures and read once is gone
1561
+ (the logger's level decides); and the `get_` prefix on a handful of
1562
+ functions is dropped (`free_variables`, `used_names`,
1563
+ `enclosing_names`, `affected_lines`, `bound_variables_in_context`).
1564
+ - The test suite is type-checked with the same strict flags as the source
1565
+ (fifteen `type: ignore` comments ignored nothing; the AST accesses behind
1566
+ the rest are `isinstance` assertions now), coverage traces the forked
1567
+ pair workers (`parallel.py` 85% to 95%), the helpers copied across test
1568
+ modules live in `tests/test_helpers.py`, thirty-odd non-emptiness assertions state
1569
+ the value they are about, the goldens expected to equal their inputs are
1570
+ named and checked exactly, the example3 tests run the cross-file path
1571
+ they describe, a symlinked input directory is pinned, and the engine and
1572
+ command-line branches the suite left untested have tests.
1573
+ - The clustering pass scans a file for the sites that can share a helper
1574
+ once per distinct helper template instead of once per pair, and the
1575
+ reuse redirect finds a function whose body starts at a site through an
1576
+ index instead of scanning the file per replacement: 50 identical
1577
+ functions took 17 s and 100 took 134 s at 1.618, and 6.8 s and 36 s at
1578
+ commit a0596f0 (September 18, 2026, one core of a machine shared with
1579
+ other work); measured with `scripts/bench_similar_blocks.py` at commit
1580
+ 5ff2458 (September 19, 2026, one core), 50 take 4.0 s and 100 take
1581
+ 15.9 s, with identical output. Orphan detection, the instantiation
1582
+ check's normalized block and the class-private-name scan are memoized
1583
+ on structure, so the re-parse after each applied proposal hits too. The
1584
+ analysis session grows to the number of files and the source bytes an
1585
+ analysis covers, so a project above 128 files or 8 MiB of source no
1586
+ longer re-parses everything on every pass (trio's second pass: 144
1587
+ parses and 7.2 s, now none and 5.0 s). Both insertion-position parses
1588
+ go through the engine's parse memo.
1589
+ - The AST visitors are built on three Template Method bases in
1590
+ `visitors.py`: `OwnScopeVisitor` for collectors that read one scope's own
1591
+ code, `DefinitionDepthVisitor` for those that track how deeply a
1592
+ definition sits, and `ScopeVisitor` for the analyses that follow lexical
1593
+ scopes, which now share one visiting order for definitions and
1594
+ comprehensions and override hooks where they deviate on purpose.
1595
+ - `refactor_directory_to_fixed_point` returns a `TerminationReason` literal;
1596
+ `CodeBlockPair` carries every field pairing always gives it (only the
1597
+ class and enclosing-function names are Optional) and answers
1598
+ `is_cross_file`; `RefactoringProposal.replacements` holds `Replacement`
1599
+ values only, the tuple coercion is gone; the unused exception classes are
1600
+ gone and `UnsupportedLayoutError` joins `RefactoringError` under
1601
+ `TowelError` (and is still a `ValueError`); `towel.project_layout` is a
1602
+ top-level module (the old path
1603
+ re-exports it); `is_package_dir` takes only the path, since whether a
1604
+ bare directory is a namespace package is the layout's decision.
1605
+ - Progress modes are a `ProgressMode` literal (`normalize_progress`,
1606
+ `wants_bar`, `DEFAULT_PROGRESS`); `orphaned_variables` returns the set of
1607
+ orphaned names and `bound_names_in_block` the names a block binds; the
1608
+ engine's `type_inferrer` parameter is `type_oracle` and the
1609
+ `TypeInferrer` alias is gone, `TypeOracle` is the name. Module-internal
1610
+ helpers that read as public are private, and the predicates that ask
1611
+ whether a project configures a tool all say `project_configures_*`.
1612
+ - `run_pipeline` takes the engine it drives (`engine=`) and the pipeline no
1613
+ longer imports the engine; the two phase entry points it calls are public
1614
+ (`find_block_pairs`, `process_block_pairs`). `from towel.unification
1615
+ import UnificationRefactorEngine` still works and loads the engine lazily.
1616
+ - `formatter_for_project`, `import_sorter_for_project`, and
1617
+ `type_oracle_for_project` return a `ToolChoice` (`tool`, `note`) instead
1618
+ of a tuple; `TypeOracle.is_subtype` returns `Subtyping` verdicts
1619
+ (`YES`, `NO`, `UNKNOWN`) instead of `Optional[bool]`. Rejection reasons
1620
+ are a `RejectReason` enum, evaluation kinds a `ParameterKind` literal,
1621
+ the rename inventory a `TypedDict`, and the applied-change log is
1622
+ `engine.change_log`, a sequence of `AppliedChange`.
1623
+ - The library prints no diagnostics. Warnings go to the `towel` logger,
1624
+ which reaches stderr even when nothing configures logging, and the
1625
+ rejection, validation, overlap, and type traces go to `towel.rejections`,
1626
+ `towel.validation`, `towel.overlap`, and `towel.types` at DEBUG. The
1627
+ documented environment variables still switch them on; they and
1628
+ `TOWEL_WORKERS` are read once into `Settings` at engine construction
1629
+ (`settings=` overrides them for library callers), and the engine
1630
+ constructor no longer changes logger levels
1631
+ (`Settings.enable_debug_logging` is the library caller's to call).
1632
+ - Helpers other modules imported under leading underscores are public:
1633
+ `find_project_root`, `load_pyproject`, `is_package_dir`,
1634
+ `walk_own_scope`, `body_without_docstring`, `stored_names`, `reindent`,
1635
+ `relative_import_module`. Two implementations of the docstring stripper,
1636
+ the own-scope walker, the `__param_N` minting rule, and the subtype
1637
+ verdict reader became one each, and ten duplications Towel found in its
1638
+ own source are named helpers.
1639
+ - The import-graph tables are an `ImportGraphCache` the engine owns per run,
1640
+ bounded, instead of process-global dictionaries, and a required argument
1641
+ of the cycle and resolution checks; the apply path parses each modified
1642
+ file once per source text and re-reads a file only when its stat changes;
1643
+ the pair loop skips two functions whose blocks share no bucket key before
1644
+ visiting any block.
1645
+ - Two catches that turned bugs into silent defaults (a module-name fallback
1646
+ catching every exception, a call renderer swallowing a `TypeError`) are
1647
+ narrowed or removed; the nine copies of `try/except/pass` around
1648
+ progress-bar calls are one `quietly`; four unjustified `type: ignore`
1649
+ comments and three `pragma: no cover` exclusions are gone.
1650
+ - Pair evaluation keeps one proposal per distinct refactoring (the same
1651
+ helper over the same clustered sites, whichever pair found it first)
1652
+ instead of every pair's copy. A file of sixty near-identical functions
1653
+ under the pair budget peaked at 33.6 GB holding the copies and now peaks
1654
+ at 0.74 GB; the output is the same, since the overlap filter never chose
1655
+ a later copy.
1656
+ - A proposal whose helper, home and call sites repeat an earlier pair's is
1657
+ declined (`duplicate_proposal`) before the reuse redirect, the forwarder
1658
+ filter and annotation run on it; the clustering scan's key no longer
1659
+ carries names the template block never reads, which made it differ per
1660
+ function position; and the function lookup every clustered site makes is
1661
+ memoized. The clustering scan cache is bounded by the sites it holds
1662
+ (200,000) as well as by its entry count, since a scan of a file of a
1663
+ thousand near-identical functions holds a thousand sites. Such a file,
1664
+ which the module-name rule now admits where the module-data rule used to
1665
+ decline every pair in seconds, is evaluated in full: a thousand
1666
+ near-identical five-line functions reading one module global take 33
1667
+ minutes on one core (commit `5ff2458`, September 19, 2026) and yield one
1668
+ helper with 669 sites, peaking at 6.6 GB (8.8 GB before the scan cache
1669
+ was bounded); its pair evaluation is what `--max-pairs` bounds. A
1670
+ per-candidate memo beneath the scan, which no run ever read, is gone.
1671
+ - The verdict of the instantiation check is memoized on the helper, call
1672
+ and block, which it repeated many times over across pair and cluster
1673
+ evaluation: a file of a hundred similar functions took 31 s instead of
1674
+ 43 s when the memo landed (commit 77d18a8); the current figure is in the
1675
+ clustering entry above.
1676
+ - The `format` extra's Black floor is the version the goldens were
1677
+ generated with (`black>=26.3.1`, the same floor as the `dev` extra); it
1678
+ was two majors lower, so a user at the extra's floor could get output the
1679
+ goldens do not show. The sdist no longer ships the `.templates` scratch
1680
+ directory or two debugging scripts.
1681
+ - The help says what the commands do: usage lines read `towel`, each
1682
+ boolean option's help names its default, the retired spellings are left
1683
+ out, and `preview` no longer promises per-phase summaries under
1684
+ `--progress detail`, which only the fixed-point driver emits.
1685
+ - The structural memo is keyed on a block's structure, so equal blocks share
1686
+ one entry wherever they appear.
1687
+
1688
+ ### Deprecated
1689
+ - The `--max-iterations`, `--non-interactive` and `rename-helpers
1690
+ --dry-run` spellings still parse but are left out of the help; use
1691
+ `--max-refactorings`, `--no-interactive` and `--preview`.
1692
+
1693
+ ### Removed
1694
+ - `nominal_unifier.py`, which nothing imported, and the TOML backport for
1695
+ Python 3.10, below the supported floor, with its `tomli` dev dependency.
1696
+ - `ImportGraphCache.clear`, which nothing called: the cache is built per run
1697
+ and dropped with the engine.
1698
+
1699
+ ### Fixed
1700
+ - The ecosystem release gate requires completed, nonempty test runs. Setup
1701
+ failures, incomplete runs, unknown verdicts, changed failing-test identities,
1702
+ and missing tests cannot pass merely because exit codes or totals match.
1703
+ Documented known failures and flaky-test reruns cannot hide a changed test
1704
+ count; matching pre-existing failures remain visible in the report. Pytest
1705
+ commands request complete tallies and failure identities; custom runner
1706
+ failure statuses must be declared, and retests retain their actual statuses.
1707
+ Retests remove broad selectors wherever they occur and require the exact
1708
+ selected test count; ambiguous command options decline the retest. Agreement
1709
+ on isolated tests requires confirmation with the complete original test
1710
+ command, so test-order regressions cannot disappear from the check. Failed
1711
+ Git observations cannot become `NO_CHANGE`, and added, deleted, or renamed
1712
+ paths count as changes. Source provenance names the checkout actually used;
1713
+ archives explicitly require a separately retained source manifest.
1714
+ The corpus manifest supplies Cheroot's declared test plugins and SimPy's
1715
+ benchmark fixture, and runs PLY's test scripts from the project root so each
1716
+ suite imports the source tree under test.
1717
+ The harness records its requested typing mode. Its explicit `--no-types`
1718
+ option supports behavioral validation of projects without a complete typing
1719
+ environment; it never retries a failed typed run by silently opting out.
1720
+ - The worker-cleanup regression fails when process inspection fails, instead
1721
+ of treating an empty result from a failed command as proof that workers exited.
1722
+ - Mypy runs in an owned persistent worker with incremental caches and periodic
1723
+ garbage collection. Its process-global state cannot freeze or unfreeze a
1724
+ library caller's heap, concurrent requests are serialized, and explicit
1725
+ cleanup covers both combined checkers and failed optional-dependency setup.
1726
+ The CLI preserves input-project configuration while checking the copied
1727
+ output under its original module identities, and closes the checker on
1728
+ success and failure. Project type rules are honored while plugins,
1729
+ configured executables and report destinations remain disabled.
1730
+ (1.772 replaced that periodic collection: each build now runs in a forked
1731
+ child of the worker which exits when it has answered.)
1732
+ - Mypy diagnostic presentation is normalized for in-memory sources. A
1733
+ project's `pretty = true` setting no longer crashes when a subtype probe
1734
+ or prospective helper extends beyond the on-disk file; project type rules
1735
+ remain unchanged.
1736
+ - Checker outcomes distinguish infrastructure failure from valid diagnostics.
1737
+ A pyright timeout, malformed output or failure is never evidence of a clean
1738
+ project or a successful subtype relation.
1739
+ - Lexical binding analysis recognizes PEP 695 type parameters and shadowed
1740
+ builtins, preserving generic identity and conditional-name evaluation.
1741
+ - Ruff subprocesses exclude the current project from Python's import path.
1742
+ Import sorting preserves ordered providers of shared bindings and statement
1743
+ boundaries. Permanent filesystem conflicts stop instead of retrying forever.
1744
+ Directory mode also terminates after all proposals in an unchanged project
1745
+ fail rendering or type verification, and retries deferred proposals after
1746
+ another successful change can alter their checking context.
1747
+ - Helper renaming preserves builtin reads, keyword callers, exact selection
1748
+ filters and Python protocol behavior. Static aliases and re-exports are
1749
+ tracked; ambiguous callable/module aliases, visible reflection and dynamic
1750
+ keyword dictionaries are refused before writing. Source discovery prunes
1751
+ environments and excluded directories and reports unreadable consumers.
1752
+ - The eager-argument guard is rebuilt on control flow. Twelve shapes that
1753
+ passed a differing name eagerly where the original read it only on some
1754
+ path are thunked or declined: a failed optional import, a `TYPE_CHECKING`
1755
+ import, a class attribute read bare in a method, a name bound later,
1756
+ deleted, or bound conditionally in the enclosing function, and at module
1757
+ level a name bound by `except ... as`, a `match` capture, a later `def`,
1758
+ or a later import.
1759
+ - A free variable the two blocks share follows the same rule as a differing
1760
+ argument: it is passed eagerly only when the call site resolves it on
1761
+ every path, and as a thunk otherwise. A name bound nowhere, read only on
1762
+ a branch no run takes, used to be hoisted into an eager argument and raise
1763
+ `NameError` at every call (found by the new property test); a helper
1764
+ defined below its callers, called once at import time before its
1765
+ definition, raised the same way. Both are now read where the block read
1766
+ them: a same-module helper reads the name bare (next entry), and a
1767
+ cross-file helper takes it as a thunk.
1768
+ - A free name that both sites resolve at module scope, or nowhere, is no
1769
+ longer a parameter of a same-module helper: the helper reads it bare,
1770
+ which is the lookup the block made, at the moment the block made it. A
1771
+ helper defined below its callers, a class, an import, or module data a
1772
+ callback rebinds between two reads (previously declined as
1773
+ `module_data_lookup` or `rebound_external_binding`) all extract, with
1774
+ fewer parameters and no thunk; hyper-h2 loses seven such parameters and
1775
+ gains three extractions, and its own 1,662 tests pass on the output. A
1776
+ clustered occurrence whose same-spelled name is a local does not join;
1777
+ cross-file helpers still take the name as a parameter, and module data
1778
+ a callback may rebind still declines a cross-file pair. So does a
1779
+ same-file pair whose helper is hosted in a shared ancestor class defined
1780
+ in another module: the pair is decided again with every name a
1781
+ parameter, since the ancestor's module need not import them (the
1782
+ release-candidate ecosystem run found oauthlib's `BearerToken` read bare
1783
+ in `introspect.py`, which raised NameError in 55 tests). `__class__`, the
1784
+ cell zero-argument `super()` reads, names the defining class and stays a
1785
+ parameter of a method helper.
1786
+ - A call to a function or method of the same module whose body reads a
1787
+ frame relative to its caller (`sys._getframe(n)`, `inspect.stack()`, a
1788
+ `stacklevel=`), directly or through other such functions, is a frame read
1789
+ at its call site, and the block stays where it is. The module-name rule
1790
+ had admitted blocks that typing_extensions' `_caller()` reads through,
1791
+ which a reflection guard had declined only by accident, and six
1792
+ `TypeAliasType` pickling tests failed in the release-candidate ecosystem
1793
+ run.
1794
+ - A call site that would pass a callee as
1795
+ `lambda *args, **kwargs: callee(*args, **kwargs)` is declined by name
1796
+ (`forwarded_callee`). It used to be declined by accident, because the
1797
+ lambda's own parameters were counted as names the site could not resolve;
1798
+ that miscount also declined any thunk containing a lambda, such as
1799
+ `lambda: sorted(items, key=lambda x: x)`, which now extracts.
1800
+ - A comprehension's target shadows only its own expressions: `[i for i in
1801
+ xs]` used to become `[__param_0() for i in xs]` while a free read of `i`
1802
+ after the comprehension stayed literal.
1803
+ - The frame-reading builtins, `eval`/`exec`, `sys._getframe` and
1804
+ `inspect.currentframe` anywhere in the enclosing function decline the
1805
+ block, not only inside it, and their aliases, imported or assigned
1806
+ (`e = eval`, `warn = warnings.warn`, `gf = sys._getframe`, an alias of an
1807
+ alias), are resolved through the module's bindings.
1808
+ - `warnings.warn` without `stacklevel`, and a `stacklevel` reached through
1809
+ an alias, decline the block.
1810
+ - An object a class instantiation or a resource factory (`open`,
1811
+ `connect`, `socket`, `mkdtemp`, `Popen`, `urlopen`, ...) binds in the
1812
+ block is returned from the helper rather than dropped when the helper's
1813
+ frame ends, since a later read may observe its lifetime (a temporary
1814
+ file, a weak reference).
1815
+ - A helper import goes after the module's last leading import, before its
1816
+ first definition (after the docstring when there are no imports), so a
1817
+ script that runs a statement before its imports keeps that statement
1818
+ first. A host for a cross-file helper is accepted only when the borrower
1819
+ already imports it or every module the new import would load is
1820
+ definition-only, so a helper import cannot run a module that prints at
1821
+ import time (`xf13_import_time_effects` is declined).
1822
+ - A form feed or a Unicode line separator in a source no longer crashes the
1823
+ splice: lines are counted the way the tokenizer counts them.
1824
+ - Async comprehensions are declined.
1825
+ - A rendering failure in one proposal ends that proposal, not the run.
1826
+ - A relative output path no longer ends the fixed point early.
1827
+ - Type inference no longer switches off when the working directory is
1828
+ above the output.
1829
+ - A pending transaction journal blocks a run only when its manifest names
1830
+ a file the run would change (a journal without a readable manifest
1831
+ blocks everything beneath it), and journals of concurrent runs never
1832
+ share a name.
1833
+ - A killed run's pyright probe cannot be mistaken for source.
1834
+ - The mypy cache and partial-copy directories are cleaned on SIGTERM.
1835
+ - An ASCII locale no longer breaks a run.
1836
+ - The change sidecar is written atomically.
1837
+ - `--interactive` with a closed stdin declines instead of raising.
1838
+ - Settings are read from the environment once per run.
1839
+ - Three ways an extraction could change what a program does, each found by
1840
+ executing crafted inputs before and after refactoring: a closure created
1841
+ inside the block over a name the block binds (a loop target) kept the
1842
+ helper's cell where the original saw the caller's later rebinding; an
1843
+ argument-free `dir()` read the helper's frame; and a differing free name
1844
+ was passed eagerly on the assumption that a name has no failure mode,
1845
+ so a global the module never binds raised `NameError` at the call site
1846
+ where the original read it only in a branch it did not take. The first
1847
+ two are declined; the third follows the shared-free-variable rule above.
1848
+ - Single-file `dry` on a file it cannot decode copied it and then stopped
1849
+ with a codec message naming nothing, and on a file with a syntax error
1850
+ reported success over an unchanged copy; both are an error naming the
1851
+ file (and the line). A negative `--max-refactorings` was accepted and
1852
+ meant "run to a fixed point"; it is refused. `preview` of one file showed
1853
+ a progress bar under `--progress none`, and the banner said helpers go at
1854
+ the end of files.
1855
+ - With `--types`, the variables a helper returns are revealed under each
1856
+ call site's own spelling; a site whose names alpha-renaming spelled
1857
+ differently from the helper's used to probe names that did not exist
1858
+ there, so the helper's return type stayed `Any` and parameter unions the
1859
+ return type would have invalidated went unchecked.
1860
+ - A refactored file keeps its own encoding, byte-order mark and newline
1861
+ convention: a CRLF file used to come back with every line changed, a file
1862
+ with a UTF-8 BOM was skipped, and one latin-1 file with a coding cookie
1863
+ failed a whole directory run. Sources are decoded as the interpreter
1864
+ decodes them and written back in the file's own bytes; `rename-helpers`
1865
+ reads the same way and keeps each file's encoding.
1866
+ - A project whose packaging layout Towel cannot model (a `hatch.toml`, some
1867
+ flit, poetry and pdm forms) no longer aborts the run when it has a
1868
+ cross-file candidate: the pair is declined (`unknown_layout` in the
1869
+ rejection trace) and every same-file extraction proceeds. Layout
1870
+ discovery raises `UnsupportedLayoutError`, and the command line reports
1871
+ any `TowelError` as an error line instead of a traceback.
1872
+ - `--progress none` is silent through the localized and stale re-analyses
1873
+ that follow each applied proposal, the single-file driver takes a
1874
+ progress mode, and `preview` accepts `--progress`. Both drivers run to a
1875
+ fixed point by default, as the CLI always did; they used to stop after
1876
+ ten iterations, and the goldens generated through them were ten-step
1877
+ snapshots rather than fixed points.
1878
+ - An import sorter's result is accepted only if it permutes or merges import
1879
+ statements, at any depth (a sorter also orders the imports under
1880
+ `if TYPE_CHECKING:`); anything else is discarded and the file is left as
1881
+ Towel assembled it, rather than failing the refactoring (trio).
1882
+ - `rename-helpers --list --json` no longer crashes on a symlinked module.
1883
+ - A confirmation prompt at a closed stdin declines instead of raising.
1884
+ - An isort skip setting, a failing or hung ruff, a hung pyright, and pyright
1885
+ output of an unexpected shape each degrade with a warning naming the file
1886
+ instead of aborting or being swallowed.
1887
+ - A non-integer or non-positive `TOWEL_WORKERS` is reported, and a
1888
+ configured worker count never reaches a platform without `fork`.
1889
+ - A corrupt transaction manifest and a concurrent transaction are reported
1890
+ as the journal's own errors.
1891
+ - The engine forgets a rewritten file's cached lines when it invalidates
1892
+ the file.
1893
+ - The parallel fallback no longer hides worker exceptions behind a serial
1894
+ retry.
1895
+ - The command line's stderr handler follows a redirected stderr.
1896
+ - The cross-file fixture package named `lib` was hidden from fresh checkouts
1897
+ by the `lib/` ignore pattern; the pattern exempts the fixtures.
1898
+ - Two blocks that differed only in the spelling of a lambda's parameter
1899
+ (`lambda value: value * 2` against `lambda other: other * 2`), or of an
1900
+ assignment expression's target (`(t := f())` against `(temp := f())`),
1901
+ were declined. Lambda parameters are now renamed within their own lambda
1902
+ by the instantiation check, which leaves a free name spelled the same
1903
+ outside the lambda alone, and walrus targets are block-level binders for
1904
+ the unifier, since an assignment expression binds in the enclosing scope.
1905
+ - A block that binds a variable read after it is extracted again, with the
1906
+ helper returning the variable and the call rebinding it (`total =
1907
+ helper(order)`), as the README has always shown. The orphan guard added for
1908
+ networkx in 1.618 did not know the generated call rebinds a returned
1909
+ variable, so it rejected every such block; only blocks that returned or
1910
+ bound nothing live could be extracted.
1911
+ - The import-cycle guard treats `from . import name` (and `from .. import
1912
+ name`) as an edge to the package's `__init__`, which that import runs
1913
+ whether or not `name` is a submodule. A helper hosted in a submodule that
1914
+ reaches back into its package this way was imported by the package
1915
+ initializer, which the submodule then imported half-initialized
1916
+ (beautifulsoup4's tests package). The initializer, which the submodule
1917
+ already imports, is now the host.
1918
+ - The import-cycle guard resolves a file's own package's absolute imports
1919
+ inside the tree being refactored, by suffix, even when a full-path match
1920
+ exists elsewhere: an out-of-place output beside the original clone
1921
+ (`sphinx-cleaned` next to `sphinx`) had `from sphinx.transforms import X`
1922
+ resolved against the original, where the helper import that closed the
1923
+ cycle did not exist, and sphinx's transforms package broke on import.
1924
+ - A union of forward references is written as one string (`'Left | Right'`),
1925
+ not as `'Left' | 'Right'`, which is a TypeError at definition (Towel's own
1926
+ suite, refactored by Towel).
1927
+ - Definite-assignment analysis now knows that `except E as name` deletes
1928
+ `name` when the handler exits, and that a `del` nested in a branch unbinds
1929
+ its target on that path. The returned-variable check relies on this; without
1930
+ it a helper could return a handler's name and raise `UnboundLocalError`
1931
+ where the original code did not.
1932
+ - With mypy, a union whose members the checker could not all order (some
1933
+ verdicts unanswerable: A under B, B under C, C under A) let every member
1934
+ absorb every other, emptied the union, and the join raised `IndexError`
1935
+ (sphinx, after eighteen minutes of refactoring). A member now absorbs
1936
+ another only on a definite `YES`, a normalization that would keep nothing
1937
+ keeps the union as it was, and a join with no members writes no
1938
+ annotation.
1939
+ - A function preceded by `@overload` stubs is a valid reuse target: the
1940
+ reuse check verifies the last module-level definition of the name, which
1941
+ is the runtime binding the calls resolve to, instead of refusing a name
1942
+ defined more than once (rfc3986's `normalize_query`).
1943
+ - A type mypy reveals with the path of an out-of-place output directory
1944
+ that is not an identifier (`h2-dbg.stream.H2Stream`, which parsed as a
1945
+ subtraction and was written as `self: h2 - H2Stream`) is rewritten with a
1946
+ placeholder for that component, and a revealed type is accepted only when
1947
+ its tree is made of names, attributes, subscripts, tuples, constants and
1948
+ unions.
1949
+ - `rename-helpers` with a rename file it cannot open or parse exits 1 with
1950
+ `Error: ...` on stderr like every other command failure; it used to print
1951
+ the failure to stdout without the prefix.
1952
+ - Invariants the engine enforced with `assert`, which `python -O` removes
1953
+ (a helper without a host name, a node without an end position), are
1954
+ exceptions that survive `-O`; a rendered module that does not parse
1955
+ raises `RefactoringError` naming the parse error instead of being dropped
1956
+ as "no insertion point"; and when applying a change fails, the cleanup's
1957
+ own error no longer hides the failure that triggered it. The cross-file
1958
+ reuse redirect follows the same import-time rule as cross-file helpers: a
1959
+ site is not redirected to a function in another module when the import
1960
+ would make the site's module load one that runs code at import time.
1961
+ - Ctrl-C prints one line instead of a traceback, and a reader that closes
1962
+ the pipe ends the run quietly; `preview` and `rename-helpers` warn about
1963
+ a pending journal and an in-place `dry` refuses before analysing; two
1964
+ source paths that differ only by case are refused before an out-of-place
1965
+ copy would merge them on a case-insensitive volume; a virtual environment
1966
+ inside the input is recognized by its `pyvenv.cfg` whatever it is named;
1967
+ the inline progress bar goes to stderr like tqdm's, so redirected output
1968
+ stays clean, and a run that asked for tqdm without it installed says so
1969
+ once.
1970
+ - A single-file run prints the frame-sensitivity warning that directory
1971
+ runs already printed when a file reads `locals()`, `eval` or the frame.
1972
+ - A `pyproject.toml` that does not parse is reported (and no layout
1973
+ information is taken from it) instead of being read as empty.
1974
+
1975
+ ### Security
1976
+ - The pyright oracle starts `python -I -m pyright`. It runs from the
1977
+ module's directory, and `-m` put that directory first on `sys.path`, so a
1978
+ project package named like a standard module was imported and executed
1979
+ in its place: sphinx's `locale` package ran when pyright's launcher
1980
+ imported `subprocess`, and pyright produced nothing for flask, pytest,
1981
+ structlog, sphinx, trio and werkzeug in the release-candidate ecosystem
1982
+ run.
1983
+ - Pyright runs with Towel's interpreter (`--pythonpath`), so a `venv`
1984
+ setting in the analyzed project's pyright configuration can no longer
1985
+ execute that project's interpreter; SECURITY.md says exactly what each
1986
+ checker does and when to pass `--no-types`. Ruff and pyright are resolved
1987
+ from the current interpreter before PATH. The ecosystem job runs
1988
+ third-party test suites without the checkout token persisted and with a
1989
+ scratch HOME; the workflows pin actions to commits. The rename file is
1990
+ read as UTF-8 and the change sidecar is never written through a symlink.
1991
+ - The pyright probe is created with `mkstemp` in the user's package
1992
+ (exclusive, owner-only, never following a symlink) and removed at
1993
+ interpreter exit if a crash skips the cleanup; it used to be a
1994
+ predictable, world-readable file left behind by a kill.
1995
+ - The ecosystem check (`scripts/ecosystem_check.py`, `just ecosystem`)
1996
+ clones public repositories and runs their setup, dependency installation
1997
+ and test suites with the caller's privileges. It now refuses to run
1998
+ unless the caller passes `--run-untrusted-code` or sets
1999
+ `TOWEL_ECOSYSTEM_RUN_UNTRUSTED=1`, the refusal says what would execute
2000
+ and recommends a disposable machine or container, and each manifest
2001
+ entry's `prepare` command runs without a shell. Every project in the
2002
+ manifest is pinned to the full commit the release-gate run tested rather
2003
+ than `HEAD`, so a hijacked upstream cannot put unreviewed code into the
2004
+ run; `--print-pins` lists the commits a run tested for refreshing the pins.
2005
+ `towel-main` alone still tracks `main`. Its work directory is a fresh
2006
+ private temporary directory unless `--work` names one, so another local
2007
+ user cannot pre-create the tree it clones into and executes from.
2008
+
2009
+ ## [1.618] - 2026-09-17
2010
+
2011
+ ### Changed
2012
+ - Cross-file helpers are imported relatively by default (`from .module import
2013
+ helper`), which stays valid when an out-of-place output is adopted into its
2014
+ real location. An absolute import is used only when a packaging marker
2015
+ (`pyproject.toml`/`setup.*`) anchors the module name, so the previous
2016
+ behavior is preserved for in-place refactoring of a packaged project.
2017
+ - Trivial forwarding helpers are no longer proposed: a block whose helper body
2018
+ would be a single `raise`, a `return` of one call, or a bare call adds
2019
+ indirection without sharing logic. Construct the engine with
2020
+ `skip_trivial_helpers=False` to restore the old behavior.
2021
+ - A cross-file helper is placed in a module that does not close an import cycle.
2022
+ When the shared block spans modules, the helper is hosted in one the others
2023
+ already import rather than adding a back-edge; the extraction is declined only
2024
+ when no placement is safe (a genuine pre-existing cycle). This replaces the
2025
+ earlier behavior of hosting the helper in the first module and declining
2026
+ whenever that would cycle.
2027
+
2028
+ ### Fixed
2029
+ - The rename tool no longer refuses a module merely because it contains a local
2030
+ variable or parameter named `vars`, `globals`, `locals`, `eval`, or `exec`;
2031
+ it flags only a genuine reference to the builtin.
2032
+
2033
+ ## [1.414] - 2026-09-15
2034
+
2035
+ First release prepared under the open-source audit. Beta: the engineering and
2036
+ evidence are strong, but Towel rewrites source code and has documented
2037
+ limitations it cannot always detect, so preview, review the diff, and run the
2038
+ affected project's own tests.
2039
+
2040
+ ### Added
2041
+ - Cross-file refactoring with import-root inference for setuptools, Hatch,
2042
+ Flit, Poetry, and pdm project layouts.
2043
+ - `towel rename-helpers`: a JSON inventory of extracted helpers and an atomic,
2044
+ checked rename batch, intended to be driven by a coding assistant.
2045
+ - `towel dry --exclude DIR` to leave a directory out of directory mode.
2046
+ - A pre-run scan that warns, before refactoring a directory, about modules that
2047
+ inspect frames or tracebacks, attribute warnings by `stacklevel`, or read
2048
+ source through `inspect.getsource`.
2049
+ - A standing ecosystem check over 91 public projects
2050
+ (`scripts/ecosystem_check.py`, `just ecosystem`), run weekly in CI.
2051
+ - Fork-based parallel analysis, enabled by a timed probe and capped by
2052
+ available memory, with a per-worker watchdog so a killed run leaves no
2053
+ workers behind. `TOWEL_WORKERS` controls it.
2054
+
2055
+ ### Changed
2056
+ - Every accepted proposal is verified by instantiating the helper with each
2057
+ call site's arguments and comparing against the block it replaces, up to
2058
+ renamed binders. Arguments that could have observable effects or fresh
2059
+ identity are evaluated inside the helper at their original position.
2060
+ - Analysis is memoized by block structure and shares parsed graphs by
2061
+ reference, checked under `TOWEL_CHECK_AST_IMMUTABLE=1`.
2062
+ - Minimum Python is 3.11; the matrix covers 3.11 through 3.13.
2063
+
2064
+ ### Fixed
2065
+ - A large set of soundness and scope defects found by adversarial batteries and
2066
+ the ecosystem check, each now covered by a fixture and a row in
2067
+ [docs/ADVERSARIAL_REVIEW.md](docs/ADVERSARIAL_REVIEW.md).
2068
+
2069
+ ### Notes
2070
+ - The PyPI releases `1.0.0`–`1.0.4` are yanked for broken import handling and
2071
+ are not a recommended installation target.
2072
+
2073
+ [Unreleased]: https://github.com/ericeallen/towel/compare/v1.772...HEAD
2074
+ [1.772]: https://github.com/ericeallen/towel/compare/v1.732.post1...v1.772
2075
+ [1.732.post1]: https://github.com/ericeallen/towel/compare/v1.732...v1.732.post1
2076
+ [1.732]: https://github.com/ericeallen/towel/compare/v1.618...v1.732
2077
+ [1.618]: https://github.com/ericeallen/towel/compare/v1.414...v1.618
2078
+ [1.414]: https://github.com/ericeallen/towel/releases/tag/v1.414