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)
| 255 | |
| 256 | |
| 257 | def file_digest(fileobj, digest, /, *, _bufsize=2**18): |
| 258 | """Hash the contents of a file-like object. Returns a digest object. |
| 259 | |
| 260 | *fileobj* must be a file-like object opened for reading in binary mode. |
| 261 | It accepts file objects from open(), io.BytesIO(), and SocketIO objects. |
| 262 | The function may bypass Python's I/O and use the file descriptor *fileno* |
| 263 | directly. |
| 264 | |
| 265 | *digest* must either be a hash algorithm name as a *str*, a hash |
| 266 | constructor, or a callable that returns a hash object. |
| 267 | """ |
| 268 | # On Linux we could use AF_ALG sockets and sendfile() to archive zero-copy |
| 269 | # hashing with hardware acceleration. |
| 270 | if isinstance(digest, str): |
| 271 | digestobj = new(digest) |
| 272 | else: |
| 273 | digestobj = digest() |
| 274 | |
| 275 | if hasattr(fileobj, "getbuffer"): |
| 276 | # io.BytesIO object, use zero-copy buffer |
| 277 | digestobj.update(fileobj.getbuffer()) |
| 278 | return digestobj |
| 279 | |
| 280 | # Only binary files implement readinto(). |
| 281 | if not ( |
| 282 | hasattr(fileobj, "readinto") |
| 283 | and hasattr(fileobj, "readable") |
| 284 | and fileobj.readable() |
| 285 | ): |
| 286 | raise ValueError( |
| 287 | f"'{fileobj!r}' is not a file-like object in binary reading mode." |
| 288 | ) |
| 289 | |
| 290 | # binary file, socket.SocketIO object |
| 291 | # Note: socket I/O uses different syscalls than file I/O. |
| 292 | buf = bytearray(_bufsize) # Reusable buffer to reduce allocations. |
| 293 | view = memoryview(buf) |
| 294 | while True: |
| 295 | size = fileobj.readinto(buf) |
| 296 | if size == 0: |
| 297 | break # EOF |
| 298 | digestobj.update(view[:size]) |
| 299 | |
| 300 | return digestobj |
| 301 | |
| 302 | |
| 303 | for __func_name in __always_supported: |