crcutil 2.0.2__tar.gz → 2.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 (52) hide show
  1. {crcutil-2.0.2/src/crcutil.egg-info → crcutil-2.0.4}/PKG-INFO +1 -1
  2. {crcutil-2.0.2 → crcutil-2.0.4}/pyproject.toml +1 -1
  3. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/checksum.py +97 -2
  4. {crcutil-2.0.2 → crcutil-2.0.4/src/crcutil.egg-info}/PKG-INFO +1 -1
  5. {crcutil-2.0.2 → crcutil-2.0.4}/.github/workflows/python-publish.yml +0 -0
  6. {crcutil-2.0.2 → crcutil-2.0.4}/.gitignore +0 -0
  7. {crcutil-2.0.2 → crcutil-2.0.4}/LICENSE +0 -0
  8. {crcutil-2.0.2 → crcutil-2.0.4}/README.md +0 -0
  9. {crcutil-2.0.2 → crcutil-2.0.4}/__init__.py +0 -0
  10. {crcutil-2.0.2 → crcutil-2.0.4}/git-hooks/commit-msg +0 -0
  11. {crcutil-2.0.2 → crcutil-2.0.4}/git-hooks/pre-commit +0 -0
  12. {crcutil-2.0.2 → crcutil-2.0.4}/init.sh +0 -0
  13. {crcutil-2.0.2 → crcutil-2.0.4}/requirements.txt +0 -0
  14. {crcutil-2.0.2 → crcutil-2.0.4}/ruff.toml +0 -0
  15. {crcutil-2.0.2 → crcutil-2.0.4}/setup.cfg +0 -0
  16. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/__init__.py +0 -0
  17. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/__main__.py +0 -0
  18. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/config/log_config.yaml +0 -0
  19. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/__init__.py +0 -0
  20. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/checksum_manager.py +0 -0
  21. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor.py +0 -0
  22. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor_factory.py +0 -0
  23. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor_wayland.py +0 -0
  24. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor_windows.py +0 -0
  25. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor_x11.py +0 -0
  26. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/prompt.py +0 -0
  27. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/__init__.py +0 -0
  28. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/bootstrap_paths_dto.py +0 -0
  29. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/checksum_dto.py +0 -0
  30. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/crc_diff_report_dto.py +0 -0
  31. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/user_instructions_dto.py +0 -0
  32. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/enums/__init__.py +0 -0
  33. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/enums/user_request.py +0 -0
  34. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/__init__.py +0 -0
  35. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/bootstrap_error.py +0 -0
  36. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/corrupt_crc_error.py +0 -0
  37. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/device_error.py +0 -0
  38. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/unexpected_argument_error.py +0 -0
  39. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/user_error.py +0 -0
  40. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/serializer/__init__.py +0 -0
  41. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/serializer/checksum_serializer.py +0 -0
  42. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/serializer/crc_diff_report_serializer.py +0 -0
  43. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/__init__.py +0 -0
  44. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/crcutil_logger.py +0 -0
  45. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/file_importer.py +0 -0
  46. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/path_ops.py +0 -0
  47. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/static.py +0 -0
  48. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/SOURCES.txt +0 -0
  49. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/dependency_links.txt +0 -0
  50. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/entry_points.txt +0 -0
  51. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/requires.txt +0 -0
  52. {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crcutil
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Author-email: Carlos Florez <carlos@florez.co.uk>
5
5
  Maintainer-email: Carlos Florez <carlos@florez.co.uk>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "crcutil"
7
- version = "2.0.2"
7
+ version = "2.0.4"
8
8
  requires-python = ">=3.12.10"
9
9
  authors = [
10
10
  {name = "Carlos Florez", email = "carlos@florez.co.uk"}
@@ -3,6 +3,10 @@ import zlib
3
3
  from collections.abc import Callable
4
4
  from pathlib import Path
5
5
 
6
+ from crcutil.util.crcutil_logger import CrcutilLogger
7
+
8
+ CHUNK_SIZE = 4096 * 1024
9
+
6
10
 
7
11
  class Checksum:
8
12
  def __init__(self, location: Path, root_location: Path) -> None:
@@ -83,7 +87,9 @@ class Checksum:
83
87
  - location: a/b/c
84
88
  Returns -> b'b/c'
85
89
  """
86
- return str(location.relative_to(root_location)).encode("utf-8")
90
+ return str(location.relative_to(root_location).as_posix()).encode(
91
+ "utf-8"
92
+ )
87
93
 
88
94
  def __get_checksum_from_file_contents(self, location: Path) -> bytes:
89
95
  """
@@ -95,6 +101,8 @@ class Checksum:
95
101
  and updating the CRC32 value incrementally. The result is
96
102
  masked to ensure it's treated as an unsigned 32-bit integer.
97
103
 
104
+ Text files with dos line endings are normalized to unix
105
+
98
106
  Args:
99
107
  location (pathlib.Path): The path to evaluate
100
108
 
@@ -102,7 +110,94 @@ class Checksum:
102
110
  bytes: 4-byte little-endian representation of the CRC32 checksum
103
111
  """
104
112
  file_checksum = 0
113
+ is_text_file = self.__is_likely_text_file(location)
114
+
115
+ if is_text_file:
116
+ try:
117
+ with location.open("r", encoding="utf-8") as f:
118
+ content = f.read().replace("\r\n", "\n")
119
+ content_bytes = content.encode("utf-8")
120
+ file_checksum = zlib.crc32(content_bytes) & 0xFFFFFFFF
121
+ return file_checksum.to_bytes(4, "little", signed=False)
122
+ except UnicodeDecodeError:
123
+ description = (
124
+ f"Understood as text file: {location!s} "
125
+ "but encountered decoding error. Proceeding w/ binary"
126
+ )
127
+ CrcutilLogger.get_logger().debug(description)
128
+
105
129
  with location.open("rb") as f:
106
- for chunk in iter(lambda: f.read(4096), b""):
130
+ for chunk in iter(lambda: f.read(CHUNK_SIZE), b""):
107
131
  file_checksum = zlib.crc32(chunk, file_checksum) & 0xFFFFFFFF
132
+
108
133
  return file_checksum.to_bytes(4, "little", signed=False)
134
+
135
+ def __is_likely_text_file(self, location: Path) -> bool:
136
+ """
137
+ Check if a file is likely to be text by sampling the beginning.
138
+ """
139
+ # Known text extensions to consider for line ending normalization
140
+ text_extensions = {
141
+ ".txt",
142
+ ".md",
143
+ ".rst",
144
+ ".html",
145
+ ".htm",
146
+ ".xml",
147
+ ".json",
148
+ ".yaml",
149
+ ".yml",
150
+ ".csv",
151
+ ".tsv",
152
+ ".ini",
153
+ ".cfg",
154
+ ".conf",
155
+ ".py",
156
+ ".js",
157
+ ".java",
158
+ ".c",
159
+ ".cpp",
160
+ ".h",
161
+ ".hpp",
162
+ ".php",
163
+ ".rb",
164
+ ".go",
165
+ ".rs",
166
+ ".ts",
167
+ ".css",
168
+ ".scss",
169
+ ".sql",
170
+ ".sh",
171
+ ".bash",
172
+ ".zsh",
173
+ ".ps1",
174
+ ".bat",
175
+ ".cmd",
176
+ ".log",
177
+ }
178
+ extension = location.suffix.lower()
179
+ # Only read a sample
180
+ with location.open("rb") as f:
181
+ sample = f.read(8192)
182
+
183
+ # Empty file, treat as empty text
184
+ if not sample and extension in text_extensions:
185
+ return True
186
+
187
+ # Null byte presence, likely a binary file
188
+ if b"\0" in sample and extension not in text_extensions:
189
+ return False
190
+
191
+ ascii_lower_range = 32
192
+ ascii_upper_range = 126
193
+ tab = 9
194
+ line_feed = 10
195
+ carriage_return = 13
196
+ printable_count = sum(
197
+ 1
198
+ for byte in sample
199
+ if ascii_lower_range <= byte <= ascii_upper_range
200
+ or byte in (tab, line_feed, carriage_return)
201
+ )
202
+ printable_character_treshold = 0.8
203
+ return (printable_count / len(sample)) > printable_character_treshold
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crcutil
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Author-email: Carlos Florez <carlos@florez.co.uk>
5
5
  Maintainer-email: Carlos Florez <carlos@florez.co.uk>
6
6
  License: MIT License
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