code-towel 1.0.3__tar.gz → 1.0.4__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 (103) hide show
  1. {code_towel-1.0.3/src/code_towel.egg-info → code_towel-1.0.4}/PKG-INFO +1 -1
  2. {code_towel-1.0.3 → code_towel-1.0.4}/pyproject.toml +1 -1
  3. {code_towel-1.0.3 → code_towel-1.0.4/src/code_towel.egg-info}/PKG-INFO +1 -1
  4. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/cli.py +3 -1
  5. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/refactor_engine.py +12 -0
  6. {code_towel-1.0.3 → code_towel-1.0.4}/LICENSE +0 -0
  7. {code_towel-1.0.3 → code_towel-1.0.4}/README.md +0 -0
  8. {code_towel-1.0.3 → code_towel-1.0.4}/setup.cfg +0 -0
  9. {code_towel-1.0.3 → code_towel-1.0.4}/src/code_towel.egg-info/SOURCES.txt +0 -0
  10. {code_towel-1.0.3 → code_towel-1.0.4}/src/code_towel.egg-info/dependency_links.txt +0 -0
  11. {code_towel-1.0.3 → code_towel-1.0.4}/src/code_towel.egg-info/entry_points.txt +0 -0
  12. {code_towel-1.0.3 → code_towel-1.0.4}/src/code_towel.egg-info/requires.txt +0 -0
  13. {code_towel-1.0.3 → code_towel-1.0.4}/src/code_towel.egg-info/top_level.txt +0 -0
  14. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/__init__.py +0 -0
  15. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/__init__.py +0 -0
  16. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/assignment_analyzer.py +0 -0
  17. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/ast_normalizer.py +0 -0
  18. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/ast_pretty_printer.py +0 -0
  19. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/binding_detector.py +0 -0
  20. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/block_signature.py +0 -0
  21. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/builtins.py +0 -0
  22. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/exceptions.py +0 -0
  23. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/extractor.py +0 -0
  24. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/models.py +0 -0
  25. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/nominal_unifier.py +0 -0
  26. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/orphan_detector.py +0 -0
  27. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/pipeline.py +0 -0
  28. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/project_layout.py +0 -0
  29. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/scope_analyzer.py +0 -0
  30. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/unifier.py +0 -0
  31. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/visitor_utils.py +0 -0
  32. {code_towel-1.0.3 → code_towel-1.0.4}/src/towel/unification/visitors.py +0 -0
  33. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_ancestor_insertion.py +0 -0
  34. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_assignment_analyzer_comprehensive.py +0 -0
  35. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_ast_normalizer_comprehensive.py +0 -0
  36. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_ast_pretty_printer.py +0 -0
  37. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_ast_pretty_printer_comprehensive.py +0 -0
  38. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_ast_pretty_printer_more.py +0 -0
  39. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_binding_detector.py +0 -0
  40. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_binding_detector_edge_cases.py +0 -0
  41. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_bindings.py +0 -0
  42. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_bindings_additional.py +0 -0
  43. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_breakers.py +0 -0
  44. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_builtins_comprehensive.py +0 -0
  45. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_comprehensive_coverage.py +0 -0
  46. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_crossfile_integration.py +0 -0
  47. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_crossfile_observational_equivalence.py +0 -0
  48. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_engine_adversarial.py +0 -0
  49. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_exceptions.py +0 -0
  50. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_additional_branches.py +0 -0
  51. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_augassign_and_fstrings.py +0 -0
  52. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_callee_and_multi_return.py +0 -0
  53. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_comprehensive.py +0 -0
  54. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_edge_cases.py +0 -0
  55. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_final_lines.py +0 -0
  56. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_generate_call_variants.py +0 -0
  57. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_more_paths.py +0 -0
  58. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_preamble_and_call_mapping.py +0 -0
  59. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_remaining_branches.py +0 -0
  60. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_return_statements.py +0 -0
  61. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_extractor_substituter_and_helpers.py +0 -0
  62. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_final_coverage_push.py +0 -0
  63. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_free_variable_correspondence.py +0 -0
  64. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_fstrings.py +0 -0
  65. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_helpers.py +0 -0
  66. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_method_level_and_attributes.py +0 -0
  67. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_nominal_unifier.py +0 -0
  68. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_observational_equivalence.py +0 -0
  69. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_orphan_detection.py +0 -0
  70. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_orphan_detector_comprehensive.py +0 -0
  71. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_overlap_filtering.py +0 -0
  72. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_pipeline_api.py +0 -0
  73. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_pipeline_phases.py +0 -0
  74. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_progress_modes.py +0 -0
  75. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_project_layout_and_imports.py +0 -0
  76. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_project_layout_behavior.py +0 -0
  77. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_promotion_and_mangling.py +0 -0
  78. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_refactor_engine_comprehensive.py +0 -0
  79. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_refactor_engine_edge_cases.py +0 -0
  80. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_refactor_engine_more.py +0 -0
  81. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_refactor_engine_targeted_branches.py +0 -0
  82. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_refactoring_engine.py +0 -0
  83. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_regression.py +0 -0
  84. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_regressions_broader.py +0 -0
  85. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_return_values.py +0 -0
  86. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_scope_analyzer.py +0 -0
  87. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_scope_analyzer_comprehensive.py +0 -0
  88. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_signature_prefilter.py +0 -0
  89. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_additional_branches.py +0 -0
  90. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_batch2.py +0 -0
  91. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_batch3.py +0 -0
  92. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_batch4.py +0 -0
  93. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_batch5.py +0 -0
  94. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_bound_variables.py +0 -0
  95. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_comprehensions.py +0 -0
  96. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_comprehensive.py +0 -0
  97. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_core.py +0 -0
  98. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_edge_cases.py +0 -0
  99. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_more_branches.py +0 -0
  100. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_unifier_state_reset.py +0 -0
  101. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_validation_partial_lifetime.py +0 -0
  102. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_variable_capture_bug.py +0 -0
  103. {code_towel-1.0.3 → code_towel-1.0.4}/tests/test_visitor_utils_comprehensive.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-towel
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Summary: A Python tool that DRYs your code - finds and refactors repeated code using unification-based analysis
5
5
  Author: Eric Allen
