From 0d202d119b452253c23ea419b5857b9cda53e20d Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 20 Feb 2014 23:44:58 -0500 Subject: [PATCH] PEP 463: Fix some ReST warnings & errors --- pep-0463.txt | 206 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 129 insertions(+), 77 deletions(-) diff --git a/pep-0463.txt b/pep-0463.txt index 96a62959e..660ab8d60 100644 --- a/pep-0463.txt +++ b/pep-0463.txt @@ -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)