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.
- {crcutil-2.0.2/src/crcutil.egg-info → crcutil-2.0.4}/PKG-INFO +1 -1
- {crcutil-2.0.2 → crcutil-2.0.4}/pyproject.toml +1 -1
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/checksum.py +97 -2
- {crcutil-2.0.2 → crcutil-2.0.4/src/crcutil.egg-info}/PKG-INFO +1 -1
- {crcutil-2.0.2 → crcutil-2.0.4}/.github/workflows/python-publish.yml +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/.gitignore +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/LICENSE +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/README.md +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/git-hooks/commit-msg +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/git-hooks/pre-commit +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/init.sh +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/requirements.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/ruff.toml +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/setup.cfg +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/__main__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/config/log_config.yaml +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/checksum_manager.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor_factory.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor_wayland.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor_windows.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/keyboard_monitor_x11.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/core/prompt.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/bootstrap_paths_dto.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/checksum_dto.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/crc_diff_report_dto.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/dto/user_instructions_dto.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/enums/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/enums/user_request.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/bootstrap_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/corrupt_crc_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/device_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/unexpected_argument_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/exception/user_error.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/serializer/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/serializer/checksum_serializer.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/serializer/crc_diff_report_serializer.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/__init__.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/crcutil_logger.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/file_importer.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/path_ops.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil/util/static.py +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/SOURCES.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/dependency_links.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/entry_points.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/src/crcutil.egg-info/requires.txt +0 -0
- {crcutil-2.0.2 → crcutil-2.0.4}/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 = 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(
|
|
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(
|
|
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
|
|
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
|