Skip to content

Commit 871688f

Browse files
Update docs
1 parent 362bfba commit 871688f

7 files changed

+52
-79
lines changed

python/ql/src/Functions/IncorrectRaiseInSpecialMethod.py

Lines changed: 0 additions & 16 deletions
This file was deleted.

python/ql/src/Functions/IncorrectRaiseInSpecialMethod.qhelp

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ When the expression <code>a + b</code> is evaluated the Python virtual machine w
99
is not implemented it will call <code>type(b).__radd__(b, a)</code>.</p>
1010
<p>
1111
Since the virtual machine calls these special methods for common expressions, users of the class will expect these operations to raise standard exceptions.
12-
For example, users would expect that the expression <code>a.b</code> might raise an <code>AttributeError</code>
12+
For example, users would expect that the expression <code>a.b</code> may raise an <code>AttributeError</code>
1313
if the object <code>a</code> does not have an attribute <code>b</code>.
1414
If a <code>KeyError</code> were raised instead,
1515
then this would be unexpected and may break code that expected an <code>AttributeError</code>, but not a <code>KeyError</code>.
@@ -20,50 +20,48 @@ Therefore, if a method is unable to perform the expected operation then its resp
2020
</p>
2121

2222
<ul>
23-
<li>Attribute access, <code>a.b</code>: Raise <code>AttributeError</code></li>
24-
<li>Arithmetic operations, <code>a + b</code>: Do not raise an exception, return <code>NotImplemented</code> instead.</li>
25-
<li>Indexing, <code>a[b]</code>: Raise <code>KeyError</code>.</li>
26-
<li>Hashing, <code>hash(a)</code>: Use <code>__hash__ = None</code> to indicate that an object is unhashable.</li>
27-
<li>Equality methods, <code>a != b</code>: Never raise an exception, always return <code>True</code> or <code>False</code>.</li>
28-
<li>Ordering comparison methods, <code>a &lt; b</code>: Raise a <code>TypeError</code> if the objects cannot be ordered.</li>
23+
<li>Attribute access, <code>a.b</code> (<code>__getattr__</code>): Raise <code>AttributeError</code></li>
24+
<li>Arithmetic operations, <code>a + b</code> (<code>__add__</code>): Do not raise an exception, return <code>NotImplemented</code> instead.</li>
25+
<li>Indexing, <code>a[b]</code> (<code>__getitem__</code>): Raise <code>KeyError</code> or <code>IndexError</code>.</li>
26+
<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>
27+
<li>Equality methods, <code>a == b</code> (<code>__eq__</code>): Never raise an exception, always return <code>True</code> or <code>False</code>.</li>
28+
<li>Ordering comparison methods, <code>a &lt; b</code> (<code>__lt__</code>): Raise a <code>TypeError</code> if the objects cannot be ordered.</li>
2929
<li>Most others: Ideally, do not implement the method at all, otherwise raise <code>TypeError</code> to indicate that the operation is unsupported.</li>
3030
</ul>
3131

3232
</overview>
3333
<recommendation>
34-
<p>If the method is meant to be abstract, then declare it so using the <code>@abstractmethod</code> decorator.
34+
<p>If the method is intended to be abstract, then declare it so using the <code>@abstractmethod</code> decorator.
3535
Otherwise, either remove the method or ensure that the method raises an exception of the correct type.
3636
</p>
3737

3838
</recommendation>
3939
<example>
4040

4141
<p>
42-
This example shows two unhashable classes. The first class is unhashable in a non-standard way which may cause maintenance problems.
43-
The second, corrected, class uses the standard idiom for unhashable classes.
42+
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.
43+
However, it should return <code>NotImplemented</code> instead of rising an exception, to allow other classes to support adding to <code>A</code>.
44+
This is demonstrated in the class <code>B</code>.
4445
</p>
45-
<sample src="IncorrectRaiseInSpecialMethod.py" />
46+
<sample src="examples/IncorrectRaiseInSpecialMethod.py" />
4647
<p>
47-
In this example, the first class is implicitly abstract; the <code>__add__</code> method is unimplemented,
48-
presumably with the expectation that it will be implemented by sub-classes.
49-
The second class makes this explicit with an <code>@abstractmethod</code> decoration on the unimplemented <code>__add__</code> method.
48+
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.
5049
</p>
51-
<sample src="IncorrectRaiseInSpecialMethod2.py" />
50+
<sample src="examples/IncorrectRaiseInSpecialMethod2.py" />
5251
<p>
53-
In this last example, the first class implements a collection backed by the file store.
54-
However, should an <code>IOError</code> be raised in the <code>__getitem__</code> it will propagate to the caller.
55-
The second class handles any <code>IOError</code> by reraising a <code>KeyError</code> which is the standard exception for
56-
the <code>__getitem__</code> method.
52+
In the following example, the class <code>__hash__</code> method of <code>D</code> raises <code>TypeError</code>.
53+
This causes <code>D</code> to be incorrectly identified as hashable by <code>isinstance(obj, collections.abc.Hashable)</code>; so the correct
54+
way to make a class unhashable is to set <code>__hash__ = None</code>.
5755
</p>
5856

59-
<sample src="IncorrectRaiseInSpecialMethod3.py" />
57+
<sample src="examples/IncorrectRaiseInSpecialMethod3.py" />
6058

6159

6260
</example>
6361
<references>
6462

6563
<li>Python Language Reference: <a href="http://docs.python.org/dev/reference/datamodel.html#special-method-names">Special Method Names</a>.</li>
66-
<li>Python Library Reference: <a href="https://docs.python.org/2/library/exceptions.html">Exceptions</a>.</li>
64+
<li>Python Library Reference: <a href="https://docs.python.org/3/library/exceptions.html">Exceptions</a>.</li>
6765

6866

6967

python/ql/src/Functions/IncorrectRaiseInSpecialMethod2.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

python/ql/src/Functions/IncorrectRaiseInSpecialMethod3.py

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class A:
2+
def __init__(self, a):
3+
self.a = a
4+
5+
def __add__(self, other):
6+
# BAD: Should return NotImplemented instead of raising
7+
if not isinstance(other,A):
8+
raise TypeError(f"Cannot add A to {other.__type__}")
9+
return A(self.a + other.a)
10+
11+
class B:
12+
def __init__(self, a):
13+
self.a = a
14+
15+
def __add__(self, other):
16+
# GOOD: Returning NotImplemented allows for other classes to support adding do B.
17+
if not isinstance(other,B):
18+
return NotImplemented
19+
return B(self.a + other.a)
20+
21+
22+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class C:
2+
def __getitem__(self, idx):
3+
if self.idx < 0:
4+
# BAD: Should raise a KeyError or IndexError instead.
5+
raise ValueError("Invalid index")
6+
return self.lookup(idx)
7+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class D:
2+
def __hash__(self):
3+
# BAD: Use `__hash__ = None` instead.
4+
raise NotImplementedError(f"{self.__type__} is unhashable.")

0 commit comments

Comments
 (0)