-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Expand file tree
/
Copy pathIncorrectRaiseInSpecialMethod.qhelp
More file actions
69 lines (57 loc) · 3.96 KB
/
IncorrectRaiseInSpecialMethod.qhelp
File metadata and controls
69 lines (57 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>User-defined classes interact with the Python virtual machine via special methods (also called "magic methods").
For example, for a class to support addition it must implement the <code>__add__</code> and <code>__radd__</code> special methods.
When the expression <code>a + b</code> is evaluated, the Python virtual machine will call <code>type(a).__add__(a, b)</code>, and if that
is not implemented it will call <code>type(b).__radd__(b, a)</code>.</p>
<p>
Since the virtual machine calls these special methods for common expressions, users of the class will expect these operations to raise standard exceptions.
For example, users would expect that the expression <code>a.b</code> may raise an <code>AttributeError</code>
if the object <code>a</code> does not have an attribute <code>b</code>.
If a <code>KeyError</code> were raised instead,
then this would be unexpected and may break code that expected an <code>AttributeError</code>, but not a <code>KeyError</code>.
</p>
<p>
Therefore, if a method is unable to perform the expected operation then its response should conform to the standard protocol, described below.
</p>
<ul>
<li>Attribute access, <code>a.b</code> (<code>__getattr__</code>): Raise <code>AttributeError</code>.</li>
<li>Arithmetic operations, <code>a + b</code> (<code>__add__</code>): Do not raise an exception, return <code>NotImplemented</code> instead.</li>
<li>Indexing, <code>a[b]</code> (<code>__getitem__</code>): Raise <code>KeyError</code> or <code>IndexError</code>.</li>
<li>Hashing, <code>hash(a)</code> (<code>__hash__</code>): Should not raise an exception. Use <code>__hash__ = None</code> to indicate that an object is unhashable rather than raising an exception.</li>
<li>Equality methods, <code>a == b</code> (<code>__eq__</code>): Never raise an exception, always return <code>True</code> or <code>False</code>.</li>
<li>Ordering comparison methods, <code>a < b</code> (<code>__lt__</code>): Raise a <code>TypeError</code> if the objects cannot be ordered.</li>
<li>Most others: If the operation is never supported, the method often does not need to be implemented at all; otherwise a <code>TypeError</code> should be raised.</li>
</ul>
</overview>
<recommendation>
<p>If the method always raises as exception, then if it is intended to be an abstract method, the <code>@abstractmethod</code> decorator should be used.
Otherwise, ensure that the method raises an exception of the correct type, or remove the method if the operation does not need to be supported.
</p>
</recommendation>
<example>
<p>
In the following example, the <code>__add__</code> method of <code>A</code> raises a <code>TypeError</code> if <code>other</code> is of the wrong type.
However, it should return <code>NotImplemented</code> instead of rising an exception, to allow other classes to support adding to <code>A</code>.
This is demonstrated in the class <code>B</code>.
</p>
<sample src="examples/IncorrectRaiseInSpecialMethod.py" />
<p>
In the following example, the <code>__getitem__</code> method of <code>C</code> raises a <code>ValueError</code>, rather than a <code>KeyError</code> or <code>IndexError</code> as expected.
</p>
<sample src="examples/IncorrectRaiseInSpecialMethod2.py" />
<p>
In the following example, the class <code>__hash__</code> method of <code>D</code> raises <code>NotImplementedError</code>.
This causes <code>D</code> to be incorrectly identified as hashable by <code>isinstance(obj, collections.abc.Hashable)</code>; so the correct
way to make a class unhashable is to set <code>__hash__ = None</code>.
</p>
<sample src="examples/IncorrectRaiseInSpecialMethod3.py" />
</example>
<references>
<li>Python Language Reference: <a href="http://docs.python.org/dev/reference/datamodel.html#special-method-names">Special Method Names</a>.</li>
<li>Python Library Reference: <a href="https://docs.python.org/3/library/exceptions.html">Exceptions</a>.</li>
</references>
</qhelp>