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)
| 193 | |
| 194 | |
| 195 | def file_digest(fileobj, digest, /, *, _bufsize=2**18): |
| 196 | """Hash the contents of a file-like object. Returns a digest object. |
| 197 | |
| 198 | *fileobj* must be a file-like object opened for reading in binary mode. |
| 199 | It accepts file objects from open(), io.BytesIO(), and SocketIO objects. |
| 200 | The function may bypass Python's I/O and use the file descriptor *fileno* |
| 201 | directly. |
| 202 | |
| 203 | *digest* must either be a hash algorithm name as a *str*, a hash |
| 204 | constructor, or a callable that returns a hash object. |
| 205 | """ |
| 206 | # On Linux we could use AF_ALG sockets and sendfile() to archive zero-copy |
| 207 | # hashing with hardware acceleration. |
| 208 | if isinstance(digest, str): |
| 209 | digestobj = new(digest) |
| 210 | else: |
| 211 | digestobj = digest() |
| 212 | |
| 213 | if hasattr(fileobj, "getbuffer"): |
| 214 | # io.BytesIO object, use zero-copy buffer |
| 215 | digestobj.update(fileobj.getbuffer()) |
| 216 | return digestobj |
| 217 | |
| 218 | # Only binary files implement readinto(). |
| 219 | if not ( |
| 220 | hasattr(fileobj, "readinto") |
| 221 | and hasattr(fileobj, "readable") |
| 222 | and fileobj.readable() |
| 223 | ): |
| 224 | raise ValueError( |
| 225 | f"'{fileobj!r}' is not a file-like object in binary reading mode." |
| 226 | ) |
| 227 | |
| 228 | # binary file, socket.SocketIO object |
| 229 | # Note: socket I/O uses different syscalls than file I/O. |
| 230 | buf = bytearray(_bufsize) # Reusable buffer to reduce allocations. |
| 231 | view = memoryview(buf) |
| 232 | while True: |
| 233 | size = fileobj.readinto(buf) |
| 234 | if size is None: |
| 235 | raise BlockingIOError("I/O operation would block.") |
| 236 | if size == 0: |
| 237 | break # EOF |
| 238 | digestobj.update(view[:size]) |
| 239 | |
| 240 | return digestobj |
| 241 | |
| 242 | |
| 243 | for __func_name in __always_supported: |