AI Skill Report Card

Inheriting Built In Exceptions

A-83·Aug 13, 2026·Source: Web
13 / 15

Some built-in exceptions (notably OSError subclasses) use __new__ to select a subclass based on errno, and override __init__/__new__ to set attributes like errno, strerror, filename. If you subclass these and add your own attributes without careful handling, they can be silently dropped or overwritten.

Python
class MyError(OSError): def __init__(self, message, foo): super().__init__(message) self.foo = foo def __str__(self): return f"{super().__str__()} (foo={self.foo!r})" try: raise MyError("bad thing", foo=42) except MyError as e: print(e.foo) # 42 print(str(e)) # bad thing (foo=42)
Recommendation
Add an example showing the __new__ override pattern to prevent OSError subclass hijacking, since it's mentioned but never demonstrated in code
13 / 15

Progress:

  • Step 1: Identify if the base exception has custom __new__ (e.g., OSError picks subclasses like FileNotFoundError based on errno).
  • Step 2: Check whether base __init__/__new__ accepts and stores extra positional args — subclass args beyond what the base expects are stored in args but not as separate attributes.
  • Step 3: Always call super().__init__(...) explicitly with the args the base class expects, then set additional custom attributes afterward in your own __init__.
  • Step 4: If overriding __new__ (rare, only needed for immutable-like customization), pass through *args, **kwargs to super().__new__ and avoid overwriting base attributes.
  • Step 5: Override __str__/__repr__ if you want custom attributes reflected in the printed message — the base __str__ won't know about them.
  • Step 6: Test pickling/copying if relevant — exceptions with custom __init__ signatures may fail to unpickle unless __reduce__ is implemented, since default pickling replays args through __init__ or __new__.
Recommendation
Include a full __reduce__ example that's tested end-to-end with pickle.dumps/loads to prove correctness
18 / 20

Example 1: Adding an attribute to OSError subclass Input:

Python
class ValidationError(OSError): def __init__(self, message, field): super().__init__(message) self.field = field

Output: ValidationError("bad", field="x").field == "x"; str(exc) == "bad"; pickling works if args == (message,) matches what __init__ expects — but field is lost on unpickle unless __reduce__ is added:

Python
def __reduce__(self): return (self.__class__, (self.args[0], self.field))

Example 2: OSError subclass hijacked by errno-based dispatch Input:

Python
class MyOSError(OSError): pass err = MyOSError(2, "No such file") type(err)

Output: FileNotFoundError — because OSError.__new__ inspects the errno argument (2 == ENOENT) and returns an instance of the more specific built-in subclass instead of MyOSError, unless MyOSError itself overrides __new__ to prevent this dispatch.

Example 3: StopIteration with custom value Input:

Python
class MyStop(StopIteration): def __init__(self, value, extra): super().__init__(value) self.extra = extra e = MyStop("done", extra="meta") e.value, e.extra

Output: ("done", "meta")StopIteration.__init__ already sets self.value from the first arg, so no need to set it manually.

Recommendation
Consider condensing the Workflow checklist since some steps overlap with Best Practices/Pitfalls content
  • Always call super().__init__() with exactly the arguments the built-in expects; don't assume it forwards **kwargs.
  • Keep custom attributes as plain instance attributes set after super().__init__(), not passed through it.
  • Override __str__/__repr__ when custom attributes should appear in tracebacks/logs.
  • If subclassing OSError, be aware of implicit subclass dispatch via __new__; override __new__ in your subclass if you need to force your exact type to be preserved.
  • Implement __reduce__ for exceptions with non-standard __init__ signatures to keep pickling/multiprocessing working.
  • Forgetting that OSError(errno, strerror[, filename]) auto-selects a more specific subclass (FileNotFoundError, PermissionError, etc.), silently changing your exception's type.
  • Overriding __init__ without calling super().__init__(), leaving args, strerror, errno, etc. unset.
  • Assuming default pickling works — it calls cls(*exc.args), which breaks if __init__ has a different signature (extra required args like field above).
  • Not overriding __str__, resulting in custom attributes being invisible in printed tracebacks.
  • Adding attributes via __new__ instead of __init__ when __new__ doesn't need customizing — unnecessary complexity for most cases.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
13/15
Workflow
13/15
Examples
18/20
Completeness
17/20
Format
14/15
Conciseness
14/15