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.
Consider this example of a two-level cache::
for key in sequence:
x = (lvl1[key] except KeyError: (lvl2[key] except KeyError: f(key)))
# do something with x
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
calculate a default value to pass to get(). The .get() version calculates
backwards; the exception-testing version calculates forwards, as 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
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.
Retrieve an argument, defaulting to None::
cond = args[1] except IndexError: None
# Lib/pdb.py:803:
try:
cond = args[1]
except IndexError:
cond = None
cond = args[1] except IndexError: None
# Lib/pdb.py:803:
try:
cond = args[1]
except IndexError:
cond = None
Fetch information from the system if available::
pwd = os.getcwd() except OSError: None
# Lib/tkinter/filedialog.py:210:
try:
pwd = os.getcwd()
except OSError:
pwd = None
pwd = os.getcwd() except OSError: None
# Lib/tkinter/filedialog.py:210:
try:
pwd = os.getcwd()
except OSError:
pwd = None
Attempt a translation, falling back on the original::
e.widget = self._nametowidget(W) except KeyError: W
# Lib/tkinter/__init__.py:1222:
try:
e.widget = self._nametowidget(W)
except KeyError:
e.widget = W
e.widget = self._nametowidget(W) except KeyError: W
# Lib/tkinter/__init__.py:1222:
try:
e.widget = self._nametowidget(W)
except KeyError:
e.widget = W
Read from an iterator, continuing with blank lines once it's
exhausted::
line = readline() except StopIteration: ''
# Lib/lib2to3/pgen2/tokenize.py:370:
try:
line = readline()
except StopIteration:
line = ''
line = readline() except StopIteration: ''
# Lib/lib2to3/pgen2/tokenize.py:370:
try:
line = readline()
except StopIteration:
line = ''
Retrieve platform-specific information (note the DRY improvement);
this particular example could be taken further, turning a series of
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:
try:
_CONFIG_VARS['abiflags'] = sys.abiflags
except AttributeError:
# sys.abiflags may not be defined on all platforms.
_CONFIG_VARS['abiflags'] = ''
# sys.abiflags may not be defined on all platforms.
_CONFIG_VARS['abiflags'] = sys.abiflags except AttributeError: ''
# Lib/sysconfig.py:529:
try:
_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)::
def getNamedItem(self, name):
return self._attrs[name] except KeyError: None
@ -291,20 +301,20 @@ Retrieve an indexed item, defaulting to None (similar to dict.get)::
except KeyError:
return None
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:
try:
g = grp.getgrnam(tarinfo.gname)[2]
except KeyError:
g = tarinfo.gid
try:
u = pwd.getpwnam(tarinfo.uname)[2]
except KeyError:
u = tarinfo.uid
g = grp.getgrnam(tarinfo.gname)[2] except KeyError: tarinfo.gid
u = pwd.getpwnam(tarinfo.uname)[2] except KeyError: tarinfo.uid
# Lib/tarfile.py:2198:
try:
g = grp.getgrnam(tarinfo.gname)[2]
except KeyError:
g = tarinfo.gid
try:
u = pwd.getpwnam(tarinfo.uname)[2]
except KeyError:
u = tarinfo.uid
Perform some lengthy calculations in EAFP mode, handling division by
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.
Lib/ipaddress.py:343::
try:
ips.append(ip.ip)
except AttributeError:
ips.append(ip.network_address)
try:
ips.append(ip.ip)
except AttributeError:
ips.append(ip.network_address)
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::
try:
_ = ip.ip
except AttributeError:
_ = ip.network_address
ips.append(_)
try:
_ = ip.ip
except AttributeError:
_ = ip.network_address
ips.append(_)
Lib/tempfile.py:130::
try:
dirlist.append(_os.getcwd())
except (AttributeError, OSError):
dirlist.append(_os.curdir)
Becomes::
dirlist.append(_os.getcwd() except (AttributeError, OSError): _os.curdir)
Lib/asyncore.py:264::
try:
status.append('%s:%d' % self.addr)
except TypeError:
status.append(repr(self.addr))
try:
status.append('%s:%d' % self.addr)
except TypeError:
status.append(repr(self.addr))
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
@ -427,10 +448,12 @@ In terms of this PEP::
x = computation() except default(e)
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
::
x = try computation() catch MyException:e -> default(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.
Here's `SML's`__ "handle"::
Here's `SML's`__ "handle"
__ http://www.cs.cmu.edu/~rwh/introsml/core/exceptions.htm
::
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
::
let x = try computation() with MyException explanation -> default(explanation);;
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
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
::
x = try computation() catch MyException as e then default(e)
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
write, effectively::
write, effectively
__ http://clojure.org/special_forms#Special%20Forms--(try%20expr*%20catch-clause*%20finally-clause?)
::
try(computation(), catch(MyException, explanation, default(explanation)))
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
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
::
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.
@ -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
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
Python terms than Lisp macros, but something like::
Python terms than Lisp macros, but something like
__ http://wiki.tcl.tk/902
::
if {[ catch("computation()") "explanation"]} { default(explanation) }
`Smalltalk`__ is also somewhat hard to map to Python. The basic version
would be::
would be
__ http://smalltalk.gnu.org/wiki/exceptions
::
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.
@ -646,9 +690,9 @@ changed in Python 3 for good reason.)
Additionally, this syntax would allow a convenient way to capture
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
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.
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::
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,
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
@ -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
(ValueError, UnicodeError, AttributeError, EOFError, IOError, OSError,
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
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
of multiple except clauses and/or exception capturing is included. In
their absence, the parentheses would be thus::
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
this is a function call, makes this non-compelling. The expression can, of
course, be parenthesized if desired, as can the default::
value = (expr) except ExceptionType: (default)
@ -778,9 +828,11 @@ short-hand, though not technically an expression::
pass
For instance, a common use-case is attempting the removal of a file::
os.unlink(some_file) except OSError: pass
There is an equivalent already in Python 3.4, however, in contextlib::
from contextlib import suppress
with suppress(OSError): os.unlink(some_file)