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