code-normalizer-pro 3.0.1__tar.gz → 3.0.2__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_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/LICENSE +1 -2
- code_normalizer_pro-3.0.2/PKG-INFO +24 -0
- code_normalizer_pro-3.0.2/README.md +11 -0
- {code_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/code_normalizer_pro/__init__.py +4 -5
- code_normalizer_pro-3.0.2/code_normalizer_pro/cli.py +26 -0
- code_normalizer_pro-3.0.1/code_normalizer_pro/code_normalize_pro.py → code_normalizer_pro-3.0.2/code_normalizer_pro/code_normalizer_pro.py +101 -34
- code_normalizer_pro-3.0.2/code_normalizer_pro.egg-info/PKG-INFO +24 -0
- {code_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/code_normalizer_pro.egg-info/SOURCES.txt +2 -7
- code_normalizer_pro-3.0.2/code_normalizer_pro.egg-info/requires.txt +1 -0
- code_normalizer_pro-3.0.2/pyproject.toml +19 -0
- {code_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/tests/test_code_normalize_pro.py +50 -6
- code_normalizer_pro-3.0.1/PKG-INFO +0 -304
- code_normalizer_pro-3.0.1/README.md +0 -277
- code_normalizer_pro-3.0.1/code_normalizer_pro/cli.py +0 -42
- code_normalizer_pro-3.0.1/code_normalizer_pro.egg-info/PKG-INFO +0 -304
- code_normalizer_pro-3.0.1/code_normalizer_pro.egg-info/requires.txt +0 -4
- code_normalizer_pro-3.0.1/main.py +0 -16
- code_normalizer_pro-3.0.1/pyproject.toml +0 -48
- code_normalizer_pro-3.0.1/tests/test_feedback_prioritizer.py +0 -49
- code_normalizer_pro-3.0.1/tests/test_launch_metrics.py +0 -42
- code_normalizer_pro-3.0.1/tests/test_release_prep.py +0 -41
- code_normalizer_pro-3.0.1/tests/test_sales_pipeline_metrics.py +0 -40
- {code_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/code_normalizer_pro.egg-info/dependency_links.txt +0 -0
- {code_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/code_normalizer_pro.egg-info/entry_points.txt +0 -0
- {code_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/code_normalizer_pro.egg-info/top_level.txt +0 -0
- {code_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 Michael Rawls Jr.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
-
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-normalizer-pro
|
|
3
|
+
Version: 3.0.2
|
|
4
|
+
Summary: Production-grade code normalization tool for encoding, newlines, and whitespace hygiene.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/MRJR0101/Code-Normalizer-Pro
|
|
7
|
+
Keywords: code-quality,formatter,normalization,cli,python
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: tqdm
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# code-normalizer-pro
|
|
15
|
+
|
|
16
|
+
A CLI tool that normalizes encoding, line endings, and whitespace across a codebase.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
pip install code-normalizer-pro
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
code-normalizer-pro . --dry-run -e .py
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""Package metadata for code-normalizer-pro."""
|
|
2
|
-
|
|
3
|
-
__all__ = ["__version__"]
|
|
4
|
-
__version__ = "3.0.
|
|
5
|
-
|
|
1
|
+
"""Package metadata for code-normalizer-pro."""
|
|
2
|
+
|
|
3
|
+
__all__ = ["__version__"]
|
|
4
|
+
__version__ = "3.0.2"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Console entry point for the code-normalizer-pro installed package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import runpy
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _find_script() -> Path:
|
|
11
|
+
"""Return the path to code_normalizer_pro.py."""
|
|
12
|
+
pkg_dir = Path(__file__).resolve().parent
|
|
13
|
+
candidate = pkg_dir / "code_normalizer_pro.py"
|
|
14
|
+
if candidate.is_file():
|
|
15
|
+
return candidate
|
|
16
|
+
raise FileNotFoundError(f"code_normalizer_pro.py not found at: {candidate}")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main() -> None:
|
|
20
|
+
script = _find_script()
|
|
21
|
+
sys.argv[0] = str(script)
|
|
22
|
+
runpy.run_path(str(script), run_name="__main__")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
main()
|
|
@@ -27,6 +27,7 @@ import os
|
|
|
27
27
|
import hashlib
|
|
28
28
|
import json
|
|
29
29
|
import shutil
|
|
30
|
+
import tempfile
|
|
30
31
|
from pathlib import Path
|
|
31
32
|
from typing import Optional, Tuple, List, Dict, Set
|
|
32
33
|
from dataclasses import dataclass, asdict
|
|
@@ -66,7 +67,7 @@ COMMON_ENCODINGS = [
|
|
|
66
67
|
# Multi-language syntax checkers
|
|
67
68
|
SYNTAX_CHECKERS = {
|
|
68
69
|
".py": {
|
|
69
|
-
"command": [
|
|
70
|
+
"command": [sys.executable, "-m", "py_compile"],
|
|
70
71
|
"stdin": False,
|
|
71
72
|
"file_arg": True,
|
|
72
73
|
},
|
|
@@ -215,7 +216,8 @@ class CodeNormalizer:
|
|
|
215
216
|
use_cache: bool = True,
|
|
216
217
|
interactive: bool = False,
|
|
217
218
|
parallel: bool = False,
|
|
218
|
-
max_workers: Optional[int] = None
|
|
219
|
+
max_workers: Optional[int] = None,
|
|
220
|
+
cache_path: Optional[Path] = None):
|
|
219
221
|
self.dry_run = dry_run
|
|
220
222
|
self.verbose = verbose
|
|
221
223
|
self.in_place = in_place
|
|
@@ -224,9 +226,30 @@ class CodeNormalizer:
|
|
|
224
226
|
self.interactive = interactive
|
|
225
227
|
self.parallel = parallel
|
|
226
228
|
self.max_workers = max_workers or max(1, cpu_count() - 1)
|
|
229
|
+
self.cache_path_override = cache_path
|
|
227
230
|
self.stats = ProcessStats()
|
|
228
231
|
self.errors: List[Tuple[Path, str]] = []
|
|
229
|
-
self.cache = CacheManager() if use_cache else None
|
|
232
|
+
self.cache = CacheManager(cache_path) if use_cache and cache_path else None
|
|
233
|
+
|
|
234
|
+
def _resolve_cache_path(self, target: Path) -> Path:
|
|
235
|
+
"""Place cache beside the project or file being processed."""
|
|
236
|
+
if self.cache_path_override:
|
|
237
|
+
return self.cache_path_override
|
|
238
|
+
base = target if target.is_dir() else target.parent
|
|
239
|
+
return base / CACHE_FILE
|
|
240
|
+
|
|
241
|
+
def _ensure_cache_manager(self, target: Path) -> None:
|
|
242
|
+
"""Bind cache storage to the current processing target."""
|
|
243
|
+
if not self.use_cache:
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
desired_path = self._resolve_cache_path(target)
|
|
247
|
+
if self.cache is None or self.cache.cache_path != desired_path:
|
|
248
|
+
self.cache = CacheManager(desired_path)
|
|
249
|
+
|
|
250
|
+
def _should_show_progress(self) -> bool:
|
|
251
|
+
"""Avoid tqdm collisions with verbose per-file output."""
|
|
252
|
+
return HAS_TQDM and not self.interactive and not self.verbose
|
|
230
253
|
|
|
231
254
|
def _looks_like_utf16_text(self, data: bytes) -> bool:
|
|
232
255
|
"""Best-effort check for UTF-16 text before binary rejection."""
|
|
@@ -320,8 +343,8 @@ class CodeNormalizer:
|
|
|
320
343
|
|
|
321
344
|
return text, changes
|
|
322
345
|
|
|
323
|
-
def
|
|
324
|
-
"""Run syntax
|
|
346
|
+
def _run_syntax_check(self, path: Path, content: Optional[str] = None) -> Tuple[bool, str]:
|
|
347
|
+
"""Run a syntax checker against a file or normalized text buffer."""
|
|
325
348
|
ext = path.suffix.lower()
|
|
326
349
|
|
|
327
350
|
if ext not in SYNTAX_CHECKERS:
|
|
@@ -329,10 +352,24 @@ class CodeNormalizer:
|
|
|
329
352
|
|
|
330
353
|
checker = SYNTAX_CHECKERS[ext]
|
|
331
354
|
cmd = checker['command'].copy()
|
|
355
|
+
temp_path: Optional[Path] = None
|
|
332
356
|
|
|
333
357
|
try:
|
|
334
358
|
if checker['file_arg']:
|
|
335
|
-
|
|
359
|
+
target_path = path
|
|
360
|
+
if content is not None:
|
|
361
|
+
with tempfile.NamedTemporaryFile(
|
|
362
|
+
"w",
|
|
363
|
+
encoding="utf-8",
|
|
364
|
+
newline="\n",
|
|
365
|
+
suffix=path.suffix,
|
|
366
|
+
delete=False,
|
|
367
|
+
) as tmp:
|
|
368
|
+
tmp.write(content)
|
|
369
|
+
temp_path = Path(tmp.name)
|
|
370
|
+
target_path = temp_path
|
|
371
|
+
|
|
372
|
+
cmd.append(str(target_path))
|
|
336
373
|
result = subprocess.run(
|
|
337
374
|
cmd,
|
|
338
375
|
stdout=subprocess.DEVNULL,
|
|
@@ -342,8 +379,9 @@ class CodeNormalizer:
|
|
|
342
379
|
)
|
|
343
380
|
else:
|
|
344
381
|
# Read file and pass via stdin
|
|
345
|
-
|
|
346
|
-
|
|
382
|
+
if content is None:
|
|
383
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
384
|
+
content = f.read()
|
|
347
385
|
result = subprocess.run(
|
|
348
386
|
cmd,
|
|
349
387
|
input=content,
|
|
@@ -364,6 +402,17 @@ class CodeNormalizer:
|
|
|
364
402
|
return False, "Timeout"
|
|
365
403
|
except Exception as e:
|
|
366
404
|
return False, str(e)[:100]
|
|
405
|
+
finally:
|
|
406
|
+
if temp_path and temp_path.exists():
|
|
407
|
+
temp_path.unlink(missing_ok=True)
|
|
408
|
+
|
|
409
|
+
def syntax_check(self, path: Path, language: Optional[str] = None) -> Tuple[bool, str]:
|
|
410
|
+
"""Run syntax check on file - multi-language support"""
|
|
411
|
+
return self._run_syntax_check(path)
|
|
412
|
+
|
|
413
|
+
def syntax_check_text(self, path: Path, text: str) -> Tuple[bool, str]:
|
|
414
|
+
"""Syntax check normalized content without writing it to the real file."""
|
|
415
|
+
return self._run_syntax_check(path, content=text)
|
|
367
416
|
|
|
368
417
|
def create_backup_file(self, path: Path) -> Path:
|
|
369
418
|
"""Create timestamped backup"""
|
|
@@ -432,6 +481,8 @@ class CodeNormalizer:
|
|
|
432
481
|
self.stats.total_files += 1
|
|
433
482
|
|
|
434
483
|
try:
|
|
484
|
+
self._ensure_cache_manager(path)
|
|
485
|
+
|
|
435
486
|
# Check cache first (incremental processing)
|
|
436
487
|
if self.use_cache and self.cache and self.cache.is_cached(path):
|
|
437
488
|
if self.verbose:
|
|
@@ -449,8 +500,11 @@ class CodeNormalizer:
|
|
|
449
500
|
# Determine output
|
|
450
501
|
out_path = self.get_output_path(path, output_path)
|
|
451
502
|
|
|
452
|
-
|
|
453
|
-
|
|
503
|
+
needs_encoding_fix = enc != "utf-8"
|
|
504
|
+
needs_content_fix = text != normalized
|
|
505
|
+
|
|
506
|
+
# Check if any normalization work is needed
|
|
507
|
+
if not needs_content_fix and not needs_encoding_fix:
|
|
454
508
|
if self.verbose:
|
|
455
509
|
print(f"⊗ SKIP {path.name} - already normalized")
|
|
456
510
|
self.stats.skipped += 1
|
|
@@ -483,6 +537,15 @@ class CodeNormalizer:
|
|
|
483
537
|
if changes['final_newline_added']:
|
|
484
538
|
print(f" Final newline: added")
|
|
485
539
|
|
|
540
|
+
if check_syntax:
|
|
541
|
+
ok, reason = self.syntax_check_text(path, normalized)
|
|
542
|
+
status = "✓ OK" if ok else f"✗ {reason}"
|
|
543
|
+
print(f" Syntax: {status}")
|
|
544
|
+
if ok:
|
|
545
|
+
self.stats.syntax_checks_passed += 1
|
|
546
|
+
else:
|
|
547
|
+
self.stats.syntax_checks_failed += 1
|
|
548
|
+
|
|
486
549
|
self.stats.bytes_removed += changes['bytes_removed']
|
|
487
550
|
self.stats.processed += 1
|
|
488
551
|
return True
|
|
@@ -545,6 +608,8 @@ class CodeNormalizer:
|
|
|
545
608
|
def walk_and_process(self, root: Path, exts: List[str],
|
|
546
609
|
check_syntax: bool = False) -> None:
|
|
547
610
|
"""Process all files in directory tree"""
|
|
611
|
+
self._ensure_cache_manager(root)
|
|
612
|
+
|
|
548
613
|
# Collect files
|
|
549
614
|
files = []
|
|
550
615
|
for ext in exts:
|
|
@@ -592,7 +657,10 @@ class CodeNormalizer:
|
|
|
592
657
|
|
|
593
658
|
# Confirmation
|
|
594
659
|
if not self.dry_run and self.in_place and not self.interactive:
|
|
595
|
-
response = input(
|
|
660
|
+
response = input(
|
|
661
|
+
f"\n⚠️ In-place editing will scan {len(files_to_process)} file(s) "
|
|
662
|
+
"and modify only files that need changes. Continue? (y/N): "
|
|
663
|
+
)
|
|
596
664
|
if response.lower() != 'y':
|
|
597
665
|
print("Cancelled")
|
|
598
666
|
return
|
|
@@ -610,7 +678,7 @@ class CodeNormalizer:
|
|
|
610
678
|
|
|
611
679
|
def _process_sequential(self, files: List[Path], check_syntax: bool):
|
|
612
680
|
"""Process files sequentially"""
|
|
613
|
-
iterator = tqdm(files, desc="Processing") if
|
|
681
|
+
iterator = tqdm(files, desc="Processing") if self._should_show_progress() else files
|
|
614
682
|
|
|
615
683
|
for file_path in iterator:
|
|
616
684
|
self.process_file(file_path, check_syntax=check_syntax)
|
|
@@ -635,7 +703,7 @@ class CodeNormalizer:
|
|
|
635
703
|
|
|
636
704
|
# Progress tracking
|
|
637
705
|
iterator = as_completed(futures)
|
|
638
|
-
if
|
|
706
|
+
if self._should_show_progress():
|
|
639
707
|
iterator = tqdm(iterator, total=len(files), desc="Processing")
|
|
640
708
|
|
|
641
709
|
# Collect results
|
|
@@ -744,7 +812,7 @@ def install_git_hook(hook_type: str = "pre-commit") -> bool:
|
|
|
744
812
|
|
|
745
813
|
# Create hook script
|
|
746
814
|
hook_script = f"""#!/usr/bin/env python3
|
|
747
|
-
# Auto-generated by
|
|
815
|
+
# Auto-generated by code_normalizer_pro.py
|
|
748
816
|
import subprocess
|
|
749
817
|
import sys
|
|
750
818
|
from pathlib import Path
|
|
@@ -790,7 +858,7 @@ def main():
|
|
|
790
858
|
print("\\n⚠️ Some files need normalization:")
|
|
791
859
|
for file_path in needs_normalization:
|
|
792
860
|
print(f" - {{file_path}}")
|
|
793
|
-
print("\\nRun:
|
|
861
|
+
print("\\nRun: uv run code-normalizer-pro <file> --in-place")
|
|
794
862
|
print("Or add --no-verify to skip this check")
|
|
795
863
|
sys.exit(1)
|
|
796
864
|
|
|
@@ -814,27 +882,28 @@ if __name__ == "__main__":
|
|
|
814
882
|
|
|
815
883
|
def main():
|
|
816
884
|
ap = argparse.ArgumentParser(
|
|
885
|
+
prog="code-normalizer-pro",
|
|
817
886
|
description="Code Normalizer Pro - Production-grade normalization tool",
|
|
818
887
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
819
888
|
epilog="""
|
|
820
889
|
Examples:
|
|
821
890
|
# Dry run with parallel processing
|
|
822
|
-
|
|
891
|
+
uv run code-normalizer-pro /path/to/dir --dry-run --parallel
|
|
823
892
|
|
|
824
893
|
# Interactive mode (file-by-file approval)
|
|
825
|
-
|
|
894
|
+
uv run code-normalizer-pro /path/to/dir --interactive
|
|
826
895
|
|
|
827
|
-
# In-place
|
|
828
|
-
|
|
896
|
+
# In-place normalization (cache is enabled by default)
|
|
897
|
+
uv run code-normalizer-pro /path/to/dir -e .py --in-place
|
|
829
898
|
|
|
830
|
-
# Multi-language syntax checking
|
|
831
|
-
|
|
899
|
+
# Multi-language syntax checking on normalized output
|
|
900
|
+
uv run code-normalizer-pro /path/to/dir -e .py -e .js -e .go --check
|
|
832
901
|
|
|
833
902
|
# Install git pre-commit hook
|
|
834
|
-
|
|
903
|
+
uv run code-normalizer-pro --install-hook
|
|
835
904
|
|
|
836
905
|
# Parallel processing (all cores)
|
|
837
|
-
|
|
906
|
+
uv run code-normalizer-pro /path/to/dir --parallel --in-place
|
|
838
907
|
"""
|
|
839
908
|
)
|
|
840
909
|
|
|
@@ -857,7 +926,7 @@ Examples:
|
|
|
857
926
|
ap.add_argument(
|
|
858
927
|
"--check",
|
|
859
928
|
action="store_true",
|
|
860
|
-
help="Run syntax check
|
|
929
|
+
help="Run syntax check on normalized output"
|
|
861
930
|
)
|
|
862
931
|
ap.add_argument(
|
|
863
932
|
"--dry-run",
|
|
@@ -874,15 +943,16 @@ Examples:
|
|
|
874
943
|
action="store_true",
|
|
875
944
|
help="Don't create backups (dangerous!)"
|
|
876
945
|
)
|
|
877
|
-
ap.
|
|
946
|
+
cache_group = ap.add_mutually_exclusive_group()
|
|
947
|
+
cache_group.add_argument(
|
|
878
948
|
"--cache",
|
|
879
949
|
action="store_true",
|
|
880
|
-
help="
|
|
950
|
+
help="Enable incremental processing cache (default)"
|
|
881
951
|
)
|
|
882
|
-
|
|
952
|
+
cache_group.add_argument(
|
|
883
953
|
"--no-cache",
|
|
884
954
|
action="store_true",
|
|
885
|
-
help="Disable
|
|
955
|
+
help="Disable incremental processing cache"
|
|
886
956
|
)
|
|
887
957
|
ap.add_argument(
|
|
888
958
|
"--interactive",
|
|
@@ -934,11 +1004,7 @@ Examples:
|
|
|
934
1004
|
args.parallel = False
|
|
935
1005
|
|
|
936
1006
|
# Determine cache setting
|
|
937
|
-
use_cache =
|
|
938
|
-
if args.no_cache:
|
|
939
|
-
use_cache = False
|
|
940
|
-
elif args.cache:
|
|
941
|
-
use_cache = True
|
|
1007
|
+
use_cache = not args.no_cache
|
|
942
1008
|
|
|
943
1009
|
# Create normalizer
|
|
944
1010
|
normalizer = CodeNormalizer(
|
|
@@ -949,7 +1015,8 @@ Examples:
|
|
|
949
1015
|
use_cache=use_cache,
|
|
950
1016
|
interactive=args.interactive,
|
|
951
1017
|
parallel=args.parallel,
|
|
952
|
-
max_workers=args.workers
|
|
1018
|
+
max_workers=args.workers,
|
|
1019
|
+
cache_path=(args.path / CACHE_FILE) if args.path and args.path.is_dir() else (args.path.parent / CACHE_FILE)
|
|
953
1020
|
)
|
|
954
1021
|
|
|
955
1022
|
print("="*70)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-normalizer-pro
|
|
3
|
+
Version: 3.0.2
|
|
4
|
+
Summary: Production-grade code normalization tool for encoding, newlines, and whitespace hygiene.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/MRJR0101/Code-Normalizer-Pro
|
|
7
|
+
Keywords: code-quality,formatter,normalization,cli,python
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: tqdm
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# code-normalizer-pro
|
|
15
|
+
|
|
16
|
+
A CLI tool that normalizes encoding, line endings, and whitespace across a codebase.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
pip install code-normalizer-pro
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
code-normalizer-pro . --dry-run -e .py
|
{code_normalizer_pro-3.0.1 → code_normalizer_pro-3.0.2}/code_normalizer_pro.egg-info/SOURCES.txt
RENAMED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
README.md
|
|
3
|
-
main.py
|
|
4
3
|
pyproject.toml
|
|
5
4
|
code_normalizer_pro/__init__.py
|
|
6
5
|
code_normalizer_pro/cli.py
|
|
7
|
-
code_normalizer_pro/
|
|
6
|
+
code_normalizer_pro/code_normalizer_pro.py
|
|
8
7
|
code_normalizer_pro.egg-info/PKG-INFO
|
|
9
8
|
code_normalizer_pro.egg-info/SOURCES.txt
|
|
10
9
|
code_normalizer_pro.egg-info/dependency_links.txt
|
|
11
10
|
code_normalizer_pro.egg-info/entry_points.txt
|
|
12
11
|
code_normalizer_pro.egg-info/requires.txt
|
|
13
12
|
code_normalizer_pro.egg-info/top_level.txt
|
|
14
|
-
tests/test_code_normalize_pro.py
|
|
15
|
-
tests/test_feedback_prioritizer.py
|
|
16
|
-
tests/test_launch_metrics.py
|
|
17
|
-
tests/test_release_prep.py
|
|
18
|
-
tests/test_sales_pipeline_metrics.py
|
|
13
|
+
tests/test_code_normalize_pro.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tqdm
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "code-normalizer-pro"
|
|
7
|
+
version = "3.0.2"
|
|
8
|
+
description = "Production-grade code normalization tool for encoding, newlines, and whitespace hygiene."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = ["tqdm"]
|
|
13
|
+
keywords = ["code-quality", "formatter", "normalization", "cli", "python"]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://github.com/MRJR0101/Code-Normalizer-Pro"
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
code-normalizer-pro = "code_normalizer_pro.cli:main"
|
|
@@ -6,11 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
9
|
-
|
|
10
|
-
if str(SRC_DIR) not in sys.path:
|
|
11
|
-
sys.path.insert(0, str(SRC_DIR))
|
|
12
|
-
|
|
13
|
-
import code_normalize_pro as cnp
|
|
9
|
+
from code_normalizer_pro import code_normalizer_pro as cnp
|
|
14
10
|
|
|
15
11
|
|
|
16
12
|
def test_guess_and_read_accepts_utf16_text(tmp_path: Path) -> None:
|
|
@@ -24,6 +20,38 @@ def test_guess_and_read_accepts_utf16_text(tmp_path: Path) -> None:
|
|
|
24
20
|
assert "print('hi')" in text
|
|
25
21
|
|
|
26
22
|
|
|
23
|
+
def test_in_place_rewrites_clean_utf16_to_utf8(tmp_path: Path) -> None:
|
|
24
|
+
sample = tmp_path / "utf16_clean.py"
|
|
25
|
+
sample.write_text("print('hi')\n", encoding="utf-16")
|
|
26
|
+
|
|
27
|
+
normalizer = cnp.CodeNormalizer(
|
|
28
|
+
in_place=True,
|
|
29
|
+
create_backup=False,
|
|
30
|
+
use_cache=False,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
assert normalizer.process_file(sample) is True
|
|
34
|
+
assert sample.read_text(encoding="utf-8") == "print('hi')\n"
|
|
35
|
+
assert not sample.read_bytes().startswith((b"\xff\xfe", b"\xfe\xff"))
|
|
36
|
+
assert normalizer.stats.processed == 1
|
|
37
|
+
assert normalizer.stats.encoding_changes == 1
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_dry_run_check_validates_normalized_output(tmp_path: Path, capsys) -> None:
|
|
41
|
+
sample = tmp_path / "needs_fix.py"
|
|
42
|
+
sample.write_bytes(b"print('hi') \r\n")
|
|
43
|
+
|
|
44
|
+
normalizer = cnp.CodeNormalizer(dry_run=True, use_cache=False)
|
|
45
|
+
|
|
46
|
+
assert normalizer.process_file(sample, check_syntax=True) is True
|
|
47
|
+
captured = capsys.readouterr()
|
|
48
|
+
|
|
49
|
+
assert "Would normalize" in captured.out
|
|
50
|
+
assert "Syntax: \u2713 OK" in captured.out
|
|
51
|
+
assert normalizer.stats.processed == 1
|
|
52
|
+
assert normalizer.stats.syntax_checks_passed == 1
|
|
53
|
+
|
|
54
|
+
|
|
27
55
|
def test_guess_and_read_rejects_binary_with_nuls(tmp_path: Path) -> None:
|
|
28
56
|
sample = tmp_path / "blob.bin"
|
|
29
57
|
sample.write_bytes(b"\x89PNG\x00\x01\x02\x03\x04\x00\x00\xff")
|
|
@@ -53,7 +81,7 @@ def test_install_git_hook_uses_current_python_and_checks_failures(tmp_path: Path
|
|
|
53
81
|
assert "for file_path in files" in hook_text
|
|
54
82
|
assert 'file_path, "--dry-run"' in hook_text
|
|
55
83
|
assert "result.returncode != 0" in hook_text
|
|
56
|
-
assert "
|
|
84
|
+
assert "code_normalizer_pro.py" in hook_text
|
|
57
85
|
|
|
58
86
|
|
|
59
87
|
def test_install_hook_cli_exits_nonzero_outside_git_repo(tmp_path: Path) -> None:
|
|
@@ -116,3 +144,19 @@ def test_parallel_cache_hits_are_persisted(tmp_path: Path) -> None:
|
|
|
116
144
|
assert second.returncode == 0, second.stderr
|
|
117
145
|
assert "All discovered files were unchanged and skipped by cache." in second.stdout
|
|
118
146
|
assert "⊙ Cached hits: 2" in second.stdout
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_cache_file_scoped_to_target_directory(tmp_path: Path, monkeypatch) -> None:
|
|
150
|
+
sample_dir = tmp_path / "samples"
|
|
151
|
+
sample_dir.mkdir()
|
|
152
|
+
(sample_dir / "a.py").write_text("print('a') \n", encoding="utf-8")
|
|
153
|
+
|
|
154
|
+
elsewhere = tmp_path / "elsewhere"
|
|
155
|
+
elsewhere.mkdir()
|
|
156
|
+
monkeypatch.chdir(elsewhere)
|
|
157
|
+
|
|
158
|
+
normalizer = cnp.CodeNormalizer(use_cache=True)
|
|
159
|
+
normalizer.walk_and_process(sample_dir, [".py"])
|
|
160
|
+
|
|
161
|
+
assert (sample_dir / cnp.CACHE_FILE).exists()
|
|
162
|
+
assert not (elsewhere / cnp.CACHE_FILE).exists()
|