Copilot commented on code in PR #3861:
URL: https://github.com/apache/avro/pull/3861#discussion_r3567333230


##########
lang/py/avro/io.py:
##########
@@ -214,11 +222,43 @@ def read(self, n: int) -> bytes:
         """
         if n < 0:
             raise avro.errors.InvalidAvroBinaryEncoding(f"Requested {n} bytes 
to read, expected positive integer.")
+        if n > self._MAX_UNCHECKED_READ:
+            remaining = self.bytes_remaining()
+            if remaining is not None and n > remaining:
+                raise avro.errors.InvalidAvroBinaryEncoding(f"Requested {n} 
bytes to read, but only {remaining} remain.")
         read_bytes = self.reader.read(n)
         if len(read_bytes) != n:
             raise avro.errors.InvalidAvroBinaryEncoding(f"Read 
{len(read_bytes)} bytes, expected {n} bytes")
         return read_bytes
 
+    def bytes_remaining(self) -> Optional[int]:
+        """
+        Return the number of bytes still available to read, or ``None`` when
+        that count is not known (a non-seekable reader, or one whose position
+        cannot be obtained). Used to reject a declared length or collection
+        block count that exceeds the data actually available before allocating
+        for it.
+        """
+        reader = self.reader
+        try:
+            pos = reader.tell()
+        except (OSError, ValueError, AttributeError):
+            # Not seekable, or the position could not be determined.
+            return None
+        try:
+            reader.seek(0, os.SEEK_END)
+            end = reader.tell()
+            return end - pos
+        except (OSError, ValueError, AttributeError):
+            return None
+        finally:
+            # Always restore the original position, even if seeking to the end
+            # or reading it failed, so the reader is never left at EOF.
+            try:
+                reader.seek(pos)
+            except (OSError, ValueError, AttributeError):
+                pass

Review Comment:
   The `finally` restore path in `bytes_remaining()` can also raise `TypeError` 
(e.g., if `pos` is not a valid seek offset for a custom stream). Since this is 
best-effort cleanup, type errors should be swallowed the same way as 
`OSError`/`ValueError`/`AttributeError`.



##########
lang/py/avro/io.py:
##########
@@ -214,11 +222,43 @@ def read(self, n: int) -> bytes:
         """
         if n < 0:
             raise avro.errors.InvalidAvroBinaryEncoding(f"Requested {n} bytes 
to read, expected positive integer.")
+        if n > self._MAX_UNCHECKED_READ:
+            remaining = self.bytes_remaining()
+            if remaining is not None and n > remaining:
+                raise avro.errors.InvalidAvroBinaryEncoding(f"Requested {n} 
bytes to read, but only {remaining} remain.")
         read_bytes = self.reader.read(n)
         if len(read_bytes) != n:
             raise avro.errors.InvalidAvroBinaryEncoding(f"Read 
{len(read_bytes)} bytes, expected {n} bytes")
         return read_bytes
 
+    def bytes_remaining(self) -> Optional[int]:
+        """
+        Return the number of bytes still available to read, or ``None`` when
+        that count is not known (a non-seekable reader, or one whose position
+        cannot be obtained). Used to reject a declared length or collection
+        block count that exceeds the data actually available before allocating
+        for it.
+        """
+        reader = self.reader
+        try:
+            pos = reader.tell()
+        except (OSError, ValueError, AttributeError):
+            # Not seekable, or the position could not be determined.
+            return None
+        try:
+            reader.seek(0, os.SEEK_END)
+            end = reader.tell()
+            return end - pos
+        except (OSError, ValueError, AttributeError):
+            return None

Review Comment:
   `bytes_remaining()` can raise `TypeError` (e.g., if a non-standard file-like 
object returns a non-int from `tell()`, making `end - pos` invalid). This would 
escape as an unexpected exception and break decoding; the method should 
reliably fall back to returning `None` on type errors as well.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to