PEP 463: Fix some ReST warnings & errors

This commit is contained in:
Yury Selivanov 2014-02-20 23:44:58 -05:00
parent 31ef98498d
commit 0d202d119b
1 changed files with 129 additions and 77 deletions

View File

@ -154,18 +154,22 @@ This ternary operator would be between lambda and if/else in
precedence. precedence.
Consider this example of a two-level cache:: Consider this example of a two-level cache::
for key in sequence: for key in sequence:
x = (lvl1[key] except KeyError: (lvl2[key] except KeyError: f(key))) x = (lvl1[key] except KeyError: (lvl2[key] except KeyError: f(key)))
# do something with x # do something with x
This cannot be rewritten as:: This cannot be rewritten as::
x = lvl1.get(key, lvl2.get(key, f(key)))
x = lvl1.get(key, lvl2.get(key, f(key)))
which, despite being shorter, defeats the purpose of the cache, as it must which, despite being shorter, defeats the purpose of the cache, as it must
calculate a default value to pass to get(). The .get() version calculates calculate a default value to pass to get(). The .get() version calculates
backwards; the exception-testing version calculates forwards, as would be backwards; the exception-testing version calculates forwards, as would be
expected. The nearest useful equivalent would be:: expected. The nearest useful equivalent would be::
x = lvl1.get(key) or lvl2.get(key) or f(key)
x = lvl1.get(key) or lvl2.get(key) or f(key)
which depends on the values being nonzero, as well as depending on the cache which depends on the values being nonzero, as well as depending on the cache
object supporting this functionality. object supporting this functionality.
@ -231,56 +235,62 @@ library, with file names and line numbers correct as of early Feb 2014.
Many of these patterns are extremely common. Many of these patterns are extremely common.
Retrieve an argument, defaulting to None:: Retrieve an argument, defaulting to None::
cond = args[1] except IndexError: None
# Lib/pdb.py:803: cond = args[1] except IndexError: None
try:
cond = args[1] # Lib/pdb.py:803:
except IndexError: try:
cond = None cond = args[1]
except IndexError:
cond = None
Fetch information from the system if available:: Fetch information from the system if available::
pwd = os.getcwd() except OSError: None
# Lib/tkinter/filedialog.py:210: pwd = os.getcwd() except OSError: None
try:
pwd = os.getcwd() # Lib/tkinter/filedialog.py:210:
except OSError: try:
pwd = None pwd = os.getcwd()
except OSError:
pwd = None
Attempt a translation, falling back on the original:: Attempt a translation, falling back on the original::
e.widget = self._nametowidget(W) except KeyError: W
# Lib/tkinter/__init__.py:1222: e.widget = self._nametowidget(W) except KeyError: W
try:
e.widget = self._nametowidget(W) # Lib/tkinter/__init__.py:1222:
except KeyError: try:
e.widget = W e.widget = self._nametowidget(W)
except KeyError:
e.widget = W
Read from an iterator, continuing with blank lines once it's Read from an iterator, continuing with blank lines once it's
exhausted:: exhausted::
line = readline() except StopIteration: ''
# Lib/lib2to3/pgen2/tokenize.py:370: line = readline() except StopIteration: ''
try:
line = readline() # Lib/lib2to3/pgen2/tokenize.py:370:
except StopIteration: try:
line = '' line = readline()
except StopIteration:
line = ''
Retrieve platform-specific information (note the DRY improvement); Retrieve platform-specific information (note the DRY improvement);
this particular example could be taken further, turning a series of this particular example could be taken further, turning a series of
separate assignments into a single large dict initialization:: separate assignments into a single large dict initialization::
# sys.abiflags may not be defined on all platforms.
_CONFIG_VARS['abiflags'] = sys.abiflags except AttributeError: ''
# Lib/sysconfig.py:529: # sys.abiflags may not be defined on all platforms.
try: _CONFIG_VARS['abiflags'] = sys.abiflags except AttributeError: ''
_CONFIG_VARS['abiflags'] = sys.abiflags
except AttributeError: # Lib/sysconfig.py:529:
# sys.abiflags may not be defined on all platforms. try:
_CONFIG_VARS['abiflags'] = '' _CONFIG_VARS['abiflags'] = sys.abiflags
except AttributeError:
# sys.abiflags may not be defined on all platforms.
_CONFIG_VARS['abiflags'] = ''
Retrieve an indexed item, defaulting to None (similar to dict.get):: Retrieve an indexed item, defaulting to None (similar to dict.get)::
def getNamedItem(self, name): def getNamedItem(self, name):
return self._attrs[name] except KeyError: None return self._attrs[name] except KeyError: None
@ -291,20 +301,20 @@ Retrieve an indexed item, defaulting to None (similar to dict.get)::
except KeyError: except KeyError:
return None return None
Translate numbers to names, falling back on the numbers:: Translate numbers to names, falling back on the numbers::
g = grp.getgrnam(tarinfo.gname)[2] except KeyError: tarinfo.gid
u = pwd.getpwnam(tarinfo.uname)[2] except KeyError: tarinfo.uid
# Lib/tarfile.py:2198: g = grp.getgrnam(tarinfo.gname)[2] except KeyError: tarinfo.gid
try: u = pwd.getpwnam(tarinfo.uname)[2] except KeyError: tarinfo.uid
g = grp.getgrnam(tarinfo.gname)[2]
except KeyError: # Lib/tarfile.py:2198:
g = tarinfo.gid try:
try: g = grp.getgrnam(tarinfo.gname)[2]
u = pwd.getpwnam(tarinfo.uname)[2] except KeyError:
except KeyError: g = tarinfo.gid
u = tarinfo.uid try:
u = pwd.getpwnam(tarinfo.uname)[2]
except KeyError:
u = tarinfo.uid
Perform some lengthy calculations in EAFP mode, handling division by Perform some lengthy calculations in EAFP mode, handling division by
zero as a sort of sticky NaN:: zero as a sort of sticky NaN::
@ -369,34 +379,45 @@ To do this with the statement form of try/except would require a temporary
variable, but it's far cleaner as an expression. variable, but it's far cleaner as an expression.
Lib/ipaddress.py:343:: Lib/ipaddress.py:343::
try:
ips.append(ip.ip) try:
except AttributeError: ips.append(ip.ip)
ips.append(ip.network_address) except AttributeError:
ips.append(ip.network_address)
Becomes:: Becomes::
ips.append(ip.ip except AttributeError: ip.network_address)
ips.append(ip.ip except AttributeError: ip.network_address)
The expression form is nearly equivalent to this:: The expression form is nearly equivalent to this::
try:
_ = ip.ip try:
except AttributeError: _ = ip.ip
_ = ip.network_address except AttributeError:
ips.append(_) _ = ip.network_address
ips.append(_)
Lib/tempfile.py:130:: Lib/tempfile.py:130::
try: try:
dirlist.append(_os.getcwd()) dirlist.append(_os.getcwd())
except (AttributeError, OSError): except (AttributeError, OSError):
dirlist.append(_os.curdir) dirlist.append(_os.curdir)
Becomes:: Becomes::
dirlist.append(_os.getcwd() except (AttributeError, OSError): _os.curdir) dirlist.append(_os.getcwd() except (AttributeError, OSError): _os.curdir)
Lib/asyncore.py:264:: Lib/asyncore.py:264::
try:
status.append('%s:%d' % self.addr) try:
except TypeError: status.append('%s:%d' % self.addr)
status.append(repr(self.addr)) except TypeError:
status.append(repr(self.addr))
Becomes:: Becomes::
status.append('%s:%d' % self.addr except TypeError: repr(self.addr))
status.append('%s:%d' % self.addr except TypeError: repr(self.addr))
Comparisons with other languages Comparisons with other languages
@ -427,10 +448,12 @@ In terms of this PEP::
x = computation() except default(e) x = computation() except default(e)
x = computation() except MyException default() except OtherException other() x = computation() except MyException default() except OtherException other()
`Erlang`__ has a try expression that looks like this:: `Erlang`__ has a try expression that looks like this
__ http://erlang.org/doc/reference_manual/expressions.html#id79284 __ http://erlang.org/doc/reference_manual/expressions.html#id79284
::
x = try computation() catch MyException:e -> default(e) end; x = try computation() catch MyException:e -> default(e) end;
x = try computation() catch MyException:e -> default(e); OtherException:e -> other(e) end; x = try computation() catch MyException:e -> default(e); OtherException:e -> other(e) end;
@ -455,14 +478,20 @@ some dialects, "->" in others.
To avoid confusion, I'll write the function calls in Python style. To avoid confusion, I'll write the function calls in Python style.
Here's `SML's`__ "handle":: Here's `SML's`__ "handle"
__ http://www.cs.cmu.edu/~rwh/introsml/core/exceptions.htm __ http://www.cs.cmu.edu/~rwh/introsml/core/exceptions.htm
::
let x = computation() handle MyException => default();; let x = computation() handle MyException => default();;
Here's `OCaml's`__ "try":: Here's `OCaml's`__ "try"
__ http://www2.lib.uchicago.edu/keith/ocaml-class/exceptions.html __ http://www2.lib.uchicago.edu/keith/ocaml-class/exceptions.html
::
let x = try computation() with MyException explanation -> default(explanation);; let x = try computation() with MyException explanation -> default(explanation);;
let x = try computation() with let x = try computation() with
@ -482,17 +511,23 @@ In terms of this PEP, these would be something like::
Many ML-inspired but not-directly-related languages from academia mix things Many ML-inspired but not-directly-related languages from academia mix things
up, usually using more keywords and fewer symbols. So, the `Oz`__ would map up, usually using more keywords and fewer symbols. So, the `Oz`__ would map
to Python as:: to Python as
__ http://mozart.github.io/mozart-v1/doc-1.4.0/tutorial/node5.html __ http://mozart.github.io/mozart-v1/doc-1.4.0/tutorial/node5.html
::
x = try computation() catch MyException as e then default(e) x = try computation() catch MyException as e then default(e)
Many Lisp-derived languages, like `Clojure,`__ implement try/catch as special Many Lisp-derived languages, like `Clojure,`__ implement try/catch as special
forms (if you don't know what that means, think function-like macros), so you forms (if you don't know what that means, think function-like macros), so you
write, effectively:: write, effectively
__ http://clojure.org/special_forms#Special%20Forms--(try%20expr*%20catch-clause*%20finally-clause?) __ http://clojure.org/special_forms#Special%20Forms--(try%20expr*%20catch-clause*%20finally-clause?)
::
try(computation(), catch(MyException, explanation, default(explanation))) try(computation(), catch(MyException, explanation, default(explanation)))
try(computation(), try(computation(),
@ -507,9 +542,12 @@ __ http://clhs.lisp.se/Body/m_hand_1.htm
The Lisp style is, surprisingly, used by some languages that don't have The Lisp style is, surprisingly, used by some languages that don't have
macros, like Lua, where `xpcall`__ takes functions. Writing lambdas macros, like Lua, where `xpcall`__ takes functions. Writing lambdas
Python-style instead of Lua-style:: Python-style instead of Lua-style
__ http://www.gammon.com.au/scripts/doc.php?lua=xpcall __ http://www.gammon.com.au/scripts/doc.php?lua=xpcall
::
x = xpcall(lambda: expression(), lambda e: default(e)) x = xpcall(lambda: expression(), lambda e: default(e))
This actually returns (true, expression()) or (false, default(e)), but I think we can ignore that part. This actually returns (true, expression()) or (false, default(e)), but I think we can ignore that part.
@ -541,19 +579,25 @@ colon in the proposal much more obvious::
true if an exception was caught, false otherwise, and you get the value out true if an exception was caught, false otherwise, and you get the value out
in other ways. And it's all built around the the implicit quote-and-exec in other ways. And it's all built around the the implicit quote-and-exec
that everything in Tcl is based on, making it even harder to describe in that everything in Tcl is based on, making it even harder to describe in
Python terms than Lisp macros, but something like:: Python terms than Lisp macros, but something like
__ http://wiki.tcl.tk/902 __ http://wiki.tcl.tk/902
::
if {[ catch("computation()") "explanation"]} { default(explanation) } if {[ catch("computation()") "explanation"]} { default(explanation) }
`Smalltalk`__ is also somewhat hard to map to Python. The basic version `Smalltalk`__ is also somewhat hard to map to Python. The basic version
would be:: would be
__ http://smalltalk.gnu.org/wiki/exceptions __ http://smalltalk.gnu.org/wiki/exceptions
::
x := computation() on:MyException do:default() x := computation() on:MyException do:default()
but that's basically Smalltalk's passing-arguments-with-colons ... but that's basically Smalltalk's passing-arguments-with-colons
syntax, not its exception-handling syntax. syntax, not its exception-handling syntax.
@ -646,9 +690,9 @@ changed in Python 3 for good reason.)
Additionally, this syntax would allow a convenient way to capture Additionally, this syntax would allow a convenient way to capture
exceptions in interactive Python; returned values are captured by "_", exceptions in interactive Python; returned values are captured by "_",
but exceptions currently are not. This could be spelled: but exceptions currently are not. This could be spelled::
>>> expr except Exception as e: e >>> expr except Exception as e: e
(The inner scope idea is tempting, but currently CPython handles list (The inner scope idea is tempting, but currently CPython handles list
comprehensions with a nested function call, as this is considered comprehensions with a nested function call, as this is considered
@ -668,11 +712,15 @@ included in this proposal. A subsequent Python version can add this without
breaking any existing code, as 'as' is already a keyword. breaking any existing code, as 'as' is already a keyword.
One example where this could possibly be useful is Lib/imaplib.py:568:: One example where this could possibly be useful is Lib/imaplib.py:568::
try: typ, dat = self._simple_command('LOGOUT')
except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]] try: typ, dat = self._simple_command('LOGOUT')
except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]]
This could become:: This could become::
typ, dat = (self._simple_command('LOGOUT')
except BaseException as e: ('NO', '%s: %s' % (type(e), e))) typ, dat = (self._simple_command('LOGOUT')
except BaseException as e: ('NO', '%s: %s' % (type(e), e)))
Or perhaps some other variation. This is hardly the most compelling use-case, Or perhaps some other variation. This is hardly the most compelling use-case,
but an intelligent look at this code could tidy it up significantly. In the but an intelligent look at this code could tidy it up significantly. In the
absence of further examples showing any need of the exception object, I have absence of further examples showing any need of the exception object, I have
@ -701,7 +749,7 @@ of a new type called ExpressionError), or have it look for a tuple named
ExpressionError in the current scope, with a built-in default such as ExpressionError in the current scope, with a built-in default such as
(ValueError, UnicodeError, AttributeError, EOFError, IOError, OSError, (ValueError, UnicodeError, AttributeError, EOFError, IOError, OSError,
LookupError, NameError, ZeroDivisionError). All of these were rejected, LookupError, NameError, ZeroDivisionError). All of these were rejected,
for severa reasons. for several reasons.
* First and foremost, consistency with the statement form of try/except * First and foremost, consistency with the statement form of try/except
would be broken. Just as a list comprehension or ternary if expression would be broken. Just as a list comprehension or ternary if expression
@ -754,6 +802,7 @@ the expression that could raise? Example::
This is more compelling when one or both of the deferred sub-proposals This is more compelling when one or both of the deferred sub-proposals
of multiple except clauses and/or exception capturing is included. In of multiple except clauses and/or exception capturing is included. In
their absence, the parentheses would be thus:: their absence, the parentheses would be thus::
value = expr except ExceptionType: default value = expr except ExceptionType: default
value = expr (except ExceptionType: default) value = expr (except ExceptionType: default)
@ -761,6 +810,7 @@ The advantage is minimal, and the potential to confuse a reader into
thinking the except clause is separate from the expression, or into thinking thinking the except clause is separate from the expression, or into thinking
this is a function call, makes this non-compelling. The expression can, of this is a function call, makes this non-compelling. The expression can, of
course, be parenthesized if desired, as can the default:: course, be parenthesized if desired, as can the default::
value = (expr) except ExceptionType: (default) value = (expr) except ExceptionType: (default)
@ -778,9 +828,11 @@ short-hand, though not technically an expression::
pass pass
For instance, a common use-case is attempting the removal of a file:: For instance, a common use-case is attempting the removal of a file::
os.unlink(some_file) except OSError: pass os.unlink(some_file) except OSError: pass
There is an equivalent already in Python 3.4, however, in contextlib:: There is an equivalent already in Python 3.4, however, in contextlib::
from contextlib import suppress from contextlib import suppress
with suppress(OSError): os.unlink(some_file) with suppress(OSError): os.unlink(some_file)