code-towel 1.0.2__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.
- {code_towel-1.0.2/src/code_towel.egg-info → code_towel-1.0.4}/PKG-INFO +11 -1
- {code_towel-1.0.2 → code_towel-1.0.4}/README.md +10 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/pyproject.toml +1 -1
- {code_towel-1.0.2 → code_towel-1.0.4/src/code_towel.egg-info}/PKG-INFO +11 -1
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/cli.py +53 -13
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/refactor_engine.py +12 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/LICENSE +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/setup.cfg +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/code_towel.egg-info/SOURCES.txt +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/code_towel.egg-info/dependency_links.txt +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/code_towel.egg-info/entry_points.txt +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/code_towel.egg-info/requires.txt +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/code_towel.egg-info/top_level.txt +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/__init__.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/__init__.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/assignment_analyzer.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/ast_normalizer.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/ast_pretty_printer.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/binding_detector.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/block_signature.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/builtins.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/exceptions.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/extractor.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/models.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/nominal_unifier.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/orphan_detector.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/pipeline.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/project_layout.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/scope_analyzer.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/unifier.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/visitor_utils.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/src/towel/unification/visitors.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_ancestor_insertion.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_assignment_analyzer_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_ast_normalizer_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_ast_pretty_printer.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_ast_pretty_printer_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_ast_pretty_printer_more.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_binding_detector.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_binding_detector_edge_cases.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_bindings.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_bindings_additional.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_breakers.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_builtins_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_comprehensive_coverage.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_crossfile_integration.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_crossfile_observational_equivalence.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_engine_adversarial.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_exceptions.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_additional_branches.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_augassign_and_fstrings.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_callee_and_multi_return.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_edge_cases.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_final_lines.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_generate_call_variants.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_more_paths.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_preamble_and_call_mapping.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_remaining_branches.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_return_statements.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_extractor_substituter_and_helpers.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_final_coverage_push.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_free_variable_correspondence.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_fstrings.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_helpers.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_method_level_and_attributes.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_nominal_unifier.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_observational_equivalence.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_orphan_detection.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_orphan_detector_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_overlap_filtering.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_pipeline_api.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_pipeline_phases.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_progress_modes.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_project_layout_and_imports.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_project_layout_behavior.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_promotion_and_mangling.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_refactor_engine_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_refactor_engine_edge_cases.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_refactor_engine_more.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_refactor_engine_targeted_branches.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_refactoring_engine.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_regression.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_regressions_broader.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_return_values.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_scope_analyzer.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_scope_analyzer_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_signature_prefilter.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_additional_branches.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_batch2.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_batch3.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_batch4.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_batch5.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_bound_variables.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_comprehensions.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_comprehensive.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_core.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_edge_cases.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_more_branches.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_unifier_state_reset.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_validation_partial_lifetime.py +0 -0
- {code_towel-1.0.2 → code_towel-1.0.4}/tests/test_variable_capture_bug.py +0 -0
- {code_towel-1.0.2 → 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
|
+
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
|
|
@@ -363,6 +363,16 @@ pip install -e ".[dev]"
|
|
|
363
363
|
pip install black flake8 mypy coverage
|
|
364
364
|
```
|
|
365
365
|
|
|
366
|
+
### Optional: Enhanced Progress Bars
|
|
367
|
+
|
|
368
|
+
For nicer progress bars during analysis, you can optionally install `tqdm`:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
pip install tqdm
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Without `tqdm`, Towel still works perfectly with zero dependencies - it just uses simpler progress indicators.
|
|
375
|
+
|
|
366
376
|
## Usage
|
|
367
377
|
|
|
368
378
|
### Preview Mode (Read-Only)
|
|
@@ -132,6 +132,16 @@ pip install -e ".[dev]"
|
|
|
132
132
|
pip install black flake8 mypy coverage
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
+
### Optional: Enhanced Progress Bars
|
|
136
|
+
|
|
137
|
+
For nicer progress bars during analysis, you can optionally install `tqdm`:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pip install tqdm
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Without `tqdm`, Towel still works perfectly with zero dependencies - it just uses simpler progress indicators.
|
|
144
|
+
|
|
135
145
|
## Usage
|
|
136
146
|
|
|
137
147
|
### Preview Mode (Read-Only)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code-towel"
|
|
7
|
-
version = "1.0.
|
|
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
|
+
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
|
|
@@ -363,6 +363,16 @@ pip install -e ".[dev]"
|
|
|
363
363
|
pip install black flake8 mypy coverage
|
|
364
364
|
```
|
|
365
365
|
|
|
366
|
+
### Optional: Enhanced Progress Bars
|
|
367
|
+
|
|
368
|
+
For nicer progress bars during analysis, you can optionally install `tqdm`:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
pip install tqdm
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Without `tqdm`, Towel still works perfectly with zero dependencies - it just uses simpler progress indicators.
|
|
375
|
+
|
|
366
376
|
## Usage
|
|
367
377
|
|
|
368
378
|
### Preview Mode (Read-Only)
|
|
@@ -622,8 +622,17 @@ def _rename_function_in_directory(
|
|
|
622
622
|
old_name: str,
|
|
623
623
|
new_name: str,
|
|
624
624
|
dry_run: bool,
|
|
625
|
+
file_filter: Optional[Path] = None,
|
|
625
626
|
) -> int:
|
|
626
|
-
"""Rename a function throughout all Python files in directory.
|
|
627
|
+
"""Rename a function throughout all Python files in directory.
|
|
628
|
+
|
|
629
|
+
Args:
|
|
630
|
+
target: Directory to search in
|
|
631
|
+
old_name: Current function name
|
|
632
|
+
new_name: New function name
|
|
633
|
+
dry_run: If True, don't actually modify files
|
|
634
|
+
file_filter: If provided, only rename in this specific file
|
|
635
|
+
"""
|
|
627
636
|
import re
|
|
628
637
|
|
|
629
638
|
# Pattern to match function definitions and calls
|
|
@@ -636,6 +645,10 @@ def _rename_function_in_directory(
|
|
|
636
645
|
total_replacements = 0
|
|
637
646
|
|
|
638
647
|
for py_file in target.rglob("*.py"):
|
|
648
|
+
# If file_filter is specified, only process that file
|
|
649
|
+
if file_filter and py_file != file_filter:
|
|
650
|
+
continue
|
|
651
|
+
|
|
639
652
|
try:
|
|
640
653
|
content = py_file.read_text()
|
|
641
654
|
new_content = content
|
|
@@ -678,13 +691,19 @@ def _run_interactive_llm_mode(
|
|
|
678
691
|
print("=" * 70 + "\n")
|
|
679
692
|
|
|
680
693
|
print("After the LLM provides suggestions, paste the JSON response below.")
|
|
681
|
-
print("The response should be a JSON object mapping old names to new names
|
|
694
|
+
print("The response should be a JSON object mapping old names to new names.")
|
|
695
|
+
print("You can use file-qualified names (e.g., 'path/to/file.py:func_name') to")
|
|
696
|
+
print("rename functions only in specific files, or just the function name to rename")
|
|
697
|
+
print("across all files.")
|
|
698
|
+
print()
|
|
699
|
+
print("Example:")
|
|
700
|
+
print(' {"__extracted_func_1": "calculate_total"}')
|
|
701
|
+
print(' {"src/utils.py:__extracted_func_2": "validate_input"}')
|
|
702
|
+
print()
|
|
682
703
|
print(
|
|
683
|
-
|
|
704
|
+
"Paste the JSON response below, then press ENTER followed by Ctrl+D (or Ctrl+Z on Windows):"
|
|
684
705
|
)
|
|
685
706
|
print()
|
|
686
|
-
print("Paste the JSON response (press Ctrl+D or Ctrl+Z when done):")
|
|
687
|
-
print()
|
|
688
707
|
|
|
689
708
|
# Read LLM response from stdin
|
|
690
709
|
try:
|
|
@@ -732,15 +751,31 @@ def _run_interactive_llm_mode(
|
|
|
732
751
|
|
|
733
752
|
# Apply renamings
|
|
734
753
|
total_changes = 0
|
|
735
|
-
for
|
|
754
|
+
for old_name_spec, new_name in renames.items():
|
|
736
755
|
if not isinstance(new_name, str) or not new_name:
|
|
737
|
-
print(f"Warning: Skipping invalid mapping {
|
|
756
|
+
print(f"Warning: Skipping invalid mapping {old_name_spec} -> {new_name}")
|
|
738
757
|
continue
|
|
739
758
|
|
|
740
|
-
|
|
759
|
+
# Parse file-qualified names (e.g., "path/to/file.py:function_name")
|
|
760
|
+
file_filter = None
|
|
761
|
+
if ":" in old_name_spec:
|
|
762
|
+
file_path_str, old_name = old_name_spec.rsplit(":", 1)
|
|
763
|
+
# Resolve file path relative to target
|
|
764
|
+
file_filter = target / file_path_str
|
|
765
|
+
if not file_filter.exists():
|
|
766
|
+
print(f"Warning: File not found: {file_filter}")
|
|
767
|
+
print(f" Skipping: {old_name_spec} -> {new_name}")
|
|
768
|
+
continue
|
|
769
|
+
else:
|
|
770
|
+
old_name = old_name_spec
|
|
771
|
+
|
|
772
|
+
count = _rename_function_in_directory(target, old_name, new_name, dry_run, file_filter)
|
|
741
773
|
if count > 0:
|
|
742
774
|
total_changes += count
|
|
743
|
-
|
|
775
|
+
if file_filter:
|
|
776
|
+
print(f" ✓ {old_name_spec} -> {new_name} ({count} replacement(s))")
|
|
777
|
+
else:
|
|
778
|
+
print(f" ✓ {old_name} -> {new_name} ({count} replacement(s))")
|
|
744
779
|
|
|
745
780
|
if dry_run:
|
|
746
781
|
print(f"\n[DRY RUN] Would make {total_changes} change(s)")
|
|
@@ -778,13 +813,18 @@ Here are the extracted functions:
|
|
|
778
813
|
|
|
779
814
|
output_format = """
|
|
780
815
|
|
|
781
|
-
Please provide your suggestions as a JSON object mapping old names to new names.
|
|
816
|
+
Please provide your suggestions as a JSON object mapping old names to new names.
|
|
817
|
+
|
|
818
|
+
If the same function name appears in multiple files, use file-qualified names (path:function_name)
|
|
819
|
+
to rename them individually. Otherwise, you can use just the function name to rename across all files.
|
|
820
|
+
|
|
821
|
+
For example:
|
|
782
822
|
|
|
783
823
|
```json
|
|
784
824
|
{
|
|
785
|
-
"__extracted_func_1": "calculate_total_price",
|
|
786
|
-
"
|
|
787
|
-
"
|
|
825
|
+
"src/utils.py:__extracted_func_1": "calculate_total_price",
|
|
826
|
+
"src/validators.py:__extracted_func_1": "validate_email_format",
|
|
827
|
+
"__extracted_func_2": "format_date_string"
|
|
788
828
|
}
|
|
789
829
|
```
|
|
790
830
|
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|