crcutil 2.0.2__tar.gz → 2.0.3__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.
- {crcutil-2.0.2/src/crcutil.egg-info → crcutil-2.0.3}/PKG-INFO +1 -1
- {crcutil-2.0.2 → crcutil-2.0.3}/pyproject.toml +1 -1
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/checksum.py +96 -1
- {crcutil-2.0.2 → crcutil-2.0.3/src/crcutil.egg-info}/PKG-INFO +1 -1
- {crcutil-2.0.2 → crcutil-2.0.3}/.github/workflows/python-publish.yml +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/.gitignore +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/LICENSE +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/README.md +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/git-hooks/commit-msg +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/git-hooks/pre-commit +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/init.sh +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/requirements.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/ruff.toml +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/setup.cfg +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/__main__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/config/log_config.yaml +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/checksum_manager.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/keyboard_monitor.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/keyboard_monitor_factory.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/keyboard_monitor_wayland.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/keyboard_monitor_windows.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/keyboard_monitor_x11.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/core/prompt.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/dto/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/dto/bootstrap_paths_dto.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/dto/checksum_dto.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/dto/crc_diff_report_dto.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/dto/user_instructions_dto.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/enums/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/enums/user_request.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/exception/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/exception/bootstrap_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/exception/corrupt_crc_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/exception/device_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/exception/unexpected_argument_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/exception/user_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/serializer/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/serializer/checksum_serializer.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/serializer/crc_diff_report_serializer.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/util/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/util/crcutil_logger.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/util/file_importer.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/util/path_ops.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil/util/static.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil.egg-info/SOURCES.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil.egg-info/dependency_links.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil.egg-info/entry_points.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil.egg-info/requires.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.3}/src/crcutil.egg-info/top_level.txt +0 -0
|
@@ -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 = 64 * 1024
|
|
9
|
+
|
|
6
10
|
|
|
7
11
|
class Checksum:
|
|
8
12
|
def __init__(self, location: Path, root_location: Path) -> None:
|
|
@@ -95,6 +99,8 @@ class Checksum:
|
|
|
95
99
|
and updating the CRC32 value incrementally. The result is
|
|
96
100
|
masked to ensure it's treated as an unsigned 32-bit integer.
|
|
97
101
|
|
|
102
|
+
Text files with dos line endings are normalized to unix
|
|
103
|
+
|
|
98
104
|
Args:
|
|
99
105
|
location (pathlib.Path): The path to evaluate
|
|
100
106
|
|
|
@@ -102,7 +108,96 @@ class Checksum:
|
|
|
102
108
|
bytes: 4-byte little-endian representation of the CRC32 checksum
|
|
103
109
|
"""
|
|
104
110
|
file_checksum = 0
|
|
111
|
+
|
|
112
|
+
# Known text extensions to consider for line ending normalization
|
|
113
|
+
text_extensions = {
|
|
114
|
+
".txt",
|
|
115
|
+
".md",
|
|
116
|
+
".rst",
|
|
117
|
+
".html",
|
|
118
|
+
".htm",
|
|
119
|
+
".xml",
|
|
120
|
+
".json",
|
|
121
|
+
".yaml",
|
|
122
|
+
".yml",
|
|
123
|
+
".csv",
|
|
124
|
+
".tsv",
|
|
125
|
+
".ini",
|
|
126
|
+
".cfg",
|
|
127
|
+
".conf",
|
|
128
|
+
".py",
|
|
129
|
+
".js",
|
|
130
|
+
".java",
|
|
131
|
+
".c",
|
|
132
|
+
".cpp",
|
|
133
|
+
".h",
|
|
134
|
+
".hpp",
|
|
135
|
+
".php",
|
|
136
|
+
".rb",
|
|
137
|
+
".go",
|
|
138
|
+
".rs",
|
|
139
|
+
".ts",
|
|
140
|
+
".css",
|
|
141
|
+
".scss",
|
|
142
|
+
".sql",
|
|
143
|
+
".sh",
|
|
144
|
+
".bash",
|
|
145
|
+
".zsh",
|
|
146
|
+
".ps1",
|
|
147
|
+
".bat",
|
|
148
|
+
".cmd",
|
|
149
|
+
".log",
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
extension = location.suffix.lower()
|
|
153
|
+
is_text_file = self.__is_likely_text_file(location)
|
|
154
|
+
|
|
155
|
+
if extension in text_extensions and is_text_file:
|
|
156
|
+
try:
|
|
157
|
+
with location.open("r", encoding="utf-8") as f:
|
|
158
|
+
content = f.read().replace("\r\n", "\n")
|
|
159
|
+
content_bytes = content.encode("utf-8")
|
|
160
|
+
file_checksum = zlib.crc32(content_bytes) & 0xFFFFFFFF
|
|
161
|
+
return file_checksum.to_bytes(4, "little", signed=False)
|
|
162
|
+
except UnicodeDecodeError:
|
|
163
|
+
description = (
|
|
164
|
+
f"Understood as text file: {location!s} "
|
|
165
|
+
"but encountered decoding error. Proceeding w/ binary"
|
|
166
|
+
)
|
|
167
|
+
CrcutilLogger.get_logger().debug(description)
|
|
168
|
+
|
|
105
169
|
with location.open("rb") as f:
|
|
106
|
-
for chunk in iter(lambda: f.read(
|
|
170
|
+
for chunk in iter(lambda: f.read(CHUNK_SIZE), b""):
|
|
107
171
|
file_checksum = zlib.crc32(chunk, file_checksum) & 0xFFFFFFFF
|
|
172
|
+
|
|
108
173
|
return file_checksum.to_bytes(4, "little", signed=False)
|
|
174
|
+
|
|
175
|
+
def __is_likely_text_file(self, location: Path) -> bool:
|
|
176
|
+
"""
|
|
177
|
+
Check if a file is likely to be text by sampling the beginning.
|
|
178
|
+
"""
|
|
179
|
+
# Only read a sample
|
|
180
|
+
with location.open("rb") as f:
|
|
181
|
+
sample = f.read(CHUNK_SIZE)
|
|
182
|
+
|
|
183
|
+
# Empty file, treat as empty text
|
|
184
|
+
if not sample:
|
|
185
|
+
return True
|
|
186
|
+
|
|
187
|
+
# Null byte presence, likely a binary file
|
|
188
|
+
if b"\0" in sample:
|
|
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
|
|
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
|