6
6
  License: Apache License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code-towel"
7
- version = "1.0.3"
7
+ version = "1.0.4"
8
8
  description = "A Python tool that DRYs your code - finds and refactors repeated code using unification-based analysis"
9
9
  authors = [
10
10
  {name = "Eric Allen"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-towel
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Summary: A Python tool that DRYs your code - finds and refactors repeated code using unification-based analysis
5
5
  Author: Eric Allen
6
6
  License: Apache License
@@ -700,7 +700,9 @@ def _run_interactive_llm_mode(
700
700
  print(' {"__extracted_func_1": "calculate_total"}')
701
701
  print(' {"src/utils.py:__extracted_func_2": "validate_input"}')
702
702
  print()
703
- print("Paste the JSON response below, then press ENTER followed by Ctrl+D (or Ctrl+Z on Windows):")
703
+ print(
704
+ "Paste the JSON response below, then press ENTER followed by Ctrl+D (or Ctrl+Z on Windows):"
705
+ )
704
706
  print()
705
707
 
706
708
  # Read LLM response from stdin
@@ -538,6 +538,7 @@ class UnificationRefactorEngine:
538
538
  docstring_char: Optional[str] = None
539
539
  last_import_line = 0
540
540
  after_docstring = 0
541
+ in_multiline_import = False
541
542
 
542
543
  for i, line in enumerate(lines):
543
544
  stripped = line.strip()
@@ -557,8 +558,19 @@ class UnificationRefactorEngine:
557
558
  after_docstring = i + 1
558
559
  continue
559
560
 
561
+ # Check for start of multi-line import (has opening paren but no closing paren)
560
562
  if stripped.startswith("import ") or stripped.startswith("from "):
561
563
  last_import_line = i + 1
564
+ # Check if this is a multi-line import
565
+ if "(" in line and ")" not in line:
566
+ in_multiline_import = True
567
+ continue
568
+
569
+ # Inside a multi-line import - continue until we see closing paren
570
+ if in_multiline_import:
571
+ last_import_line = i + 1
572
+ if ")" in line:
573
+ in_multiline_import = False
562
574
  continue
563
575
 
564
576
  if last_import_line > 0 and stripped and not stripped.startswith("#"):
File without changes
File without changes
File without changes