Hash the contents of a file-like object. Returns a digest object. *fileobj* must be a file-like object opened for reading in binary mode. It accepts file objects from open(), io.BytesIO(), and SocketIO objects. The function may bypass Python's I/O and use the file descriptor *fileno*
(fileobj, digest, /, *, _bufsize=2**18)
| 214 | |
| 215 | |
| 216 | def file_digest(fileobj, digest, /, *, _bufsize=2**18): |
| 217 | """Hash the contents of a file-like object. Returns a digest object. |
| 218 | |
| 219 | *fileobj* must be a file-like object opened for reading in binary mode. |
| 220 | It accepts file objects from open(), io.BytesIO(), and SocketIO objects. |
| 221 | The function may bypass Python's I/O and use the file descriptor *fileno* |
| 222 | directly. |
| 223 | |
| 224 | *digest* must either be a hash algorithm name as a *str*, a hash |
| 225 | constructor, or a callable that returns a hash object. |
| 226 | """ |
| 227 | # On Linux we could use AF_ALG sockets and sendfile() to achieve zero-copy |
| 228 | # hashing with hardware acceleration. |
| 229 | if isinstance(digest, str): |
| 230 | digestobj = new(digest) |
| 231 | else: |
| 232 | digestobj = digest() |
| 233 | |
| 234 | if hasattr(fileobj, "getbuffer"): |
| 235 | # io.BytesIO object, use zero-copy buffer |
| 236 | digestobj.update(fileobj.getbuffer()) |
| 237 | return digestobj |
| 238 | |
| 239 | # Only binary files implement readinto(). |
| 240 | if not ( |
| 241 | hasattr(fileobj, "readinto") |
| 242 | and hasattr(fileobj, "readable") |
| 243 | and fileobj.readable() |
| 244 | ): |
| 245 | raise ValueError( |
| 246 | f"'{fileobj!r}' is not a file-like object in binary reading mode." |
| 247 | ) |
| 248 | |
| 249 | # binary file, socket.SocketIO object |
| 250 | # Note: socket I/O uses different syscalls than file I/O. |
| 251 | buf = bytearray(_bufsize) # Reusable buffer to reduce allocations. |
| 252 | view = memoryview(buf) |
| 253 | while True: |
| 254 | size = fileobj.readinto(buf) |
| 255 | if size is None: |
| 256 | raise BlockingIOError("I/O operation would block.") |
| 257 | if size == 0: |
| 258 | break # EOF |
| 259 | digestobj.update(view[:size]) |
| 260 | |
| 261 | return digestobj |
| 262 | |
| 263 | |
| 264 | __logging = None |