mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
* Add ExceptionGroup fields and OSError use-init BaseExceptionGroup stores msg, excs, and excs_str; args keep the original sequence. OSError defers parse when a subclass overrides only __init__. characters_written can be deleted. Exception args/traceback/cause/context reject invalid setattr/delattr. UnicodeError.object is a deletable field. except* remainder projection copies traceback/context/cause. SyntaxError.__init__ clears omitted location attributes. Assisted-by: Grok:grok-4.6 * Fix OSError payload init and UnicodeError construction new_payload_exception now runs slot_new so BlockingIOError parses characters_written. new_exception falls back to invoke_exception for extra-payload types. Mixed tab/space UnexpectedIndentation is TabError. AttributeError pickle omits obj. Assisted-by: Grok:grok-4.6 * address review: classify TabError from error line Assisted-by: Grok:grok-4.6 * Add new_simple_exception; fill SyntaxError and f_lineno new_exception constructs extra-payload types; BaseException-sized types use new_simple_exception. Non-UTF-8 SyntaxError sets location and text. Codegen errors load text via ProgramText. Keep Missing parentheses capitalization. f-string adjacent atoms report a missing comma. f_lineno may be None; lasti is not restored on unwind. Assisted-by: Grok:grok-4.6 * address review: skip f-string continuation keywords Assisted-by: Grok:grok-4.6 * Fix clippy map_unwrap_or in Non-UTF-8 offset Assisted-by: Grok:grok-4.6 * Rename encode error ctor and exception setters new_unicode_encode_error_real is new_unicode_encode_error. set___traceback__/__cause__/__context__ are pygetset setters; Rust uses set_traceback/set_cause/set_context. Assisted-by: Grok:grok-4.6 * Use set_context in native pickle Assisted-by: Grok:grok-4.6 * Collapse nested if in asyncio traceback restore Assisted-by: Grok:grok-4.6 * Use current instruction for f_lineno addr2line lasti is stored as the next instruction index. addr2line of that byte offset is past RETURN_VALUE, so f_lineno was None and stdlib_threading_current_frames hung on leftover workers. Assisted-by: Grok:grok-4.6 * Look up format args, dir class, and frame builtins Consume printf args before rejecting unknown conversions. object.__dir__ uses getattr of __class__/__dict__. _PyEval_GetBuiltin looks up iter/__import__ on the frame. Assisted-by: Grok:grok-4.6 * Fix builtin lookups after rebase and unxfail passing tests Assisted-by: Grok:grok-4.6 * Restore RERAISE lasti and SyntaxError locations RERAISE with oparg restores lasti from the exception table before unwind, so leftover f_lineno matches the original raise. Handler lookup still uses the raising instruction. Indentation errors outrank print/exec missing-parentheses. BOM-with-non-utf8, bytes literals, and global+nonlocal now report the same offsets as the parser/symtable. object.__dir__ keeps string keys; type.__type_params__ skips the getset wrapper stored on builtin types. Assisted-by: Grok:grok-4.6 * Unxfail syntax/import tests and bind jump locals IMPORT_NAME passes fromlist None instead of an empty tuple. Jumping f_lineno binds None into unbound localsplus slots. str lstrip/rstrip copy subclasses to exact str when unchanged. Assisted-by: Grok:grok-4.6 * Fix import_from callers and rename frame lineno sqlite3.iterdump now passes fromlist as a PyObject. FrameObject::lineno is the i32 helper; f_lineno is the getset. Assisted-by: Grok:grok-4.6 * Pass sqlite fromlist without Into conversion from_list.into() could not infer Into<PyObjectRef> at the import_from call. The typed tuple already converts. Assisted-by: Grok:grok-4.6
212 lines
7.5 KiB
Python
Vendored
212 lines
7.5 KiB
Python
Vendored
import builtins
|
|
import os
|
|
import select
|
|
import socket
|
|
import unittest
|
|
import errno
|
|
from errno import EEXIST
|
|
|
|
|
|
class SubOSError(OSError):
|
|
pass
|
|
|
|
class SubOSErrorWithInit(OSError):
|
|
def __init__(self, message, bar):
|
|
self.bar = bar
|
|
super().__init__(message)
|
|
|
|
class SubOSErrorWithNew(OSError):
|
|
def __new__(cls, message, baz):
|
|
self = super().__new__(cls, message)
|
|
self.baz = baz
|
|
return self
|
|
|
|
class SubOSErrorCombinedInitFirst(SubOSErrorWithInit, SubOSErrorWithNew):
|
|
pass
|
|
|
|
class SubOSErrorCombinedNewFirst(SubOSErrorWithNew, SubOSErrorWithInit):
|
|
pass
|
|
|
|
class SubOSErrorWithStandaloneInit(OSError):
|
|
def __init__(self):
|
|
pass
|
|
|
|
|
|
class HierarchyTest(unittest.TestCase):
|
|
|
|
def test_builtin_errors(self):
|
|
self.assertEqual(OSError.__name__, 'OSError')
|
|
self.assertIs(IOError, OSError)
|
|
self.assertIs(EnvironmentError, OSError)
|
|
|
|
def test_socket_errors(self):
|
|
self.assertIs(socket.error, OSError)
|
|
self.assertIs(socket.gaierror.__base__, OSError)
|
|
self.assertIs(socket.herror.__base__, OSError)
|
|
self.assertIs(socket.timeout, TimeoutError)
|
|
|
|
def test_select_error(self):
|
|
self.assertIs(select.error, OSError)
|
|
|
|
# mmap.error is tested in test_mmap
|
|
|
|
_pep_map = """
|
|
+-- BlockingIOError EAGAIN, EALREADY, EWOULDBLOCK, EINPROGRESS
|
|
+-- ChildProcessError ECHILD
|
|
+-- ConnectionError
|
|
+-- BrokenPipeError EPIPE, ESHUTDOWN
|
|
+-- ConnectionAbortedError ECONNABORTED
|
|
+-- ConnectionRefusedError ECONNREFUSED
|
|
+-- ConnectionResetError ECONNRESET
|
|
+-- FileExistsError EEXIST
|
|
+-- FileNotFoundError ENOENT
|
|
+-- InterruptedError EINTR
|
|
+-- IsADirectoryError EISDIR
|
|
+-- NotADirectoryError ENOTDIR
|
|
+-- PermissionError EACCES, EPERM, ENOTCAPABLE
|
|
+-- ProcessLookupError ESRCH
|
|
+-- TimeoutError ETIMEDOUT
|
|
"""
|
|
def _make_map(s):
|
|
_map = {}
|
|
for line in s.splitlines():
|
|
line = line.strip('+- ')
|
|
if not line:
|
|
continue
|
|
excname, _, errnames = line.partition(' ')
|
|
for errname in filter(None, errnames.strip().split(', ')):
|
|
if errname == "ENOTCAPABLE" and not hasattr(errno, errname):
|
|
continue
|
|
_map[getattr(errno, errname)] = getattr(builtins, excname)
|
|
return _map
|
|
_map = _make_map(_pep_map)
|
|
|
|
def test_errno_mapping(self):
|
|
# The OSError constructor maps errnos to subclasses
|
|
# A sample test for the basic functionality
|
|
e = OSError(EEXIST, "Bad file descriptor")
|
|
self.assertIs(type(e), FileExistsError)
|
|
# Exhaustive testing
|
|
for errcode, exc in self._map.items():
|
|
e = OSError(errcode, "Some message")
|
|
self.assertIs(type(e), exc)
|
|
othercodes = set(errno.errorcode) - set(self._map)
|
|
for errcode in othercodes:
|
|
e = OSError(errcode, "Some message")
|
|
self.assertIs(type(e), OSError, repr(e))
|
|
|
|
def test_try_except(self):
|
|
filename = "some_hopefully_non_existing_file"
|
|
|
|
# This checks that try .. except checks the concrete exception
|
|
# (FileNotFoundError) and not the base type specified when
|
|
# PyErr_SetFromErrnoWithFilenameObject was called.
|
|
# (it is therefore deliberate that it doesn't use assertRaises)
|
|
try:
|
|
open(filename)
|
|
except FileNotFoundError:
|
|
pass
|
|
else:
|
|
self.fail("should have raised a FileNotFoundError")
|
|
|
|
# Another test for PyErr_SetExcFromWindowsErrWithFilenameObject()
|
|
self.assertFalse(os.path.exists(filename))
|
|
try:
|
|
os.unlink(filename)
|
|
except FileNotFoundError:
|
|
pass
|
|
else:
|
|
self.fail("should have raised a FileNotFoundError")
|
|
|
|
|
|
class AttributesTest(unittest.TestCase):
|
|
|
|
def test_windows_error(self):
|
|
if os.name == "nt":
|
|
self.assertIn('winerror', dir(OSError))
|
|
else:
|
|
self.assertNotIn('winerror', dir(OSError))
|
|
|
|
def test_posix_error(self):
|
|
e = OSError(EEXIST, "File already exists", "foo.txt")
|
|
self.assertEqual(e.errno, EEXIST)
|
|
self.assertEqual(e.args[0], EEXIST)
|
|
self.assertEqual(e.strerror, "File already exists")
|
|
self.assertEqual(e.filename, "foo.txt")
|
|
if os.name == "nt":
|
|
self.assertEqual(e.winerror, None)
|
|
|
|
@unittest.skipUnless(os.name == "nt", "Windows-specific test")
|
|
def test_errno_translation(self):
|
|
# ERROR_ALREADY_EXISTS (183) -> EEXIST
|
|
e = OSError(0, "File already exists", "foo.txt", 183)
|
|
self.assertEqual(e.winerror, 183)
|
|
self.assertEqual(e.errno, EEXIST)
|
|
self.assertEqual(e.args[0], EEXIST)
|
|
self.assertEqual(e.strerror, "File already exists")
|
|
self.assertEqual(e.filename, "foo.txt")
|
|
|
|
def test_blockingioerror(self):
|
|
args = ("a", "b", "c", "d", "e")
|
|
for n in range(6):
|
|
e = BlockingIOError(*args[:n])
|
|
with self.assertRaises(AttributeError):
|
|
e.characters_written
|
|
with self.assertRaises(AttributeError):
|
|
del e.characters_written
|
|
e = BlockingIOError("a", "b", 3)
|
|
self.assertEqual(e.characters_written, 3)
|
|
e.characters_written = 5
|
|
self.assertEqual(e.characters_written, 5)
|
|
del e.characters_written
|
|
with self.assertRaises(AttributeError):
|
|
e.characters_written
|
|
|
|
|
|
class ExplicitSubclassingTest(unittest.TestCase):
|
|
|
|
def test_errno_mapping(self):
|
|
# When constructing an OSError subclass, errno mapping isn't done
|
|
e = SubOSError(EEXIST, "Bad file descriptor")
|
|
self.assertIs(type(e), SubOSError)
|
|
|
|
def test_init_overridden(self):
|
|
e = SubOSErrorWithInit("some message", "baz")
|
|
self.assertEqual(e.bar, "baz")
|
|
self.assertEqual(e.args, ("some message",))
|
|
|
|
def test_init_kwdargs(self):
|
|
e = SubOSErrorWithInit("some message", bar="baz")
|
|
self.assertEqual(e.bar, "baz")
|
|
self.assertEqual(e.args, ("some message",))
|
|
|
|
def test_new_overridden(self):
|
|
e = SubOSErrorWithNew("some message", "baz")
|
|
self.assertEqual(e.baz, "baz")
|
|
self.assertEqual(e.args, ("some message",))
|
|
|
|
def test_new_kwdargs(self):
|
|
e = SubOSErrorWithNew("some message", baz="baz")
|
|
self.assertEqual(e.baz, "baz")
|
|
self.assertEqual(e.args, ("some message",))
|
|
|
|
def test_init_new_overridden(self):
|
|
e = SubOSErrorCombinedInitFirst("some message", "baz")
|
|
self.assertEqual(e.bar, "baz")
|
|
self.assertEqual(e.baz, "baz")
|
|
self.assertEqual(e.args, ("some message",))
|
|
e = SubOSErrorCombinedNewFirst("some message", "baz")
|
|
self.assertEqual(e.bar, "baz")
|
|
self.assertEqual(e.baz, "baz")
|
|
self.assertEqual(e.args, ("some message",))
|
|
|
|
def test_init_standalone(self):
|
|
# __init__ doesn't propagate to OSError.__init__ (see issue #15229)
|
|
e = SubOSErrorWithStandaloneInit()
|
|
self.assertEqual(e.args, ())
|
|
self.assertEqual(str(e), '')
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|