[Python-checkins] peps: Incorporate PEP 505 changes from Mark Haase, including aux files

chris.angelico python-checkins at python.org
Tue Oct 20 11:26:37 EDT 2015


https://hg.python.org/peps/rev/5d9e1c787f16
changeset:   6118:5d9e1c787f16
user:        Chris Angelico <rosuav at gmail.com>
date:        Wed Oct 21 02:25:57 2015 +1100
summary:
  Incorporate PEP 505 changes from Mark Haase, including aux files

files:
  pep-0505.txt             |  1248 ++++++++++++-
  pep-0505/find-pep505.out |  2514 ++++++++++++++++++++++++++
  pep-0505/find-pep505.py  |   434 ++++
  pep-0505/test.py         |    92 +
  4 files changed, 4201 insertions(+), 87 deletions(-)


diff --git a/pep-0505.txt b/pep-0505.txt
--- a/pep-0505.txt
+++ b/pep-0505.txt
@@ -1,5 +1,5 @@
 PEP: 505
-Title: None coalescing operators
+Title: None-aware operators
 Version: $Revision$
 Last-Modified: $Date$
 Author: Mark E. Haase <mehaase at gmail.com>
@@ -12,44 +12,193 @@
 Abstract
 ========
 
-Several modern programming languages have so-called "null coalescing" or
-"null aware" operators, including C#, Dart, Perl, Swift, and PHP (starting in
-version 7). These operators provide syntactic sugar for common patterns
-involving null references. [1]_ [2]_
+Several modern programming languages have so-called "``null``-coalescing" or
+"``null``- aware" operators, including C# [1]_, Dart [2]_, Perl, Swift, and PHP
+(starting in version 7). These operators provide syntactic sugar for common
+patterns involving null references.
 
-* The "null coalescing" operator is a binary operator that returns its first
-  first non-null operand.
-* The "null aware member access" operator is a binary operator that accesses
-  an instance member only if that instance is non-null. It returns null
-  otherwise.
-* The "null aware index access" operator is a binary operator that accesses a
-  member of a collection only if that collection is non-null. It returns null
-  otherwise.
+* The "``null``-coalescing" operator is a binary operator that returns its left
+  operand if it is not ``null``. Otherwise it returns its right operand.
+* The "``null``-aware member access" operator accesses an instance member only
+  if that instance is non-``null``. Otherwise it returns ``null``. (This is also
+  called a "safe navigation" operator.)
+* The "``null``-aware index access" operator accesses a member of a collection
+  only if that collection is non-``null``. Otherwise it returns ``null``. (This
+  is another type of "safe navigation" operator.)
 
-Python does not have any directly equivalent syntax. The ``or`` operator can
-be used to similar effect but checks for a truthy value, not ``None``
-specifically. The ternary operator ``... if ... else ...`` can be used for
-explicit null checks but is more verbose and typically duplicates part of the
-expression in between ``if`` and ``else``. The proposed ``None`` coalescing
-and ``None`` aware operators ofter an alternative syntax that is more
-intuitive and concise.
+The purpose of this PEP is to explore the possibility of implementing similar
+operators in Python. It provides some background material and then offers
+several competing alternatives for implementation.
+
+The initial reaction to this idea is majority negative. Even if ultimately
+rejected, this PEP still serves a purpose: to fully document the reasons why
+Python should not add this behavior, so that it can be pointed to in the future
+when the question inevitably arises again. (This is the null alternative, so to
+speak!)
+
+This proposal includes several new operators and should not be considered an
+all-or-nothing proposal. For example, the safe navigation operators might be
+rejected even if the ``null``-coalescing operator is approved, or vice-versa.
+
+Of course, Python does not have ``null``; it has ``None``, which is conceptually
+distinct. Although this PEP is inspired by "``null``-aware" operators in other
+languages, it uses the term "``None``-aware" operators to describe some
+hypothetical Python implementations.
+
+
+Background
+==========
+
+Specialness of ``None``
+-----------------------
+
+The Python language does not currently define any special behavior for ``None``.
+This PEP suggests making ``None`` a special case. This loss of generality is a
+noticeable drawback of the proposal. A generalization of ``None``-aware
+operators is set forth later in this document in order to avoid this
+specialization.
+
+
+Utility of ``None``
+-------------------
+
+One common criticism of adding special syntax for ``None`` is that ``None``
+shouldn't be used in the first place: it's a code smell. A related criticism is
+that ``None``-aware operators are used to silence errors (such as the novice
+misunderstanding of an implicit ``return None``) akin to `PHP's @ operator
+<http://php.net/manual/en/language.operators.errorcontrol.php>`_. Therefore,
+the utility of ``None`` must be debated before discussing whether to add new
+behavior around it.
+
+Python does not have any concept of ``null``. Every Python identifier must
+refer to an instance, so there cannot be any ``null`` references. Python does
+have a special instance called ``None`` that can be used to represent missing
+values, but ``None`` is conceptually distinct from ``null``.
+
+The most frequent use of ``None`` in Python is to provide a default value for
+optional arguments when some other default object is unwieldy. For example:
+``def get(url, proxy=None):``. In this case, ``proxy`` is an optional
+argument. If ``proxy`` is ``None``, then the request should be sent directly to
+the server; otherwise, the request should be routed through the specified proxy
+server. This use of ``None`` is preferred here to some other sentinel value or
+the Null Object Pattern. [3]_
+
+Examples of this form abound. Consider ``types.py`` in the standard library::
+
+    def prepare_class(name, bases=(), kwds=None):
+        if kwds is None:
+            kwds = {}
+        else:
+            kwds = dict(kwds)
+        ...
+
+Another frequent use of ``None`` is interfacing with external systems. Many of
+those other systems have a concept of ``null``. Therefore, Python code must have
+a way of representing ``null``, and typically it is represented by ``None``. For
+example, databases can have ``null`` values, and most Python database drivers
+will convert ``null`` to ``None`` when retrieving data from a database, and will
+convert from ``None`` back to ``null`` when sending data to a database.
+
+This convention of interchanging ``null`` and ``None`` is widespread in Python.
+It is canonized in the Python DBAPI (PEP-249). [4]_ The ``json`` module in the
+standard library and the third party PyYAML package both use ``None`` to
+represent their respective languages' ``null``.
+
+The C language ``null`` often bleeds into Python, too, particularly for thin
+wrappers around C libraries. For example, in ``pyopenssl``, the ``X509`` class
+has `a get_notBefore() method
+<https://github.com/pyca/pyopenssl/blob/3257877f8846e4357b495fa6c9344d01b11cf16d
+/OpenSSL/crypto.py#L1219>`_ that returns either a timestamp or ``None``. This
+function is a thin wrapper around an OpenSSL function with the return type
+``ASN1_TIME *``. Since this C pointer is allowed to be null, the Python wrapper
+must be able to express a missing "not before" date, e.g. ``None``.
+
+The representation of ``null`` is particularly noticeable when Python code is
+marshalling data between two systems. For example, consider a Python server that
+fetches data from a database and converts it to JSON for consumption by another
+process. In this case, it's often desirable that ``null`` in the database can be
+easily translated to ``null`` in JSON. If ``None`` is not used for this purpose,
+then each package will have to define its own representation of ``null``, and
+converting between these representations adds unnecessary complexity to the
+Python glue code.
+
+Therefore, the preference for avoiding ``None`` is nothing more than a
+preference; ``None`` has legitimate uses, particularly in specific types of
+software. Any hypothetical ``None``-aware operators should be construed as
+syntactic sugar for simplifying common patterns involving ``None``, and *should
+not be construed* as error handling behavior.
+
+
+Behavior In Other Languages
+---------------------------
+
+Given that ``null``-aware operators exist in other modern languages, it may be
+helpful to quickly understand how they work in those languages.
+
+C# example::
+
+    /* Null-coalescing. */
+
+    String s1 = null;
+    String s2 = "hello";
+    String s3 = s1 ?? s2;
+    Console.WriteLine("s3 is: " + s3);
+    // s3 is: hello
+
+    /* Null-aware member access, a.k.a. safe navigation. */
+
+    Console.WriteLine("s1.Length is: " + s1?.Length);
+    Console.WriteLine("s2.Length is: " + s2?.Length);
+    // s1.Length is:
+    // s2.Length is: 5
+
+    /* Null-aware index access, a.k.a. safe navigation. */
+
+    Dictionary<string,string> d1 = null;
+    Dictionary<string,string> d2 = new Dictionary<string, string>
+    {
+        { "foo", "bar" },
+        { "baz", "bat" }
+    };
+
+    Console.WriteLine("d1[\"foo\"] is: " + d1?["foo"]);
+    Console.WriteLine("d2[\"foo\"] is: " + d2?["foo"]);
+    // d1["foo"] is:
+    // d2["foo"] is: bar
+
+    /* Short Circuiting */
+
+    Console.WriteLine("s1 trim/upper is: " + s1?.Trim().Length);
+    Console.WriteLine("s2 trim/upper is: " + s2?.Trim().Length);
+    // s1 trimmed length is:
+    // s2 trimmed length is: 5
+
+    String s4 = s1 ?? s2 ?? DoError();
+    Console.WriteLine("s4 is: " + s4)
+    // s4 is: hello
+
+A `working example <https://dotnetfiddle.net/SxQNG8>`_ can be viewed online.
+
+Of utmost importance, notice the short circuiting behavior. The short circuiting
+of ``??`` is similar to short circuiting of other boolean operators such as
+``||`` or ``&&`` and should not be surprising. Helpfully, `?.` is *also* short
+circuiting: ``s1?.Trim()`` evaluates to null, but ``s1?.Trim().Length`` does not
+attempt to dereference the ``null`` pointer.
 
 
 Rationale
 =========
 
-Null Coalescing Operator
-------------------------
+Existing Alternatives
+---------------------
 
-The following code illustrates how the ``None`` coalescing operators would
-work in Python::
+Python does not have any specific ``None``-aware operators, but it does have
+operators that can be used for a similar purpose. This section describes why
+these alternatives may be undesirable for some common ``None`` patterns.
 
-    >>> title = 'My Title'
-    >>> title ?? 'Default Title'
-    'My Title'
-    >>> title = None
-    >>> title ?? 'Default Title'
-    'Default Title'
+
+``or`` Operator
+~~~~~~~~~~~~~~~
 
 Similar behavior can be achieved with the ``or`` operator, but ``or`` checks
 whether its left operand is false-y, not specifically ``None``. This can lead
@@ -68,17 +217,33 @@
     >>> (requested_quantity or default_quantity) * price # oops!
     100
 
-This type of bug is not possible with the ``None`` coalescing operator, 
-because there is no implicit type coersion to ``bool``::
+An experienced Python developer should know how ``or`` works and be capable of
+avoiding bugs like this. However, getting in the habit of using ``or`` for this
+purpose still might cause even an experienced developer to occasionally make
+this mistake, especially refactoring existing code and not carefully paying
+attention to the possible values of the left-hand operand.
 
-    >>> price = 100
-    >>> requested_quantity = 0
-    >>> default_quantity = 1
-    >>> (requested_quantity ?? default_quantity) * price
-    0
+For inexperienced developers, the problem is worse. The top Google hit for
+"python null coalesce" is a `StackOverflow page
+<http://stackoverflow.com/questions/4978738/is-there-a-python-equivalent-of-
+the-c-sharp-null-coalescing-operator>`_, and the top answer says to use ``or``.
+The top answer goes on to explain the caveats of using ``or`` like this, but how
+many beginning developers go on to read all those caveats?
 
-The same correct behavior can be achieved with the ternary operator. Here is
-an excerpt from the popular Requests package::
+The common usage of ``or`` for the purpose of providing default values is
+undeniable, and yet it is also booby-trapped for unsuspecting newcomers. This
+suggests that a safe operator for providing default values would have positive
+utility. While some critics claim that ``None-aware`` operators will be abused
+for error handling, they are no more prone to abuse than ``or`` is.
+
+
+Ternary Operator
+~~~~~~~~~~~~~~~~
+
+Another common way to intialize default values is to use the ternary operator.
+Here is an excerpt from the popular `Requests package <https://github.com/kennet
+hreitz/requests/blob/14a555ac716866678bf17e43e23230d81a8149f5/requests/models.py
+#L212>`_::
 
     data = [] if data is None else data
     files = [] if files is None else files
@@ -101,91 +266,1000 @@
 
 This ordering of the operands is more intuitive, but it requires 4 extra
 characters (for "not "). It also highlights the repetition of identifiers:
-``data if data``, ``files if files``, etc. The ``None`` coalescing operator
-improves readability::
+``data if data``, ``files if files``, etc. This example also benefits from short
+identifiers. What if the tested expression is longer and/or has side effects?
+This is addressed in the next section.
 
-    data = data ?? []
-    files = files ?? []
-    headers = headers ?? {}
-    params = params ?? {}
-    hooks = hooks ?? {}
 
-The ``None`` coalescing operator also has a corresponding assignment shortcut.
+Motivating Examples
+-------------------
 
-::
+The purpose of this PEP is to simplify some common patterns involving ``None``.
+This section presents some examples of common ``None`` patterns and explains
+the drawbacks.
 
-    data ?= []
-    files ?= []
-    headers ?= {}
-    params ?= {}
-    hooks ?= {}
+This first example is from a Python web crawler that uses the popular Flask
+framework as a front-end. This function retrieves information about a web site
+from a SQL database and formats it as JSON to send to an HTTP client::
 
-The ``None`` coalescing operator is left-associative, which allows for easy
-chaining::
+    class SiteView(FlaskView):
+        @route('/site/<id_>', methods=['GET'])
+        def get_site(self, id_):
+            site = db.query('site_table').find(id_)
 
-    >>> user_title = None
-    >>> local_default_title = None
-    >>> global_default_title = 'Global Default Title'
-    >>> title = user_title ?? local_default_title ?? global_default_title
-    'Global Default Title'
+            return jsonify(
+                first_seen=site.first_seen.isoformat() if site.first_seen is not None else None,
+                id=site.id,
+                is_active=site.is_active,
+                last_seen=site.last_seen.isoformat() if site.last_seen is not None else None,
+                url=site.url.rstrip('/')
+            )
 
-The direction of associativity is important because the ``None`` coalescing
-operator short circuits: if its left operand is non-null, then the right
-operand is not evaluated.
+Both ``first_seen`` and ``last_seen`` are allowed to be ``null`` in the
+database, and they are also allowed to be ``null`` in the JSON response. JSON
+does not have a native way to represent a ``datetime``, so the the server's
+contract states that any non-``null`` date is represented as a ISO-8601 string.
 
-::
+Note that this code is invalid by PEP-8 standards: several lines are over the
+line length limit. In fact, *including it in this document* violates the PEP
+formatting standard! But it's not unreasonably indented, nor are any of the
+identifiers excessively long. The excessive line length is due to the
+repetition of identifiers on both sides of the ternary ``if`` and the verbosity
+of the ternary itself (10 characters out of a 78 character line length).
 
-    >>> def get_default(): raise Exception()
-    >>> 'My Title' ?? get_default()
-    'My Title'
+One way to fix this code is to replace each ternary with a full ``if/else``
+block::
 
+    class SiteView(FlaskView):
+        @route('/site/<id_>', methods=['GET'])
+        def get_site(self, id_):
+            site = db.query('site_table').find(id_)
 
-Null-Aware Member Access Operator
----------------------------------
+            if site.first_seen is None:
+                first_seen = None
+            else:
+                first_seen = site.first_seen.isoformat()
 
-::
+            if site.last_seen is None:
+                last_seen = None
+            else:
+                last_seen = site.last_seen.isoformat()
 
-    >>> title = 'My Title'
-    >>> title.upper()
-    'MY TITLE'
-    >>> title = None
-    >>> title.upper()
+            return jsonify(
+                first_seen=first_seen,
+                id=site.id,
+                is_active=site.is_active,
+                last_seen=last_seen,
+                url=site.url.rstrip('/')
+            )
+
+This version definitely isn't *bad*. It is easy to read and understand. On the
+other hand, adding 8 lines of code to express this common behavior feels a bit
+heavy, especially for a deliberately simplified example. If a larger, more
+complicated data model was being used, then it would get tedious to continually
+write in this long form. The readability would start to suffer as the number of
+lines in the function grows, and a refactoring would be forced.
+
+Another alternative is to rename some of the identifiers::
+
+    class SiteView(FlaskView):
+        @route('/site/<id_>', methods=['GET'])
+        def get_site(self, id_):
+            site = db.query('site_table').find(id_)
+
+            fs = site.first_seen
+            ls = site.last_seen
+
+            return jsonify(
+                first_seen=fs.isodate() if fs is not None else None,
+                id=site.id,
+                is_active=site.is_active,
+                last_seen=ls.isodate() if ls is not None else None,,
+                url=site.url.rstrip('/')
+            )
+
+This adds fewer lines of code than the previous example, but it comes at the
+expense of introducing extraneous identifiers that amount to nothing more than
+aliases. These new identifiers are short enough to fit a ternary expression onto
+one line, but the identifiers are also less intuitive, e.g. ``fs`` versus
+``first_seen``.
+
+As a quick preview, consider an alternative rewrite using a new operator ``💩``.
+(This spelling of the operator is merely a placeholder so that the *concept* can
+be debated without arguing about *spelling*. It is not intended to reflect the
+public's opinion of said operator. It may, however, bring new meaning to the
+phrase "code smell".)::
+
+    class SiteView(FlaskView):
+        @route('/site/<id_>', methods=['GET'])
+        def get_site(self, id_):
+            site = db.query('site_table').find(id_)
+
+            return jsonify(
+                first_seen=site💩first_seen.isoformat(),
+                id=site.id,
+                is_active=site.is_active,
+                last_seen=site💩last_seen.isoformat(),
+                url=site.url.rstrip('/')
+            )
+
+The ``💩`` operator behaves as a "safe navigation" operator, allowing a more
+concise syntax where the expression ``site.first_seen`` is not duplicated.
+
+The next example is from a trending project on GitHub called `Grab
+<https://github.com/lorien/grab/blob/4c95b18dcb0fa88eeca81f5643c0ebfb114bf728/grab/upload.py>`_,
+which is a Python scraping library::
+
+    class BaseUploadObject(object):
+        def find_content_type(self, filename):
+            ctype, encoding = mimetypes.guess_type(filename)
+            if ctype is None:
+                return 'application/octet-stream'
+            else:
+                return ctype
+
+    class UploadContent(BaseUploadObject):
+        def __init__(self, content, filename=None, content_type=None):
+            self.content = content
+            if filename is None:
+                self.filename = self.get_random_filename()
+            else:
+                self.filename = filename
+            if content_type is None:
+                self.content_type = self.find_content_type(self.filename)
+            else:
+                self.content_type = content_type
+
+    class UploadFile(BaseUploadObject):
+        def __init__(self, path, filename=None, content_type=None):
+            self.path = path
+            if filename is None:
+                self.filename = os.path.split(path)[1]
+            else:
+                self.filename = filename
+            if content_type is None:
+                self.content_type = self.find_content_type(self.filename)
+            else:
+                self.content_type = content_type
+
+.. note::
+
+    I don't know the author of the Grab project. I used it as an example
+    because it is a trending repo on GitHub and it has good examples of common
+    ``None`` patterns.
+
+This example contains several good examples of needing to provide default
+values. It is a bit verbose as it is, and it is certainly not improved by the
+ternary operator::
+
+    class BaseUploadObject(object):
+        def find_content_type(self, filename):
+            ctype, encoding = mimetypes.guess_type(filename)
+            return 'application/octet-stream' if ctype is None else ctype
+
+    class UploadContent(BaseUploadObject):
+        def __init__(self, content, filename=None, content_type=None):
+            self.content = content
+            self.filename = self.get_random_filename() if filename \
+                is None else filename
+            self.content_type = self.find_content_type(self.filename) \
+                if content_type is None else content_type
+
+    class UploadFile(BaseUploadObject):
+        def __init__(self, path, filename=None, content_type=None):
+            self.path = path
+            self.filename = os.path.split(path)[1] if filename is \
+                None else filename
+            self.content_type = self.find_content_type(self.filename) \
+                if content_type is None else content_type
+
+The first ternary expression is tidy, but it reverses the intuitive order of
+the operands: it should return ``ctype`` if it has a value and use the string
+literal as fallback. The other ternary expressions are unintuitive and so
+long that they must be wrapped. The overall readability is worsened, not
+improved.
+
+This code *might* be improved, though, if there was a syntactic shortcut for
+this common need to supply a default value. We'll assume the fictitious
+operator ``✊🍆`` to avoid a premature debate about the spelling of said
+operator::
+
+    class BaseUploadObject(object):
+        def find_ctype(self, filename):
+            ctype, encoding = mimetypes.guess_type(filename)
+            return ctype ✊🍆 'application/octet-stream'
+
+    class UploadContent(BaseUploadObject):
+        def __init__(self, content, filename=None, content_type=None):
+            self.content = content
+            self.filename = filename ✊🍆 self.get_random_filename()
+            self.content_type = content_type ✊🍆 self.find_ctype(self.filename)
+
+    class UploadFile(BaseUploadObject):
+        def __init__(self, path, filename=None, content_type=None):
+            self.path = path
+            self.filename = filename ✊🍆 os.path.split(path)[1]
+            self.content_type = content_type ✊🍆 self.find_ctype(self.filename)
+
+This syntax has an intuitive ordering of the operands, e.g. ``ctype`` -- the
+preferred value -- comes before the fallback value. The terseness of the syntax
+also makes for fewer lines of code and less code to visually parse.
+
+.. note::
+
+    I cheated on the last example: I renamed ``find_content_type`` to
+    ``find_ctype`` in order to fit two of the lines under 80 characters. If you
+    find this underhanded, you can go back and apply the same renaming to the
+    previous 2 examples. You'll find that it doesn't change the
+    conclusions.
+
+
+Usage Of ``None`` In The Standard Library
+-----------------------------------------
+
+The previous sections show some code patterns that are claimed to be "common",
+but how common are they? The attached script ``find-pep505.py`` is meant to
+answer this question. It uses the ``ast`` module to search for these patterns in
+any ``*.py`` file. It checks for variations of the following patterns.
+
+    >>> # None-coalescing if block
+    ...
+    >>> if a is None:
+    ...     a = b
+
+    >>> # [Possible] None-coalescing "or" operator
+    ...
+    >>> a or 'foo'
+    >>> a or []
+    >>> a or {}
+
+    >>> # None-coalescing ternary
+    ...
+    >>> a if a is not None else b
+    >>> b if a is None else a
+
+    >>> # Safe navigation "and" operator
+    ...
+    >>> a and a.foo
+    >>> a and a['foo']
+    >>> a and a.foo()
+
+    >>> # Safe navigation if block
+    ...
+    >>> if a is not None:
+    ...     a.foo()
+
+    >>> # Safe navigation ternary
+    ...
+    >>> a.foo if a is not None else b
+    >>> b if a is None else a.foo
+
+This script takes one or more names of Python source files to analyze::
+
+    $ python3 find-pep505.py test.py
+    $ find /usr/lib/python3.4 -name '*.py' | xargs python3 find-pep505.py
+
+The script prints out any matches it finds. Sample::
+
+    None-coalescing if block: /usr/lib/python3.4/inspect.py:594
+        if _filename is None:
+             _filename = getsourcefile(object) or getfile(object)
+
+    [Possible] None-coalescing `or`: /usr/lib/python3.4/lib2to3/refactor.py:191
+            self.explicit = explicit or []
+
+    None-coalescing ternary: /usr/lib/python3.4/decimal.py:3909
+            self.clamp = clamp if clamp is not None else dc.clamp
+
+    Safe navigation `and`: /usr/lib/python3.4/weakref.py:512
+            obj = info and info.weakref()
+
+    Safe navigation `if` block: /usr/lib/python3.4/http/cookiejar.py:1895
+                            if k is not None:
+                                lc = k.lower()
+                            else:
+                                lc = None
+
+    Safe navigation ternary: /usr/lib/python3.4/sre_parse.py:856
+            literals = [None if s is None else s.encode('latin-1') for s in literals]
+
+.. note::
+
+    Coalescing with ``or`` is marked as a "possible" match, because it's not
+    trivial to infer whether ``or`` it is meant to coalesce False-y values
+    (correct) or if it meant to coalesce ``None`` (incorrect). On the other
+    hand, we assume that `and` is always incorrect for safe navigation.
+
+The script is tested against ``test.py`` (also attached to this document) and
+the Python 3.4 standard library, but it should work on any arbitrary Python
+source code. The complete output from running it against the standard library is
+attached to this proposal as ``find-pep505.out``.
+
+The script counts how many matches it finds and prints the totals at the
+end::
+
+    Total None-coalescing `if` blocks: 426
+    Total [possible] None-coalescing `or`: 119
+    Total None-coalescing ternaries: 21
+    Total Safe navigation `and`: 9
+    Total Safe navigation `if` blocks: 55
+    Total Safe navigation ternaries: 7
+
+This is a total of 637 possible matches for these common code patterns in the
+standard library. Allowing for some false positives and false negatives, it is
+fair to say that these code patterns are definitely common in the standard
+library.
+
+
+Rejected Ideas
+--------------
+
+Several related ideas were discussed on python-ideas, and some of these were
+roundly rejected by BDFL, the community, or both. For posterity's sake, some of
+those ideas are recorded here.
+
+``None``-aware Function Call
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``None``-aware syntax applies to attribute and index access, so it seems
+natural to ask if it should also apply to function invocation syntax. Borrowing
+a spelling similar to C#, it might be spelled ``foo?()``, where ``foo`` is only
+called if it is not None. This idea was quickly rejected, for several reasons.
+
+No other mainstream language has such syntax. Moreover, it would be difficult to
+discern if a function expression returned ``None`` because it was short-
+circuited or because the function itself returned ``None``. Finally, Python
+evaluates arguments to a function before it looks up the function itself, so
+``foo?(bar())`` would still call ``bar()`` even if ``foo`` is ``None``. This
+behaviour is unexpected for a so-called "short-circuiting" operator.
+
+Instead, the "``None``-severing" operator is proposed below. This operator
+offers a concise form for writing ``None``-aware function expressions that is
+truly short-circuiting.
+
+``?`` Unary Postfix Operator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To generalize the ``None``-aware behavior and limit the number of new operators
+introduced, a unary, postfix operator spelled ``?`` was suggested. The idea is
+that ``?`` would return a special object that could would override dunder
+methods to return itself or to return None. For example::
+
+    class NoneQuestion():
+        def __call__(self, *args, **kwargs):
+            return self
+
+        def __getattr__(self, name):
+            return self
+
+        def __getitem__(self, key):
+            reutrn self
+
+
+An expression like ``foo?`` would return a ``NoneQuestion`` instance if ``foo``
+is ``None``; otherwise, it returns ``foo``. With this operator, an expression
+like ``foo?.bar[baz]`` evaluates to ``NoneQuestion`` if ``foo`` is None. This is
+a nice generalization, but it's difficult to use in practice since most existing
+code won't know what ``NoneQuestion`` is.
+
+Going back to one of the motivating examples above, consider the following::
+
+    >>> import json
+    >>> created = None
+    >>> json.dumps({'created': created?.isoformat()})``
+
+The JSON serializer does not know how to serialize ``NoneQuestion``, nor will
+any other API. This proposal actually requires *lots of specialized logic*
+throughout the standard library and any third party library.
+
+The ``?`` operator may also be **too general**, in the sense that it can be
+combined with any other operator. What should the following expressions mean?
+
+    >>> x? + 1
+    >>> x? -= 1
+    >>> x? == 1
+    >>> ~x?
+
+This degree of generalization is not useful. The operators actually proposed
+herein are intentionally limited to a few operators that are expected to make it
+easier to write common code patterns.
+
+Haskell-style ``Maybe``
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Haskell has a concept called `Maybe <https://wiki.haskell.org/Maybe>`_ that
+encapsulates the idea of an optional value without relying on any special
+keyword (e.g. ``null``) or any special instance (e.g. ``None``). In Haskell, the
+purpose of ``Maybe`` is to avoid separate handling of "something" and nothing".
+The concept is so heavily intertwined with Haskell's lazy evaluation that it
+doesn't translate cleanly into Python.
+
+There is a Python package called `pymaybe
+<https://pypi.python.org/pypi/pymaybe/0.1.1>`_ that provides a rough
+approximation. The documentation shows the following example that appears
+relevant to the discussion at hand::
+
+    >>> maybe('VALUE').lower()
+    'value'
+
+    >>> maybe(None).invalid().method().or_else('unknown')
+    'unknown'
+
+The function ``maybe()`` returns either a ``Something`` instance or a
+``Nothing`` instance. Similar to the unary postfix operator described in the
+previous section, ``Nothing`` overrides dunder methods in order to allow
+chaining on a missing value.
+
+Note that ``or_else()`` is eventually required to retrieve the underlying value
+from ``pymaybe``'s wrappers. Furthermore, ``pymaybe`` does not short circuit any
+evaluation. Although ``pymaybe`` has some strengths and may be useful in its own
+right, it also demonstrates why a pure Python implementation of coalescing is
+not nearly as powerful as support built into the language.
+
+
+Specification
+=============
+
+This PEP suggests 4 new operators be added to Python:
+
+1. ``None``-coalescing operator
+2. ``None``-severing operator
+3. ``None``-aware attribute access
+4. ``None``-aware index access/slicing
+
+We will continue to assume the same spellings as in the previous sections in
+order to focus on behavior before diving into the much more contentious issue of
+how to spell these operators.
+
+
+``None``-Coalescing Operator
+----------------------------
+
+The ``None``-coalescing operator is a short-circuiting, binary operator that behaves
+in the following way.
+
+1. Evaluate the left operand first.
+2. If the left operand is not ``None``, then return it immediately.
+3. Else, evaluate the right operand and return the result.
+
+Some simple examples::
+
+    >>> 1 ✊🍆 2
+    1
+    >>> None ✊🍆 2
+    2
+    >>> 1 ✊🍆 None
+    1
+
+Importantly, note that the right operand is not evaluated unless the left
+operand is None::
+
+    >>> def err(): raise Exception('foo')
+    >>> 1 ✊🍆 err()
+    1
+    >>> None ✊🍆 err()
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
-    AttributeError: 'NoneType' object has no attribute 'upper'
-    >>> title?.upper()
+      File "<stdin>", line 1, in err
+    Exception: foo
+
+The operator is left associative. Combined with its short circuiting behavior,
+this makes the operator easy to chain::
+
+    >>> timeout = None
+    >>> local_timeout = 60
+    >>> global_timeout = 300
+    >>> timeout ✊🍆 local_timeout ✊🍆 global_timeout
+    60
+
+    >>> local_timeout = None
+    >>> timeout ✊🍆 local_timeout ✊🍆 global_timeout
+    300
+
+    >>> import time
+    >>> timeout ✊🍆 local_timeout ✊🍆 global_timeout ✊🍆 time.sleep(10)
+    300
+
+Note in the last example that ``time.sleep(10)`` represents an expensive
+function call, e.g. initializing a complex data structure. In this example
+``time.sleep`` is not evaluated, and the result ``300`` is returned instantly.
+
+The operator has precedence lower than ``not`` but higher than ``and`` and
+``or``. This precedence makes reasoning about the order of operations
+comfortable, because it has precedence similar to the operators used for
+coalescing false-y values. Here are pairs of examples, where each item in the
+pair is evaluated identically to the other item in the pair::
+
+    >>> not None ✊🍆 False
+    True
+    >>> (not None) ✊🍆 False
+    True
+
+    >>> 'foo' in dict() ✊🍆 {'foo': 'bar'}
+    False
+    >>> ('foo' in dict()) ✊🍆 {'foo': 'bar'}
+    False
+
+    >>> 1 == None ✊🍆 1
+    False
+    >>> (1 == None) ✊🍆 1
+    False
+
+But ``and`` and ``or`` have lower precedence::
+
+    >>> 2 or None ✊🍆 err()
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in <module>
+      File "<stdin>", line 1, in err
+    Exception: foo
+    >>> (2 or None) ✊🍆 err()
+    2
+
+Recall the example above of calculating the cost of items in a shopping cart,
+and the easy-to-miss bug. This type of bug is not possible with the ``None``-
+coalescing operator, because there is no implicit type coersion to ``bool``::
+
+    >>> price = 100
+    >>> requested_quantity = 0
+    >>> default_quantity = 1
+    >>> (requested_quantity ✊🍆 default_quantity) * price
+    0
+
+The ``None``-coalescing operator also has a corresponding assignment shortcut.
+The following assignments are semantically equivalent::
+
+    >>> foo ✊🍆= []
+    >>> foo = foo ✊🍆 []
+
+The ``None`` coalescing operator improves readability, especially when handling
+default function arguments. Consider again the example of requests, rewritten to
+use ``None``-coalescing::
+
+    def __init__(self, data=None, files=None, headers=None, params=None, hooks=None):
+        self.data = data ✊🍆 []
+        self.files = files ✊🍆 []
+        self.headers = headers ✊🍆 {}
+        self.params = params ✊🍆 {}
+        self.hooks = hooks ✊🍆 {}
+
+The operator makes the intent easier to follow (by putting operands in an
+intuitive order) and is more concise than the ternary operator, while still
+preserving the short circuit semantics of the code that it replaces.
+
+
+``None``-Severing Operator
+--------------------------
+
+The idea of a ``None``-aware function invocation syntax was discussed on python-
+ideas, but the idea was rejected by BDFL. The syntax comes dangerously close to
+allowing a caller to change the return type of a function. If a function is
+defined to always return a value, then it seems strange that the call site could
+change the function to return "value or ``None``".
+
+Still, conditional function execution is a common idiom in Python, particularly
+for callback functions. Consider this hypothetical example::
+
+    import time
+
+    def delay(seconds, callback=None):
+        time.sleep(seconds)
+
+        if callback is not None:
+            callback()
+
+With a ``None``-aware function invocation, this example might be written more
+concisely as::
+
+    import time
+
+    def delay(seconds, callback=None):
+        time.sleep(seconds)
+        callback?()
+
+Consider a "``None``-severing" operator, however, which is a short-circuiting,
+boolean operator similar to the ``None``-coalesing operator, except it returns
+its left operand if that operand is None. If the left operand is None, then the
+right operand is not evaluated. Let's temporarily spell this operator ``✂`` and
+rewrite the example accordingly::
+
+    import time
+
+    def delay(seconds, callback=None):
+        time.sleep(seconds)
+        callback ✂ callback()
+
+At this point, you may be astonished at the mere suggestion of such a strange
+operator with limited practical usefulness. It is proposed here because of the
+symmetry it has with the ``None``-coalescing operator. This symmetry may be more
+apparent if the two operators have complementary spellings.
+
+In the same way that ``or`` and ``and`` go together, ``None``-coalescing and
+``None``- severing might be spelled in a pleasing, symmetric way, e.g. ``or?``
+and ``and?``. If such a spelling can be decided on, then this operator adds very
+little cognitive load or special machinery to the language, and it's minor
+utility may justify its inclusion in the language.
+
+Note that ``None``-severing could also be used as an alternative to "safe
+navigation", at the expense of some repeated expressions::
+
+    >>> from datetime import datetime
+    >>> d = None
+    >>> type(d ✂ d.isoformat())
+    <class 'NoneType'>
+
+    >>> d = datetime.now()
+    >>> d ✂ d.isoformat()
+    '2015-10-16T20:53:40.312135'
+
+The repeated expression ``d`` makes this less useful than a ``None``-aware
+attribute access operator, but to repeat what was said at the outset: this
+proposal may be approved or rejected in whole or in part. This unlikely operator
+is included in the proposal in order to be comprehensive.
+
+The precedence and associativity of the ``None``-severing operator are the same
+as the ``None``-coalescing operator.
+
+
+``None``-Aware Attribute Access Operator
+----------------------------------------
+
+The ``None``-aware attribute access operator (also called "safe navigation")
+checks its left operand. If the left operand is ``None``, then the operator
+evaluates to ``None``. If the the left operand is not ``None``, then the
+operator accesses the attribute named by the right operand. As in the previous
+section, we continue to use the temporary spelling ``💩``::
+
+    >>> from datetime import date
+    >>> d = date.today()
+    >>> d.year
+    2015
+
+    >>> d = None
+    >>> d.year
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in <module>
+    AttributeError: 'NoneType' object has no attribute 'year'
+
+    >>> d💩year
     None
 
+The operator has the same precedence and associativity as the plain attribute
+access operator ``.``, but this operator is also short-circuiting in a unique
+way: if the left operand is ``None``, then any adjacent attribute access, index
+access, slicing, or function call operators *are not evaluated*.
 
-Null-Aware Index Access Operator
----------------------------------
+    >>> name = ' The Black Knight '
+    >>> name.strip()[4:].upper()
+    'BLACK KNIGHT'
 
-::
+    >>> name = None
+    >>> name💩strip()[4:].upper()
+    None
+
+If this operator did not short circuit in this way, then the second example
+would partially evaluate ``name💩strip()`` to ``None()`` and then fail with
+``TypeError: 'NoneType' object is not callable``.
+
+To put it another way, the following expressions are semantically equivalent::
+
+    >>> name💩strip()[4:].upper()
+    >>> name.strip()[4:].upper() if name is not None else None
+
+.. note::
+
+    C# implements its safe navigation operators with the same short-circuiting
+    semantics, but Dart does not. In Dart, the second example (suitably
+    translated) would fail. The C# semantics are obviously superior, given the
+    original goal of writing common cases more concisely. The Dart semantics are
+    nearly useless.
+
+This operator short circuits one or more immediately adjacent attribute access,
+index access, slicing, or function call operators, but it does not short circuit
+any other operators (logical, bitwise, arithmetic, etc.), nor does it escape
+parentheses::
+
+    >>> d = date.today()
+    >>> d💩year.numerator + 1
+    2016
+
+    >>> d = None
+    >>> d💩year.numerator + 1
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in <module>
+    TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
+
+    >>> (d💩year).numerator + 1
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in <module>
+    AttributeError: 'NoneType' object has no attribute 'numerator'
+
+Note that the error in the second example is not on the attribute access
+``numerator``. In fact, that attribute access is never performed. The error
+occurs when adding ``None + 1``, because the ``None``-aware attribute access
+does not short circuit ``+``.
+
+The third example fails because the operator does not escape parentheses. In
+that example, the attribute access ``numerator`` is evaluated and fails because
+``None`` does not have that attribute.
+
+Finally, observe that short circuiting adjacent operators is not at all the same thing as propagating ``None`` throughout an expression.
+
+    >>> user💩first_name.upper()
+
+If ``user`` is not ``None``, then ``user.first_name`` is evaluated. If
+``user.first_name`` evaluates to ``None``, then ``user.first_name.upper()`` is
+an error! In English, this expression says ``user`` is optional but if it has a
+value, then it must have a ``first_name``, too.
+
+If ``first_name`` is supposed to be optional attribute, then the expression must
+make that explicit:
+
+    >>> user💩first_name💩upper()
+
+The operator is not intended as an error silencing mechanism, and it would be
+wrong if its presence infected nearby operators.
+
+``None``-Aware Index Access/Slicing Operator
+--------------------------------------------
+
+The ``None``-aware index access/slicing operator (also called "safe navigation")
+is nearly identical to the ``None``-aware attribute access operator. It combines
+the familiar square bracket syntax ``[]`` with new punctuation or a new keyword,
+the spelling of which is discussed later::
 
     >>> person = {'name': 'Mark', 'age': 32}
     >>> person['name']
     'Mark'
+
     >>> person = None
     >>> person['name']
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     TypeError: 'NoneType' object is not subscriptable
-    >>> person?['name']
+
+    >>> person💩['name']
     None
 
+The ``None``-aware slicing operator behaves similarly::
 
-Specification
-=============
+    >>> name = 'The Black Knight'
+    >>> name[4:]
+    'Black Knight'
+
+    >>> name = None
+    >>> name[4:]
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in <module>
+    TypeError: 'NoneType' object is not subscriptable
+
+    >>> name💩[4:]
+    None
+
+These operators have the same precedence as the plain index access and slicing
+operators.
+
+
+Generalization
+--------------
+
+Making ``None`` a special case may seem too specialized and magical. It is
+possible to generalize the behavior by making the ``None``-aware operators
+invoke a dunder method, e.g. ``__coalesce__(self)`` that returns ``True`` if an
+object should be coalesced and ``False`` otherwise.
+
+With this generalization, ``object`` would implement a dunder method equivalent
+to this::
+
+    def __coalesce__(self):
+        return False
+
+``NoneType`` would implement a dunder method equivalent to this::
+
+    def __coalesce__(self):
+        return True
+
+This generalization allows for domain-specific ``null`` objects to be coalesced
+just like ``None``. For example the ``pyasn1`` package has a type called
+``Null`` that represents an ASN.1 ``null``.
+
+If this generalization is accepted, then the operators will need to be renamed
+such that the term ``None`` is not used, e.g. "Coalescing Operator", "Coalesced
+Member Access Operator", etc.
+
+
+Operator Spelling
+-----------------
+
+Despite significant support for the proposed operators, the majority of
+discussion on python-ideas fixated on the spelling. No consensus was achieved on
+this question, for two reasons. First, Python eschews punctuation for logical
+operators. For example, it uses ``not`` instead of ``!`` and ``… if … else …``
+instead of ``?:``. Introducing new punctuation is a major turnoff to many
+Pythonistas, including BDFL. Second, adding new keywords to the language is
+not backwards compatible. Any new keyword could only be introduced in the next
+major version, e.g. Python 4. (Even then, `there would be resistance
+<http://opensource.com/life/14/9/why-python-4-wont-be-python-3>`_.)
+
+Furthermore, nearly every single punctuation character on a standard keyboard
+already has special meaning in Python. The only exceptions are ``$``, ``~``,
+``?``, and backtick (as of Python 3). This leaves few options for a new, single-
+character operator. A two character spelling is more likely, such as the ``??``
+and ``?.`` spellings in other programming languages, but this decreases the
+appeal of punctuation even further.
+
+Finally, other projects in the Python universe assign special meaning to
+punctuation. For example, `IPython <https://ipython.org/ipython-
+doc/2/interactive/reference.html>`_ assigns special meaning to ``%``, ``%%``,
+``?``, ``??``, ``$``, and ``$$``, among others. Out of deference to those
+projects and the large communities using them, introducing conflicting syntax
+into Python is undesirable.
+
+This is not the first PEP to deal with this dilemma. PEP-308 [5]_, which
+introduced the ternary operator, faced similar issues.
+
+Alternative Spellings
+~~~~~~~~~~~~~~~~~~~~~
+
+In keeping with the spirit of the PEP, many alternative spellings for these
+``None``-aware operators are suggested, including some that conflict with each
+other. Deconfliction will be handled only if any part of this proposal is
+accepted.
+
+One caveat noted by several respondents on python-ideas: using similar spelling
+for ``None`` coalescing and other ``None``-aware operators may be confusing,
+because they have different short circuit semantics: coalescing short circuits
+on non-``None``, while ``None``-aware attribute/index access short circuit on
+``None``. This is a potential downside to spellings like ``??`` and ``?.``. This
+is only a practical concern if any part of this proposal is actually accepted,
+so there is no need to pontificate any further.
+
+The following spellings are proposed candidates for the ``None``-coalescing
+operator.
+
+1. ``foo ?? bar ?? baz``
+    - Pros: same spelling as C# and Dart
+    - Cons: punctuation is ugly; possible conflict with IPython; difficult to
+      google to find out what it means
+2. ``foo or? bar or? baz``
+    - Pros: similar to existing ``or`` operator
+    - Cons: the difference between this and ``or`` is not intuitive; punctuation
+      is ugly
+3. ``foo ? bar``
+    - Pros: similar to ``??`` used in other languages
+    - Cons: punctuation is ugly; possible conflict with IPython; not used by any
+      other language
+4. ``foo $$ bar``
+    - Pros: pronounced "value operator" because it returns the first operand
+      that has a "value"
+    - Cons: punctuation is ugly; not used by any other language
+5. ``foo else bar``
+    - Pros: prettier than punctuation; uses an existing keyword
+    - Cons: difficult or impossible to implement with Python's LL(1) parser
+6. ``foo or else bar``
+    - Pros: prettier than punctuation; use existing keywords
+    - Cons: difficult or impossible to implement with Python's LL(1) parser
+7. ``foo def bar``
+    - Pros: pronounced 'default'; prettier than punctuation
+    - Cons: difficult or impossible to implement with Python's LL(1) parser
+8. ``foo then bar``
+    - Pros: prettier than punctuation
+    - Cons: requires a new keyword, probably can't be implemented until Python 4
+      (and maybe not even then)
+9. No ``None``-coalescing operator.
+    - (Pros and cons discussed throughout this document.)
+
+The following spellings are proposed candidates for the ``None``-severing
+operator. Each alternative has symmetry with one of the proposed spellings of
+the ``None``- coalescing operator.
+
+1. ``foo !! bar``
+    - Pros: symmetric with ``??``
+    - Cons: punctuation is ugly; possible conflict with IPython; difficult to
+      google to find out what it means
+2. ``foo and? bar``
+    - Pros: symmetric with ``or?``
+    - Cons: punctuation is ugly; possible conflict with IPython; difficult to
+      google to find out what it means
+3. No ``None``-severing operator.
+    - (Pros and cons discussed throughout this document.)
+
+The following spellings are proposed candidates for the ``None``-aware attribute
+access operator. If you find any of these hard to read, consider that we may
+adopt a convention of adding whitespace around a ``None``-aware operator to
+improve readability.
+
+1. ``foo?.bar``, ``foo ?. bar``
+    - Pros: same spelling as C# and Dart
+    - Cons: punctuation is ugly; possible conflict with IPython; difficult to
+      google to find out what it means; difficult to differentiate from ``.``
+      when reading quickly
+2. ``foo$.bar``, ``foo $. bar``
+    - Pros: symmetry with ``$$`` operator proposed above
+    - Cons: punctuation is ugly; difficult to google; possible confusion because
+      it looks a bit like other languages' string interpolation; difficult to
+      google to find out what it means; difficult to differentiate from ``.``
+      when reading quickly
+3. ``foo!bar``, ``foo ! bar``
+    - Pros: similar to ordinary ``.`` operator
+    - Cons: punctuation is ugly; possible conflict with IPython; no corresponding
+      spelling for index access (e.g. ``foo!['bar']`` is ambiguous)
+4. ``foo->bar``, ``foo -> bar``
+    - Pros: easier to read than other punctuation; less likely to be confused
+      with ordinary attribute access
+    - Cons: punctuation is ugly; difficult to google; confusing because it is
+      spelled the same as C's dereference operator
+5. ``foo try .bar``
+    - Pros: uses an existing keyword;
+    - Cons: difficult or impossible to implement in Python's LL(1) parser
+6. No ``None``-aware attribute access operator.
+    - (Pros and cons discussed throughout this document.)
+
+The following spellings are proposed candidates for the ``None``-aware index
+access/slicing operator. The punctuation used for this operator ought to
+resemble the punctuation used for the ``None``-aware attribute access.
+
+1. ``foo?['bar']``, ``foo ? ['bar']``
+    - Pros: same spelling as C# and Dart
+    - Cons: punctuation is ugly; possible conflict with IPython; difficult to
+      google to find out what it means
+2. ``foo$['bar']``, ``foo $ ['bar']``
+    - Pros: symmetry with ``$$`` operator proposed above
+    - Cons: punctuation is ugly; possible confusion because
+      it looks a bit like other languages' string interpolation
+3. ``foo->['bar']``, ``foo -> ['bar']``
+    - Pros: easier to read than other punctuation; less likely to be confused
+      with ordinary attribute access
+    - Cons: punctuation is ugly; difficult to google; confusing because it is
+      spelled the same as C's dereference operator
+4. ``foo try ['bar']``
+    - Pros: uses an existing keyword;
+    - Cons: difficult or impossible to implement in Python's LL(1) parser
+5. No ``None``-aware index access/slicing operator.
+    - (Pros and cons discussed throughout this document.)
+
+Community Poll
+~~~~~~~~~~~~~~
+
+In order to collect data about the Python community's preferences for
+``None``-aware operators, and with BDFL's consent, a public poll will be
+conducted, just as with PEP-308. The poll is viewed as a data-gathering
+exercise, not a democratic vote.
+
+The poll will allow respondents to rank their favorite options from the previous
+section. The results will
+be placed in this section of the PEP.
+
+...TBD...
+
+
+Implementation
+--------------
+
+Given that the need for ``None``-aware operators is questionable and the
+spelling of said operators is almost incendiary, the implementation details for
+CPython will be deferred unless and until we have a clearer idea that one (or
+more) of the proposed operators will be approved.
+
+...TBD...
 
 
 References
 ==========
 
-.. [1] Wikipedia: Null coalescing operator
-   (https://en.wikipedia.org/wiki/Null_coalescing_operator)
+.. [1] C# Reference: Operators
+   (https://msdn.microsoft.com/en-us/library/6a71f45d.aspx)
 
-.. [2] Seth Ladd's Blog: Null-aware operators in Dart
-   (http://blog.sethladd.com/2015/07/null-aware-operators-in-dart.html)
+.. [2] A Tour of the Dart Language: Operators
+   (https://www.dartlang.org/docs/dart-up-and-running/ch02.html#operators)
+
+.. [3] Wikipedia: Null Object Pattern
+   (https://en.wikipedia.org/wiki/Null_Object_pattern)
+
+.. [4] PEP-249:
+   (https://www.python.org/dev/peps/pep-0249/)
+
+.. [5] PEP-308
+   (https://www.python.org/dev/peps/pep-0308/)
 
 
 Copyright
diff --git a/pep-0505/find-pep505.out b/pep-0505/find-pep505.out
new file mode 100644
--- /dev/null
+++ b/pep-0505/find-pep505.out
@@ -0,0 +1,2514 @@
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:495
+    if _hextobyte is None:
+        _hextobyte = {(a + b).encode(): bytes([int(a + b, 16)])
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:522
+    if encoding is None:
+        encoding = 'utf-8'
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:524
+    if errors is None:
+        errors = 'replace'
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:854
+    if _typeprog is None:
+        _typeprog = re.compile('^([^/:]+):')
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:867
+    if _hostprog is None:
+        _hostprog = re.compile('^//([^/?]*)(.*)$')
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:883
+    if _userprog is None:
+        _userprog = re.compile('^(.*)@(.*)$')
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:894
+    if _passwdprog is None:
+        _passwdprog = re.compile('^([^:]*):(.*)$',re.S)
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:906
+    if _portprog is None:
+        _portprog = re.compile('^(.*):([0-9]*)$')
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:923
+    if _nportprog is None:
+        _nportprog = re.compile('^(.*):(.*)$')
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:941
+    if _queryprog is None:
+        _queryprog = re.compile('^(.*)\?([^?]*)$')
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:952
+    if _tagprog is None:
+        _tagprog = re.compile('^(.*)#([^#]*)$')
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/parse.py:969
+    if _valueprog is None:
+        _valueprog = re.compile('^([^=]*)=(.*)$')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/parse.py:396
+        url = '//' + (netloc or '') + url
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:274
+        if origin_req_host is None:
+            origin_req_host = request_host(self)
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:727
+        if proxies is None:
+            proxies = getproxies()
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:739
+        if proxy_type is None:
+            proxy_type = orig_type
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:853
+        if password_mgr is None:
+            password_mgr = HTTPPasswordMgr()
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:935
+        if passwd is None:
+            passwd = HTTPPasswordMgr()
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:1234
+        if cookiejar is None:
+            cookiejar = http.cookiejar.CookieJar()
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:1373
+        if port is None:
+            port = ftplib.FTP_PORT
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:1550
+        if proxies is None:
+            proxies = getproxies()
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:2195
+    if _localhost is None:
+        _localhost = socket.gethostbyname('localhost')
+
+None-coalescing `if` block: /usr/lib/python3.4/urllib/request.py:2223
+    if _noheaders is None:
+        _noheaders = email.message_from_string("")
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1344
+                (mtype or 'text/plain', size, modified))
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1385
+        user = user or ''
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1386
+        passwd = passwd or ''
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1668
+                garbage, path = splithost(path or "")
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1669
+                path, garbage = splitquery(path or "")
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1670
+                path, garbage = splitattr(path or "")
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1862
+            (mtype or 'text/plain', size, modified))
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1891
+        user = unquote(user or '')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/urllib/request.py:1892
+        passwd = unquote(passwd or '')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/ssl.py:625
+                v = self._sslobj.read(len or 1024)
+
+None-coalescing `if` block: /usr/lib/python3.4/socket.py:217
+        if buffering is None:
+            buffering = -1
+
+Safe navigation `if` block: /usr/lib/python3.4/socket.py:508
+            if sock is not None:
+                sock.close()
+
+None-coalescing `if` block: /usr/lib/python3.4/ftplib.py:464
+        if callback is None:
+            callback = print_line
+
+None-coalescing `if` block: /usr/lib/python3.4/ftplib.py:729
+            if context is None:
+                context = ssl._create_stdlib_context(self.ssl_version,
+
+None-coalescing `if` block: /usr/lib/python3.4/http/server.py:424
+        if message is None:
+            message = shortmsg
+
+None-coalescing `if` block: /usr/lib/python3.4/http/server.py:426
+        if explain is None:
+            explain = longmsg
+
+None-coalescing `if` block: /usr/lib/python3.4/http/server.py:545
+        if timestamp is None:
+            timestamp = time.time()
+
+None-coalescing `if` block: /usr/lib/python3.4/http/cookies.py:399
+        if attrs is None:
+            attrs = self._reserved
+
+None-coalescing `if` block: /usr/lib/python3.4/http/client.py:1207
+            if context is None:
+                context = ssl._create_default_https_context()
+
+None-coalescing `if` block: /usr/lib/python3.4/http/client.py:1210
+            if check_hostname is None:
+                check_hostname = context.check_hostname
+
+None-coalescing `if` block: /usr/lib/python3.4/http/cookiejar.py:162
+    if hr is None: hr = 0
+
+None-coalescing `if` block: /usr/lib/python3.4/http/cookiejar.py:163
+    if min is None: min = 0
+
+None-coalescing `if` block: /usr/lib/python3.4/http/cookiejar.py:164
+    if sec is None: sec = 0
+
+None-coalescing `if` block: /usr/lib/python3.4/http/cookiejar.py:781
+        if now is None: now = time.time()
+
+None-coalescing `if` block: /usr/lib/python3.4/http/cookiejar.py:1221
+        if policy is None:
+            policy = DefaultCookiePolicy()
+
+None-coalescing `if` block: /usr/lib/python3.4/http/cookiejar.py:1548
+        if rfc2109_as_ns is None:
+            rfc2109_as_ns = not self._policy.rfc2965
+
+Safe navigation `if` block: /usr/lib/python3.4/http/cookiejar.py:269
+    if m is not None:
+        day, mon, yr, hr, min, sec, tz = m.groups()
+    else:
+        return None  # bad format
+
+Safe navigation `if` block: /usr/lib/python3.4/http/cookiejar.py:312
+    if m is not None:
+        # XXX there's an extra bit of the timezone I'm ignoring here: is
+        #   this the right thing to do?
+        yr, mon, day, hr, min, sec, tz, _ = m.groups()
+    else:
+        return None  # bad format
+
+Safe navigation `if` block: /usr/lib/python3.4/http/cookiejar.py:1895
+                        if k is not None:
+                            lc = k.lower()
+                        else:
+                            lc = None
+
+None-coalescing `if` block: /usr/lib/python3.4/imaplib.py:744
+        if ssl_context is None:
+            ssl_context = ssl._create_stdlib_context()
+
+None-coalescing `if` block: /usr/lib/python3.4/imaplib.py:855
+        if dat is None:
+            dat = b''
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/imaplib.py:1469
+    PASSWD = getpass.getpass("IMAP password for %s on %s: " % (USER, host or "localhost"))
+
+None-coalescing `if` block: /usr/lib/python3.4/pyclbr.py:60
+        if super is None:
+            super = []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/pyclbr.py:85
+    for key, value in _readmodule(module, path or []).items():
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/pyclbr.py:97
+    return _readmodule(module, path or [])
+
+None-coalescing `if` block: /usr/lib/python3.4/tarfile.py:620
+        if blockinfo is None:
+            blockinfo = [(0, size)]
+
+None-coalescing `if` block: /usr/lib/python3.4/tarfile.py:673
+        if size is None:
+            size = self.size - self.position
+
+None-coalescing `if` block: /usr/lib/python3.4/tarfile.py:1764
+        if arcname is None:
+            arcname = name
+
+None-coalescing `if` block: /usr/lib/python3.4/tarfile.py:1887
+        if arcname is None:
+            arcname = name
+
+None-coalescing `if` block: /usr/lib/python3.4/tarfile.py:1970
+        if members is None:
+            members = self
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tarfile.py:351
+        self.name     = name or ""
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tarfile.py:1557
+            filemode = filemode or "r"
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tarfile.py:1558
+            comptype = comptype or "tar"
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tarfile.py:1570
+            filemode = filemode or "r"
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tarfile.py:1571
+            comptype = comptype or "tar"
+
+Safe navigation `if` block: /usr/lib/python3.4/tarfile.py:1199
+        if match is not None:
+            pax_headers["hdrcharset"] = match.group(1).decode("utf-8")
+
+Safe navigation `if` block: /usr/lib/python3.4/tarfile.py:1758
+        if fileobj is not None:
+            name = fileobj.name
+
+None-coalescing `if` block: /usr/lib/python3.4/pydoc.py:496
+        if marginalia is None:
+            marginalia = '<tt>' + ' ' * width + '</tt>'
+
+None-coalescing `if` block: /usr/lib/python3.4/pydoc.py:672
+        if docloc is not None:
+            docloc = '<br><a href="%(docloc)s">Module Reference</a>' % locals()
+        else:
+            docloc = ''
+
+None-coalescing `if` block: /usr/lib/python3.4/pydoc.py:1004
+        if shadowed is None: shadowed = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/pydoc.py:1592
+    if renderer is None:
+        renderer = text
+
+None-coalescing `if` block: /usr/lib/python3.4/pydoc.py:1639
+    if done is None: done = {}
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/pydoc.py:85
+        dir = os.path.abspath(dir or '.')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/pydoc.py:491
+    ''' % (bgcol, fgcol, title, fgcol, extras or ' ')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/pydoc.py:1961
+            xrefs = (xrefs or '') + ' ' + more_xrefs
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/pydoc.py:1992
+            xrefs = (xrefs or '') + ' ' + more_xrefs
+
+Safe navigation `if` block: /usr/lib/python3.4/pydoc.py:1618
+        if output is None:
+            pager(render_doc(thing, title, forceload))
+        else:
+            output.write(render_doc(thing, title, forceload, plaintext))
+
+None-coalescing `if` block: /usr/lib/python3.4/hashlib.py:184
+        if dklen is None:
+            dklen = outer.digest_size
+
+None-coalescing `if` block: /usr/lib/python3.4/os.py:583
+    if env is None:
+        env = environ
+
+None-coalescing `if` block: /usr/lib/python3.4/os.py:610
+    if path_list is None:
+        path_list = defpath
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/os.py:469
+                if follow_symlinks or path.samestat(orig_st, stat(dirfd)):
+
+Safe navigation `if` block: /usr/lib/python3.4/os.py:363
+        if onerror is not None:
+            onerror(err)
+
+None-coalescing `if` block: /usr/lib/python3.4/tempfile.py:401
+    if dir is None:
+        dir = gettempdir()
+
+None-coalescing `if` block: /usr/lib/python3.4/tempfile.py:425
+    if dir is None:
+        dir = gettempdir()
+
+None-coalescing `if` block: /usr/lib/python3.4/tempfile.py:459
+    if dir is None:
+        dir = gettempdir()
+
+None-coalescing `if` block: /usr/lib/python3.4/tempfile.py:589
+    if dir is None:
+        dir = gettempdir()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/ctypes/util.py:157
+            return nums or [ sys.maxsize ]
+
+None-coalescing `if` block: /usr/lib/python3.4/ctypes/__init__.py:146
+    if typecode is None:
+        # Most _type_ codes are the same as used in struct
+        typecode = typ._type_
+
+None-coalescing `if` block: /usr/lib/python3.4/ctypes/__init__.py:350
+        if handle is None:
+            self._handle = _dlopen(self._name, mode)
+        else:
+            self._handle = handle
+
+None-coalescing `if` block: /usr/lib/python3.4/warnings.py:170
+    if category is None:
+        category = UserWarning
+
+None-coalescing `if` block: /usr/lib/python3.4/warnings.py:211
+    if registry is None:
+        registry = {}
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/warnings.py:208
+        module = filename or "<unknown>"
+
+None-coalescing ternary: /usr/lib/python3.4/warnings.py:26
+    line = linecache.getline(filename, lineno) if line is None else line
+
+None-coalescing ternary: /usr/lib/python3.4/warnings.py:323
+        self._module = sys.modules['warnings'] if module is None else module
+
+Safe navigation `if` block: /usr/lib/python3.4/dis.py:240
+    if const_list is not None:
+        argval = const_list[const_index]
+
+None-coalescing `if` block: /usr/lib/python3.4/statistics.py:461
+    if c is None:
+        c = mean(data)
+
+Safe navigation `if` block: /usr/lib/python3.4/_weakrefset.py:22
+        if w is not None:
+            w._iterating.add(self)
+
+None-coalescing `if` block: /usr/lib/python3.4/zipfile.py:374
+        if zip64 is None:
+            zip64 = file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT
+
+None-coalescing `if` block: /usr/lib/python3.4/zipfile.py:1225
+        if path is None:
+            path = os.getcwd()
+
+None-coalescing `if` block: /usr/lib/python3.4/zipfile.py:1236
+        if members is None:
+            members = self.namelist()
+
+None-coalescing `if` block: /usr/lib/python3.4/zipfile.py:1331
+        if arcname is None:
+            arcname = filename
+
+None-coalescing `if` block: /usr/lib/python3.4/zipfile.py:1340
+        if compress_type is None:
+            zinfo.compress_type = self.compression
+        else:
+            zinfo.compress_type = compress_type
+
+None-coalescing `if` block: /usr/lib/python3.4/zipfile.py:1752
+    if args is None:
+        args = sys.argv[1:]
+
+None-coalescing `if` block: /usr/lib/python3.4/pdb.py:1224
+        if last is None:
+            last = first + 10
+
+None-coalescing `if` block: /usr/lib/python3.4/pdb.py:1585
+    if t is None:
+        # sys.exc_info() returns (type, value, traceback) if an exception is
+        # being handled, otherwise it returns None
+        t = sys.exc_info()[2]
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/pdb.py:935
+            count = int(arg or 1)
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/pdb.py:955
+            count = int(arg or 1)
+
+None-coalescing `if` block: /usr/lib/python3.4/posixpath.py:445
+    if start is None:
+        start = curdir
+
+Safe navigation `if` block: /usr/lib/python3.4/posixpath.py:300
+            if environ is None:
+                value = os.fsencode(os.environ[os.fsdecode(name)])
+            else:
+                value = environ[name]
+
+Safe navigation `if` block: /usr/lib/python3.4/xml/sax/expatreader.py:223
+        if bs is not None:
+            bs.close()
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/sax/__init__.py:38
+    if errorHandler is None:
+        errorHandler = ErrorHandler()
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/sax/_exceptions.py:92
+        if sysid is None:
+            sysid = "<unknown>"
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/sax/_exceptions.py:95
+        if linenum is None:
+            linenum = "?"
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/sax/_exceptions.py:98
+        if colnum is None:
+            colnum = "?"
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/dom/pulldom.py:325
+    if bufsize is None:
+        bufsize = default_bufsize
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xml/dom/pulldom.py:44
+        self._xmlns_attrs.append((prefix or 'xmlns', uri))
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/dom/expatbuilder.py:138
+        if options is None:
+            options = xmlbuilder.Options()
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/dom/xmlbuilder.py:359
+        if snode is None:
+            snode = self
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/etree/ElementTree.py:1225
+        if events is None:
+            events = ("end",)
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/etree/ElementTree.py:1389
+        if element_factory is None:
+            element_factory = Element
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/etree/ElementTree.py:1468
+        if target is None:
+            target = TreeBuilder()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xml/etree/ElementTree.py:937
+            if text or len(elem) or not short_empty_elements:
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xml/etree/ElementTree.py:1223
+        self._parser = _parser or XMLParser(target=TreeBuilder())
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xml/etree/ElementTree.py:1523
+                    append((event, (prefix or "", uri or "")))
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xml/etree/ElementTree.py:1523
+                    append((event, (prefix or "", uri or "")))
+
+None-coalescing `if` block: /usr/lib/python3.4/xml/etree/ElementInclude.py:100
+    if loader is None:
+        loader = default_loader
+
+Safe navigation `and`: /usr/lib/python3.4/weakref.py:512
+        obj = info and info.weakref()
+
+Safe navigation `and`: /usr/lib/python3.4/weakref.py:520
+        obj = info and info.weakref()
+
+Safe navigation `and`: /usr/lib/python3.4/weakref.py:543
+        obj = info and info.weakref()
+
+None-coalescing `if` block: /usr/lib/python3.4/pprint.py:132
+        if stream is not None:
+            self._stream = stream
+        else:
+            self._stream = _sys.stdout
+
+None-coalescing `if` block: /usr/lib/python3.4/pprint.py:410
+    if object is None:
+        object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
+
+None-coalescing `if` block: /usr/lib/python3.4/json/decoder.py:145
+    if memo is None:
+        memo = {}
+
+Safe navigation `if` block: /usr/lib/python3.4/json/decoder.py:216
+    if object_hook is not None:
+        pairs = object_hook(pairs)
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/json/scanner.py:52
+                res = parse_float(integer + (frac or '') + (exp or ''))
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/json/scanner.py:52
+                res = parse_float(integer + (frac or '') + (exp or ''))
+
+None-coalescing `if` block: /usr/lib/python3.4/json/__init__.py:231
+    if cls is None:
+        cls = JSONEncoder
+
+None-coalescing `if` block: /usr/lib/python3.4/json/__init__.py:319
+    if cls is None:
+        cls = JSONDecoder
+
+None-coalescing `if` block: /usr/lib/python3.4/json/encoder.py:147
+        if separators is not None:
+            self.item_separator, self.key_separator = separators
+        elif indent is not None:
+
+None-coalescing `if` block: /usr/lib/python3.4/timeit.py:243
+    if args is None:
+        args = sys.argv[1:]
+
+Safe navigation `if` block: /usr/lib/python3.4/timeit.py:290
+    if _wrap_timer is not None:
+        timer = _wrap_timer(timer)
+
+None-coalescing `if` block: /usr/lib/python3.4/smtplib.py:835
+        if from_addr is None:
+            # Prefer the sender field per RFC 2822:3.6.2.
+            from_addr = (msg[header_prefix + 'Sender']
+
+None-coalescing `if` block: /usr/lib/python3.4/profile.py:151
+        if bias is None:
+            bias = self.bias
+
+None-coalescing `if` block: /usr/lib/python3.4/pickletools.py:2391
+    if memo is None:
+        memo = {}       # crude emulation of unpickler memo
+
+None-coalescing ternary: /usr/lib/python3.4/pickletools.py:2241
+                                 "<unknown>" if pos is None else pos,
+
+None-coalescing `if` block: /usr/lib/python3.4/collections/__init__.py:852
+        if m is None:
+            m = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/multiprocessing/managers.py:357
+            if exposed is None:
+                exposed = public_methods(obj)
+
+None-coalescing `if` block: /usr/lib/python3.4/multiprocessing/managers.py:444
+        if authkey is None:
+            authkey = process.current_process().authkey
+
+None-coalescing `if` block: /usr/lib/python3.4/multiprocessing/managers.py:620
+        if proxytype is None:
+            proxytype = AutoProxy
+
+None-coalescing `if` block: /usr/lib/python3.4/multiprocessing/managers.py:896
+    if authkey is None:
+        authkey = process.current_process().authkey
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/managers.py:452
+        self._ctx = ctx or get_context()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/managers.py:623
+        exposed = exposed or getattr(proxytype, '_exposed_', None)
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/managers.py:625
+        method_to_typeid = method_to_typeid or \
+                           getattr(proxytype, '_method_to_typeid_', None)
+
+Safe navigation `and`: /usr/lib/python3.4/multiprocessing/managers.py:245
+                    typeid = gettypeid and gettypeid.get(methodname, None)
+
+Safe navigation `if` block: /usr/lib/python3.4/multiprocessing/managers.py:351
+            if callable is None:
+                assert len(args) == 1 and not kwds
+                obj = args[0]
+            else:
+                obj = callable(*args, **kwds)
+
+Safe navigation `if` block: /usr/lib/python3.4/multiprocessing/managers.py:513
+        if initializer is not None:
+            initializer(*initargs)
+
+None-coalescing `if` block: /usr/lib/python3.4/multiprocessing/synchronize.py:52
+        if ctx is None:
+            ctx = context._default_context.get_context()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/synchronize.py:214
+        self._lock = lock or ctx.RLock()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/util.py:160
+        self._kwargs = kwargs or {}
+
+None-coalescing `if` block: /usr/lib/python3.4/multiprocessing/connection.py:194
+        if size is None:
+            size = n - offset
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/connection.py:441
+        address = address or arbitrary_address(family)
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/connection.py:490
+    family = family or address_type(address)
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/sharedctypes.py:77
+        ctx = ctx or get_context()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/sharedctypes.py:91
+        ctx = ctx or get_context()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/sharedctypes.py:104
+    ctx = ctx or get_context()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/sharedctypes.py:186
+            ctx = ctx or get_context(force=True)
+
+None-coalescing `if` block: /usr/lib/python3.4/multiprocessing/pool.py:158
+        if processes is None:
+            processes = os.cpu_count() or 1
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/multiprocessing/pool.py:149
+        self._ctx = context or get_context()
+
+Safe navigation `if` block: /usr/lib/python3.4/multiprocessing/pool.py:102
+    if initializer is not None:
+        initializer(*initargs)
+
+None-coalescing `if` block: /usr/lib/python3.4/multiprocessing/spawn.py:174
+    if main_mod_name is not None:
+        d['init_main_from_name'] = main_mod_name
+    elif sys.platform != 'win32' or (not WINEXE and not WINSERVICE):
+
+None-coalescing `if` block: /usr/lib/python3.4/sre_parse.py:92
+        if data is None:
+            data = []
+
+None-coalescing `if` block: /usr/lib/python3.4/sre_parse.py:392
+            if prefix is None:
+                prefix = item[0]
+
+None-coalescing `if` block: /usr/lib/python3.4/sre_parse.py:755
+    if pattern is None:
+        pattern = Pattern()
+
+Safe navigation ternary: /usr/lib/python3.4/sre_parse.py:856
+        literals = [None if s is None else s.encode('latin-1') for s in literals]
+
+None-coalescing `if` block: /usr/lib/python3.4/locale.py:497
+        if language is None:
+            language = 'C'
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/locale.py:1619
+    print('Language: ', lang or '(undefined)')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/locale.py:1620
+    print('Encoding: ', enc or '(undefined)')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/locale.py:1628
+        print('   Language: ', lang or '(undefined)')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/locale.py:1629
+        print('   Encoding: ', enc or '(undefined)')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/locale.py:1639
+        print('   Language: ', lang or '(undefined)')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/locale.py:1640
+        print('   Encoding: ', enc or '(undefined)')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/locale.py:1656
+            print('   Language: ', lang or '(undefined)')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/locale.py:1657
+            print('   Encoding: ', enc or '(undefined)')
+
+None-coalescing `if` block: /usr/lib/python3.4/xmlrpc/server.py:214
+        if name is None:
+            name = function.__name__
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xmlrpc/server.py:168
+        self.encoding = encoding or 'utf-8'
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xmlrpc/server.py:604
+        self.encoding = encoding or 'utf-8'
+
+Safe navigation `if` block: /usr/lib/python3.4/xmlrpc/server.py:253
+            if dispatch_method is not None:
+                response = dispatch_method(method, params)
+            else:
+                response = self._dispatch(method, params)
+
+None-coalescing `if` block: /usr/lib/python3.4/xmlrpc/client.py:387
+        if data is None:
+            data = b""
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xmlrpc/client.py:1353
+            None, context=self.context, **(x509 or {}))
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/xmlrpc/client.py:1420
+        self.__encoding = encoding or 'utf-8'
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/nntplib.py:173
+            parts.append(v.decode(enc or 'ascii'))
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/nntplib.py:828
+            cmd += ' {0}-{1}'.format(start, end or '')
+
+None-coalescing `if` block: /usr/lib/python3.4/gettext.py:355
+    if localedir is None:
+        localedir = _default_localedir
+
+None-coalescing `if` block: /usr/lib/python3.4/gettext.py:410
+    if class_ is None:
+        class_ = GNUTranslations
+
+None-coalescing `if` block: /usr/lib/python3.4/gettext.py:432
+        if result is None:
+            result = t
+
+Safe navigation `if` block: /usr/lib/python3.4/gettext.py:432
+        if result is None:
+            result = t
+        else:
+            result.add_fallback(t)
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/wsgiref/headers.py:19
+        if quote or tspecials.search(value):
+
+Safe navigation `if` block: /usr/lib/python3.4/pathlib.py:919
+        if template is not None:
+            self._accessor = template._accessor
+        else:
+            self._accessor = _normal_accessor
+
+None-coalescing `if` block: /usr/lib/python3.4/email/mime/audio.py:67
+        if _subtype is None:
+            _subtype = _whatsnd(_audiodata)
+
+None-coalescing `if` block: /usr/lib/python3.4/email/mime/image.py:40
+        if _subtype is None:
+            _subtype = imghdr.what(None, _imagedata)
+
+None-coalescing `if` block: /usr/lib/python3.4/email/contentmanager.py:47
+            if full_path_for_error is None:
+                full_path_for_error = full_path
+
+None-coalescing ternary: /usr/lib/python3.4/email/contentmanager.py:209
+        cte = '8bit' if cte is None else cte
+
+None-coalescing `if` block: /usr/lib/python3.4/email/header.py:209
+        if charset is None:
+            charset = USASCII
+
+None-coalescing `if` block: /usr/lib/python3.4/email/header.py:218
+        if maxlinelen is None:
+            maxlinelen = MAXLINELEN
+
+None-coalescing `if` block: /usr/lib/python3.4/email/header.py:289
+        if charset is None:
+            charset = self._charset
+
+None-coalescing `if` block: /usr/lib/python3.4/email/header.py:350
+        if maxlinelen is None:
+            maxlinelen = self._maxlinelen
+
+None-coalescing `if` block: /usr/lib/python3.4/email/header.py:572
+        if startval is None:
+            startval = []
+
+None-coalescing `if` block: /usr/lib/python3.4/email/utils.py:156
+    if timeval is None:
+        timeval = time.time()
+
+None-coalescing `if` block: /usr/lib/python3.4/email/utils.py:216
+    if idstring is None:
+        idstring = ''
+
+None-coalescing `if` block: /usr/lib/python3.4/email/utils.py:220
+    if domain is None:
+        domain = socket.getfqdn()
+
+None-coalescing `if` block: /usr/lib/python3.4/email/utils.py:272
+    if language is None:
+        language = ''
+
+None-coalescing `if` block: /usr/lib/python3.4/email/utils.py:344
+    if charset is None:
+        # Issue 17369: if charset/lang is None, decode_rfc2231 couldn't parse
+        # the value, so use the fallback_charset.
+        charset = fallback_charset
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/email/utils.py:269
+    s = urllib.parse.quote(s, safe='', encoding=charset or 'ascii')
+
+None-coalescing `if` block: /usr/lib/python3.4/email/generator.py:257
+        if subparts is None:
+            subparts = []
+
+None-coalescing `if` block: /usr/lib/python3.4/email/generator.py:475
+        if fmt is None:
+            self._fmt = _FMT
+        else:
+            self._fmt = fmt
+
+Safe navigation `if` block: /usr/lib/python3.4/email/generator.py:191
+        if meth is None:
+            self._write_headers(msg)
+        else:
+            meth(self)
+
+None-coalescing `if` block: /usr/lib/python3.4/email/_parseaddr.py:460
+        if atomends is None:
+            atomends = self.atomends
+
+None-coalescing `if` block: /usr/lib/python3.4/email/iterators.py:61
+    if fp is None:
+        fp = sys.stdout
+
+None-coalescing `if` block: /usr/lib/python3.4/email/message.py:1068
+        if content_manager is None:
+            content_manager = self.policy.content_manager
+
+None-coalescing `if` block: /usr/lib/python3.4/email/message.py:1073
+        if content_manager is None:
+            content_manager = self.policy.content_manager
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/email/message.py:67
+        if quote or tspecials.search(value):
+
+None-coalescing ternary: /usr/lib/python3.4/email/message.py:153
+        policy = self.policy if policy is None else policy
+
+None-coalescing ternary: /usr/lib/python3.4/email/message.py:176
+        policy = self.policy if policy is None else policy
+
+None-coalescing `if` block: /usr/lib/python3.4/email/_header_value_parser.py:130
+        if stoken is None:
+            stoken = str(token)
+
+Safe navigation `and`: /usr/lib/python3.4/email/_header_value_parser.py:2591
+    while value and value[0].isdigit():
+
+None-coalescing `if` block: /usr/lib/python3.4/concurrent/futures/process.py:334
+        if max_workers is None:
+            self._max_workers = os.cpu_count() or 1
+        else:
+            self._max_workers = max_workers
+
+None-coalescing `if` block: /usr/lib/python3.4/threading.py:211
+        if lock is None:
+            lock = RLock()
+
+None-coalescing `if` block: /usr/lib/python3.4/threading.py:604
+        if timeout is None:
+            timeout = self._timeout
+
+None-coalescing `if` block: /usr/lib/python3.4/threading.py:785
+        if kwargs is None:
+            kwargs = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/threading.py:791
+        if daemon is not None:
+            self._daemonic = daemon
+        else:
+            self._daemonic = current_thread().daemon
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/threading.py:788
+        self._name = str(name or _newname())
+
+None-coalescing ternary: /usr/lib/python3.4/threading.py:1175
+        self.args = args if args is not None else []
+
+None-coalescing ternary: /usr/lib/python3.4/threading.py:1176
+        self.kwargs = kwargs if kwargs is not None else {}
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:363
+        if frame is None:
+            frame = sys._getframe().f_back
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:469
+        if options is None: options = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:1187
+        if verbose is None:
+            verbose = '-v' in sys.argv
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:1428
+        if compileflags is None:
+            compileflags = _extract_future_flags(test.globs)
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:1490
+        if verbose is None:
+            verbose = self._verbose
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:1916
+    if m is None:
+        # DWA - m will still be None if this wasn't invoked from the command
+        # line, in which case the following TypeError is about as good an error
+        # as we should expect
+        m = sys.modules.get('__main__')
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:1927
+    if name is None:
+        name = m.__name__
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:1944
+    if master is None:
+        master = runner
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:2042
+    if name is None:
+        name = os.path.basename(filename)
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:2046
+    if globs is None:
+        globs = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:2067
+    if master is None:
+        master = runner
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:2359
+    if test_finder is None:
+        test_finder = DocTestFinder()
+
+None-coalescing `if` block: /usr/lib/python3.4/doctest.py:2412
+    if globs is None:
+        globs = {}
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/doctest.py:1186
+        self._checker = checker or OutputChecker()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/doctest.py:2039
+                                    encoding or "utf-8")
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/doctest.py:2423
+                               encoding or "utf-8")
+
+Safe navigation `if` block: /usr/lib/python3.4/doctest.py:908
+        if globs is None:
+            if module is None:
+                globs = {}
+            else:
+                globs = module.__dict__.copy()
+        else:
+            globs = globs.copy()
+
+Safe navigation `if` block: /usr/lib/python3.4/doctest.py:1944
+    if master is None:
+        master = runner
+    else:
+        master.merge(runner)
+
+Safe navigation `if` block: /usr/lib/python3.4/doctest.py:2046
+    if globs is None:
+        globs = {}
+    else:
+        globs = globs.copy()
+
+Safe navigation `if` block: /usr/lib/python3.4/doctest.py:2067
+    if master is None:
+        master = runner
+    else:
+        master.merge(runner)
+
+Safe navigation `if` block: /usr/lib/python3.4/doctest.py:2412
+    if globs is None:
+        globs = {}
+    else:
+        globs = globs.copy()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncore.py:126
+    if map is None:
+        map = socket_map
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncore.py:169
+    if map is None:
+        map = socket_map
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncore.py:198
+    if map is None:
+        map = socket_map
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncore.py:226
+        if map is None:
+            self._map = socket_map
+        else:
+            self._map = map
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncore.py:274
+        if map is None:
+            map = self._map
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncore.py:280
+        if map is None:
+            map = self._map
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncore.py:576
+    if map is None:
+        map = socket_map
+
+None-coalescing `if` block: /usr/lib/python3.4/bdb.py:211
+        if lineno is None:
+            lineno = frame.f_lineno + 1
+
+None-coalescing `if` block: /usr/lib/python3.4/bdb.py:243
+        if frame is None:
+            frame = sys._getframe().f_back
+
+None-coalescing `if` block: /usr/lib/python3.4/bdb.py:424
+        if locals is None:
+            locals = globals
+
+None-coalescing `if` block: /usr/lib/python3.4/bdb.py:442
+        if locals is None:
+            locals = globals
+
+None-coalescing `if` block: /usr/lib/python3.4/bdb.py:536
+        if out is None:
+            out = sys.stdout
+
+None-coalescing `if` block: /usr/lib/python3.4/cgi.py:140
+    if fp is None:
+        fp = sys.stdin
+
+None-coalescing `if` block: /usr/lib/python3.4/shelve.py:87
+        if protocol is None:
+            protocol = 3
+
+None-coalescing `if` block: /usr/lib/python3.4/tkinter/filedialog.py:52
+        if title is None: title = self.title
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tkinter/filedialog.py:218
+        self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
+
+None-coalescing `if` block: /usr/lib/python3.4/tkinter/__init__.py:489
+        if window is None:
+            window = self
+
+None-coalescing `if` block: /usr/lib/python3.4/tkinter/__init__.py:497
+        if window is None:
+            window = self
+
+None-coalescing `if` block: /usr/lib/python3.4/tkinter/__init__.py:2782
+        if index2 is None:
+            index2 = index1
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tkinter/ttk.py:184
+        opts = opts or {}
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tkinter/ttk.py:665
+        Widget.__init__(self, master, widget or "ttk::entry", kw)
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/tkinter/ttk.py:1490
+        self._variable = variable or tkinter.IntVar(master)
+
+None-coalescing `if` block: /usr/lib/python3.4/code.py:33
+        if locals is None:
+            locals = {"__name__": "__console__", "__doc__": None}
+
+None-coalescing `if` block: /usr/lib/python3.4/code.py:291
+    if readfunc is not None:
+        console.raw_input = readfunc
+    else:
+        try:
+
+Safe navigation `if` block: /usr/lib/python3.4/plistlib.py:331
+        if handler is not None:
+            handler(attrs)
+
+Safe navigation `if` block: /usr/lib/python3.4/plistlib.py:336
+        if handler is not None:
+            handler()
+
+None-coalescing `if` block: /usr/lib/python3.4/uu.py:72
+        if name is None:
+            name = '-'
+
+None-coalescing `if` block: /usr/lib/python3.4/uu.py:74
+        if mode is None:
+            mode = 0o666
+
+None-coalescing `if` block: /usr/lib/python3.4/uu.py:124
+        if mode is None:
+            mode = int(hdrfields[1], 8)
+
+None-coalescing `if` block: /usr/lib/python3.4/contextlib.py:42
+        if doc is None:
+            doc = type(self).__doc__
+
+None-coalescing `if` block: /usr/lib/python3.4/subprocess.py:764
+        if bufsize is None:
+            bufsize = -1  # Restore default
+
+None-coalescing `if` block: /usr/lib/python3.4/runpy.py:179
+    if run_name is None:
+        run_name = mod_name
+
+None-coalescing `if` block: /usr/lib/python3.4/runpy.py:226
+    if run_name is None:
+        run_name = "<run_path>"
+
+Safe navigation ternary: /usr/lib/python3.4/runpy.py:92
+    fname = script_name if mod_spec is None else mod_spec.origin
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/cgitb.py:271
+        info = info or sys.exc_info()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/asyncio/unix_events.py:418
+        if not (is_socket or
+                stat.S_ISFIFO(mode) or
+                stat.S_ISCHR(mode)):
+
+Safe navigation `if` block: /usr/lib/python3.4/asyncio/unix_events.py:939
+            if callback is None:
+                logger.warning(
+                    "Caught subprocess termination from unknown pid: "
+                    "%d -> %d", pid, returncode)
+            else:
+                callback(pid, returncode, *args)
+
+Safe navigation `if` block: /usr/lib/python3.4/asyncio/proactor_events.py:475
+            if f is not None:
+                f.result()  # may raise
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/futures.py:150
+        if loop is None:
+            self._loop = events.get_event_loop()
+        else:
+            self._loop = loop
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/futures.py:397
+    if loop is None:
+        loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/streams.py:58
+    if loop is None:
+        loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/streams.py:92
+    if loop is None:
+        loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/streams.py:148
+        if loop is None:
+            self._loop = events.get_event_loop()
+        else:
+            self._loop = loop
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/streams.py:311
+        if loop is None:
+            self._loop = events.get_event_loop()
+        else:
+            self._loop = loop
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/tasks.py:56
+        if loop is None:
+            loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/tasks.py:66
+        if loop is None:
+            loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/tasks.py:327
+    if loop is None:
+        loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/tasks.py:354
+    if loop is None:
+        loop = events.get_event_loop()
+
+None-coalescing ternary: /usr/lib/python3.4/asyncio/tasks.py:451
+    loop = loop if loop is not None else events.get_event_loop()
+
+Safe navigation `if` block: /usr/lib/python3.4/asyncio/tasks.py:417
+        if timeout_handle is not None:
+            timeout_handle.cancel()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/subprocess.py:192
+    if loop is None:
+        loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/subprocess.py:206
+    if loop is None:
+        loop = events.get_event_loop()
+
+Safe navigation `if` block: /usr/lib/python3.4/asyncio/subprocess.py:69
+        if reader is not None:
+            reader.feed_data(data)
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/queues.py:42
+        if loop is None:
+            self._loop = events.get_event_loop()
+        else:
+            self._loop = loop
+
+Safe navigation `if` block: /usr/lib/python3.4/asyncio/base_events.py:729
+                if sock is not None:
+                    sock.close()
+
+Safe navigation `if` block: /usr/lib/python3.4/asyncio/base_events.py:733
+                if sock is not None:
+                    sock.close()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/windows_events.py:302
+        if proactor is None:
+            proactor = IocpProactor()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/transports.py:16
+        if extra is None:
+            extra = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/transports.py:287
+        if low is None:
+            low = high // 4
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/locks.py:99
+        if loop is not None:
+            self._loop = loop
+        else:
+            self._loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/locks.py:194
+        if loop is not None:
+            self._loop = loop
+        else:
+            self._loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/locks.py:259
+        if loop is not None:
+            self._loop = loop
+        else:
+            self._loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/locks.py:264
+        if lock is None:
+            lock = Lock(loop=self._loop)
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/locks.py:391
+        if loop is not None:
+            self._loop = loop
+        else:
+            self._loop = events.get_event_loop()
+
+None-coalescing `if` block: /usr/lib/python3.4/asyncio/selector_events.py:51
+        if selector is None:
+            selector = selectors.DefaultSelector()
+
+Safe navigation `if` block: /usr/lib/python3.4/asyncio/selector_events.py:242
+            if reader is not None:
+                reader.cancel()
+
+Safe navigation `if` block: /usr/lib/python3.4/asyncio/selector_events.py:280
+            if writer is not None:
+                writer.cancel()
+
+None-coalescing `if` block: /usr/lib/python3.4/copy.py:143
+    if memo is None:
+        memo = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/copy.py:276
+    if memo is None:
+        memo = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/enum.py:333
+        if module is None:
+            _make_class_unpicklable(enum_class)
+        else:
+            enum_class.__module__ = module
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/string.py:44
+    return (sep or ' ').join(x.capitalize() for x in s.split(sep))
+
+None-coalescing `if` block: /usr/lib/python3.4/platform.py:1251
+    if sys_version is None:
+        sys_version = sys.version
+
+Safe navigation ternary: /usr/lib/python3.4/platform.py:181
+            s.decode('latin1') if s is not None else s
+
+None-coalescing `if` block: /usr/lib/python3.4/_osx_support.py:35
+    if path is None:
+        path = os.environ['PATH']
+
+None-coalescing `if` block: /usr/lib/python3.4/codecs.py:925
+    if file_encoding is None:
+        file_encoding = data_encoding
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/codecs.py:249
+        self.buffer = state or ""
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/codecs.py:547
+        readsize = size or 72
+
+None-coalescing `if` block: /usr/lib/python3.4/textwrap.py:419
+        if margin is None:
+            margin = indent
+
+None-coalescing `if` block: /usr/lib/python3.4/trace.py:505
+        if globals is None: globals = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/trace.py:506
+        if locals is None: locals = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/trace.py:659
+    if argv is None:
+        argv = sys.argv
+
+None-coalescing `if` block: /usr/lib/python3.4/poplib.py:387
+        if context is None:
+            context = ssl._create_stdlib_context()
+
+None-coalescing `if` block: /usr/lib/python3.4/modulefinder.py:75
+        if path is None:
+            path = sys.path
+
+Safe navigation `and`: /usr/lib/python3.4/modulefinder.py:267
+                                                   parent and parent.__path__, parent)
+
+None-coalescing `if` block: /usr/lib/python3.4/base64.py:206
+    if _b32rev is None:
+        _b32rev = {v: k for k, v in enumerate(_b32alphabet)}
+
+None-coalescing `if` block: /usr/lib/python3.4/gzip.py:186
+        if mode is None:
+            mode = getattr(fileobj, 'mode', 'rb')
+
+None-coalescing `if` block: /usr/lib/python3.4/gzip.py:269
+        if mtime is None:
+            mtime = time.time()
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/gzip.py:181
+            fileobj = self.myfileobj = builtins.open(filename, mode or 'rb')
+
+None-coalescing `if` block: /usr/lib/python3.4/optparse.py:1040
+        if option is None:
+            option = self._long_opt.get(opt_str)
+
+None-coalescing `if` block: /usr/lib/python3.4/optparse.py:1195
+        if formatter is None:
+            formatter = IndentedHelpFormatter()
+
+None-coalescing `if` block: /usr/lib/python3.4/optparse.py:1369
+        if values is None:
+            values = self.get_default_values()
+
+None-coalescing `if` block: /usr/lib/python3.4/optparse.py:1607
+        if formatter is None:
+            formatter = self.formatter
+
+None-coalescing `if` block: /usr/lib/python3.4/optparse.py:1627
+        if formatter is None:
+            formatter = self.formatter
+
+None-coalescing `if` block: /usr/lib/python3.4/optparse.py:1644
+        if file is None:
+            file = sys.stdout
+
+Safe navigation `if` block: /usr/lib/python3.4/test/pystone.py:174
+    if PtrGlb is not None:
+        PtrParOut = PtrGlb.PtrComp
+    else:
+        IntGlob = 100
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/test/regrtest.py:1514
+    return path or os.path.dirname(__file__) or os.curdir
+
+Safe navigation `if` block: /usr/lib/python3.4/test/regrtest.py:1475
+        if stream is not None:
+            stream.flush()
+
+None-coalescing `if` block: /usr/lib/python3.4/test/support/__init__.py:200
+    if pattern is None:
+        pattern = "test*"
+
+None-coalescing `if` block: /usr/lib/python3.4/test/support/__init__.py:1228
+            if v is None:
+                if k in self._environ:
+                    del self._environ[k]
+            else:
+                self._environ[k] = v
+
+None-coalescing `if` block: /usr/lib/python3.4/test/support/__init__.py:1527
+                if orig_tz is None:
+                    del os.environ['TZ']
+                else:
+                    os.environ['TZ'] = orig_tz
+
+None-coalescing `if` block: /usr/lib/python3.4/test/support/__init__.py:1838
+    if verbosity is None:
+        verbosity = verbose
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/test/support/__init__.py:305
+            dirname = dirname or '.'
+
+None-coalescing `if` block: /usr/lib/python3.4/argparse.py:299
+        if prefix is None:
+            prefix = _('usage: ')
+
+None-coalescing `if` block: /usr/lib/python3.4/argparse.py:1047
+        if version is None:
+            version = parser.version
+
+None-coalescing `if` block: /usr/lib/python3.4/argparse.py:1629
+        if prog is None:
+            prog = _os.path.basename(_sys.argv[0])
+
+None-coalescing `if` block: /usr/lib/python3.4/argparse.py:1744
+        if args is None:
+            # args default to the system args
+            args = _sys.argv[1:]
+
+None-coalescing `if` block: /usr/lib/python3.4/argparse.py:1752
+        if namespace is None:
+            namespace = Namespace()
+
+None-coalescing `if` block: /usr/lib/python3.4/argparse.py:2361
+        if file is None:
+            file = _sys.stdout
+
+None-coalescing `if` block: /usr/lib/python3.4/argparse.py:2366
+        if file is None:
+            file = _sys.stdout
+
+None-coalescing `if` block: /usr/lib/python3.4/logging/handlers.py:816
+        if use_socktype is None:
+            use_socktype = socket.SOCK_DGRAM
+
+None-coalescing `if` block: /usr/lib/python3.4/logging/__init__.py:951
+        if stream is None:
+            stream = sys.stderr
+
+Safe navigation `if` block: /usr/lib/python3.4/logging/__init__.py:1351
+        if f is not None:
+            f = f.f_back
+
+Safe navigation `if` block: /usr/lib/python3.4/unittest/suite.py:177
+        if previousClass is not None:
+            previousModule = previousClass.__module__
+
+None-coalescing `if` block: /usr/lib/python3.4/unittest/main.py:68
+        if argv is None:
+            argv = sys.argv
+
+Safe navigation ternary: /usr/lib/python3.4/unittest/main.py:224
+        loader = self.testLoader if Loader is None else Loader()
+
+None-coalescing `if` block: /usr/lib/python3.4/unittest/mock.py:380
+        if _new_parent is None:
+            _new_parent = parent
+
+None-coalescing `if` block: /usr/lib/python3.4/unittest/mock.py:392
+        if _eat_self is None:
+            _eat_self = parent is not None
+
+None-coalescing `if` block: /usr/lib/python3.4/unittest/mock.py:2294
+    if mock is None:
+        mock = MagicMock(name='open', spec=open)
+
+Safe navigation `and`: /usr/lib/python3.4/unittest/mock.py:452
+            _spec_signature = res and res[1]
+
+Safe navigation `if` block: /usr/lib/python3.4/unittest/mock.py:1765
+    if side_effector is not None:
+        method.side_effect = side_effector(mock)
+
+None-coalescing `if` block: /usr/lib/python3.4/unittest/case.py:227
+            if first_matching is None:
+                first_matching = w
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/unittest/case.py:1267
+            msg = msg or "Regex didn't match"
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/unittest/case.py:1277
+            msg = msg or "Regex matched"
+
+Safe navigation `if` block: /usr/lib/python3.4/unittest/case.py:472
+        if addSkip is not None:
+            addSkip(test_case, reason)
+        else:
+            warnings.warn("TestResult has no addSkip method, skips not reported",
+
+Safe navigation `if` block: /usr/lib/python3.4/unittest/case.py:491
+        if parent is None:
+            params_map = collections.ChainMap(params)
+        else:
+            params_map = parent.params.new_child(params)
+
+None-coalescing `if` block: /usr/lib/python3.4/unittest/runner.py:130
+        if stream is None:
+            stream = sys.stderr
+
+Safe navigation `if` block: /usr/lib/python3.4/unittest/runner.py:165
+            if startTestRun is not None:
+                startTestRun()
+
+Safe navigation `if` block: /usr/lib/python3.4/unittest/runner.py:171
+                if stopTestRun is not None:
+                    stopTestRun()
+
+None-coalescing `if` block: /usr/lib/python3.4/functools.py:323
+        if result is None:
+            # If the underlying descriptor didn't do anything, treat this
+            # like an instance method
+            result = self._make_unbound_method().__get__(obj, cls)
+
+None-coalescing `if` block: /usr/lib/python3.4/site.py:311
+    if prefixes is None:
+        prefixes = PREFIXES
+
+None-coalescing `if` block: /usr/lib/python3.4/site.py:570
+    if ENABLE_USER_SITE is None:
+        ENABLE_USER_SITE = check_enableusersite()
+
+None-coalescing `if` block: /usr/lib/python3.4/turtle.py:913
+        if outline is None:
+            outline = fill
+
+None-coalescing `if` block: /usr/lib/python3.4/turtle.py:1972
+        if extent is None:
+            extent = self._fullcircle
+
+None-coalescing `if` block: /usr/lib/python3.4/turtle.py:2815
+        if outline is None:
+            outline = self._outlinewidth
+
+None-coalescing `if` block: /usr/lib/python3.4/turtle.py:3724
+        if startx is None:
+            startx = (sw - width) / 2
+
+None-coalescing `if` block: /usr/lib/python3.4/turtle.py:3728
+        if starty is None:
+            starty = (sh - height) / 2
+
+None-coalescing `if` block: /usr/lib/python3.4/socketserver.py:282
+        if timeout is None:
+            timeout = self.timeout
+
+Safe navigation `and`: /usr/lib/python3.4/fileinput.py:100
+    if _state and _state._file:
+
+None-coalescing `if` block: /usr/lib/python3.4/shutil.py:725
+    if extra_args is None:
+        extra_args = []
+
+None-coalescing `if` block: /usr/lib/python3.4/shutil.py:766
+    if base_dir is None:
+        base_dir = os.curdir
+
+None-coalescing `if` block: /usr/lib/python3.4/shutil.py:841
+    if extra_args is None:
+        extra_args = []
+
+None-coalescing `if` block: /usr/lib/python3.4/shutil.py:938
+    if extract_dir is None:
+        extract_dir = os.getcwd()
+
+None-coalescing `if` block: /usr/lib/python3.4/shutil.py:1093
+    if path is None:
+        path = os.environ.get("PATH", os.defpath)
+
+Safe navigation `if` block: /usr/lib/python3.4/shutil.py:298
+    if ignore is not None:
+        ignored_names = ignore(src, names)
+    else:
+        ignored_names = set()
+
+Safe navigation `if` block: /usr/lib/python3.4/shutil.py:610
+    if logger is not None:
+        logger.info('Creating tar archive')
+
+None-coalescing `if` block: /usr/lib/python3.4/types.py:65
+    if kwds is None:
+        kwds = {}
+
+Safe navigation `if` block: /usr/lib/python3.4/types.py:50
+    if exec_body is not None:
+        exec_body(ns)
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/FormatParagraph.py:47
+        if limit is None:
+            # The default length limit is that defined by pep8
+            limit = idleConf.GetOption(
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/StackViewer.py:29
+        if tb is None:
+            tb = sys.last_traceback
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/idlelib/ReplaceDialog.py:36
+        first = first or text.index("insert")
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/macosxSupport.py:155
+        if end is None:
+            end = -1
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/idlelib/HyperParser.py:53
+            parser.set_lo(bod or 0)
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/EditorWindow.py:470
+        if end is None:
+            end = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/EditorWindow.py:1103
+        if keydefs is None:
+            keydefs = self.Bindings.default_keydefs
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/EditorWindow.py:1116
+        if menudefs is None:
+            menudefs = self.Bindings.menudefs
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/EditorWindow.py:1118
+        if keydefs is None:
+            keydefs = self.Bindings.default_keydefs
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/idlelib/EditorWindow.py:1358
+                y.set_lo(bod or 0)
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/run.py:34
+    if file is None:
+        file = sys.stderr
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/idlelib/configDialog.py:39
+        self.title(title or 'IDLE Preferences')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/idlelib/configDialog.py:1256
+        self.wm_title(title or 'IDLE Extensions Configuration')
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/rpc.py:73
+        if handlerclass is None:
+            handlerclass = RPCHandler
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/rpc.py:134
+        if objtable is None:
+            objtable = objecttable
+
+Safe navigation `if` block: /usr/lib/python3.4/idlelib/rpc.py:143
+        if sock is not None:
+            sock.close()
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/MultiStatusBar.py:6
+        if master is None:
+            master = Tk()
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/PyShell.py:56
+    if line is None:
+        line = linecache.getline(filename, lineno)
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/PyShell.py:72
+    if file is None:
+        file = warning_stream
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/PyShell.py:1362
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/PyShell.py:1385
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/idlelib/Debugger.py:55
+        if idb is None:
+            idb = Idb(self)
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/idlelib/TreeWidget.py:82
+        ext = ext or ".gif"
+
+None-coalescing `if` block: /usr/lib/python3.4/cmd.py:87
+        if stdin is not None:
+            self.stdin = stdin
+        else:
+            self.stdin = sys.stdin
+
+None-coalescing `if` block: /usr/lib/python3.4/cmd.py:91
+        if stdout is not None:
+            self.stdout = stdout
+        else:
+            self.stdout = sys.stdout
+
+Safe navigation `if` block: /usr/lib/python3.4/_threading_local.py:185
+            if local is not None:
+                dct = local.dicts.pop(idt)
+
+None-coalescing `if` block: /usr/lib/python3.4/pkgutil.py:298
+        if fullname is None:
+            fullname = self.fullname
+
+Safe navigation ternary: /usr/lib/python3.4/pkgutil.py:492
+    return spec.loader if spec is not None else None
+
+None-coalescing `if` block: /usr/lib/python3.4/crypt.py:28
+    if method is None:
+        method = methods[0]
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/getopt.py:165
+    opts.append(('--' + opt, optarg or ''))
+
+Safe navigation `and`: /usr/lib/python3.4/getopt.py:88
+    while args and args[0].startswith('-') and args[0] != '-':
+
+None-coalescing `if` block: /usr/lib/python3.4/mailbox.py:820
+        if from_line is None:
+            from_line = b'From MAILER-DAEMON ' + time.asctime(time.gmtime()).encode()
+
+None-coalescing `if` block: /usr/lib/python3.4/mailbox.py:1919
+        if pos is None:
+            self._pos = f.tell()
+        else:
+            self._pos = pos
+
+None-coalescing `if` block: /usr/lib/python3.4/mailbox.py:1975
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/binhex.py:151
+            if finfo is None:
+                finfo = FInfo()
+
+None-coalescing `if` block: /usr/lib/python3.4/pickle.py:373
+        if protocol is None:
+            protocol = DEFAULT_PROTOCOL
+
+None-coalescing `if` block: /usr/lib/python3.4/pickle.py:904
+        if name is None:
+            name = obj.__name__
+
+Safe navigation `if` block: /usr/lib/python3.4/pickle.py:484
+        if reduce is not None:
+            rv = reduce(obj)
+        else:
+            # Check for a class with a custom metaclass; treat as regular class
+            try:
+
+None-coalescing `if` block: /usr/lib/python3.4/sysconfig.py:169
+    if vars is None:
+        vars = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/sysconfig.py:229
+    if vars is None:
+        vars = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/sysconfig.py:438
+    if vars is None:
+        vars = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:543
+    if ctx is None: ctx = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:823
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1098
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1120
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1162
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1272
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1326
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1427
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1470
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1500
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1577
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:1972
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2315
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2510
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2539
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2541
+        if rounding is None:
+            rounding = context.rounding
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2694
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2696
+        if rounding is None:
+            rounding = context.rounding
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2706
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2708
+        if rounding is None:
+            rounding = context.rounding
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2725
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2830
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:2872
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3045
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3145
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3165
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3201
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3281
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3342
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3388
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3405
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3412
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3429
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3448
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3478
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3506
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3529
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3561
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3625
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3644
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3677
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3702
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3769
+        if context is None:
+            context = getcontext()
+
+None-coalescing `if` block: /usr/lib/python3.4/decimal.py:3911
+        if _ignored_flags is None:
+            self._ignored_flags = []
+        else:
+            self._ignored_flags = _ignored_flags
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/decimal.py:613
+                    self._int = str(int(diag or '0')).lstrip('0')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/decimal.py:688
+                    self._int = ''.join(map(str, digits or [0]))
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/decimal.py:6199
+    format_dict['fill'] = fill or ' '
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/decimal.py:6203
+    format_dict['align'] = align or '>'
+
+None-coalescing ternary: /usr/lib/python3.4/decimal.py:3904
+        self.prec = prec if prec is not None else dc.prec
+
+None-coalescing ternary: /usr/lib/python3.4/decimal.py:3905
+        self.rounding = rounding if rounding is not None else dc.rounding
+
+None-coalescing ternary: /usr/lib/python3.4/decimal.py:3906
+        self.Emin = Emin if Emin is not None else dc.Emin
+
+None-coalescing ternary: /usr/lib/python3.4/decimal.py:3907
+        self.Emax = Emax if Emax is not None else dc.Emax
+
+None-coalescing ternary: /usr/lib/python3.4/decimal.py:3908
+        self.capitals = capitals if capitals is not None else dc.capitals
+
+None-coalescing ternary: /usr/lib/python3.4/decimal.py:3909
+        self.clamp = clamp if clamp is not None else dc.clamp
+
+Safe navigation `if` block: /usr/lib/python3.4/decimal.py:791
+        if other is None:
+            other_is_nan = False
+        else:
+            other_is_nan = other._isnan()
+
+None-coalescing `if` block: /usr/lib/python3.4/inspect.py:594
+    if _filename is None:
+        _filename = getsourcefile(object) or getfile(object)
+
+Safe navigation `if` block: /usr/lib/python3.4/inspect.py:2436
+            if kwdefaults is not None:
+                default = kwdefaults.get(name, _empty)
+
+None-coalescing `if` block: /usr/lib/python3.4/filecmp.py:123
+        if hide is None:
+            self.hide = [os.curdir, os.pardir] # Names never to be shown
+        else:
+            self.hide = hide
+
+None-coalescing `if` block: /usr/lib/python3.4/filecmp.py:127
+        if ignore is None:
+            self.ignore = DEFAULT_IGNORES
+        else:
+            self.ignore = ignore
+
+None-coalescing `if` block: /usr/lib/python3.4/py_compile.py:157
+    if args is None:
+        args = sys.argv[1:]
+
+None-coalescing `if` block: /usr/lib/python3.4/formatter.py:42
+        if writer is None:
+            writer = NullWriter()
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:776
+        if year is None:
+            year = self._year
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:778
+        if month is None:
+            month = self._month
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:780
+        if day is None:
+            day = self._day
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1238
+        if hour is None:
+            hour = self.hour
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1240
+        if minute is None:
+            minute = self.minute
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1242
+        if second is None:
+            second = self.second
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1244
+        if microsecond is None:
+            microsecond = self.microsecond
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1419
+        if dst is None:
+            dst = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1463
+        if year is None:
+            year = self.year
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1465
+        if month is None:
+            month = self.month
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1467
+        if day is None:
+            day = self.day
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1469
+        if hour is None:
+            hour = self.hour
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1471
+        if minute is None:
+            minute = self.minute
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1473
+        if second is None:
+            second = self.second
+
+None-coalescing `if` block: /usr/lib/python3.4/datetime.py:1475
+        if microsecond is None:
+            microsecond = self.microsecond
+
+Safe navigation `if` block: /usr/lib/python3.4/datetime.py:1367
+        if tz is not None:
+            result = tz.fromutc(result)
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:496
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:566
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:732
+        if pos is None:
+            pos = self.tell()
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:847
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:908
+        if pos is None:
+            pos = self._pos
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:1138
+            if pos is None:
+                pos = self.raw.tell()
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:1207
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:1282
+        if pos is None:
+            pos = self.tell()
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:1288
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:1524
+        if errors is None:
+            errors = "strict"
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:1853
+        if pos is None:
+            pos = self.tell()
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:1938
+        if size is None:
+            size = -1
+
+None-coalescing `if` block: /usr/lib/python3.4/_pyio.py:1973
+        if size is None:
+            size = -1
+
+None-coalescing ternary: /usr/lib/python3.4/_pyio.py:396
+                                       if msg is None else msg)
+
+None-coalescing ternary: /usr/lib/python3.4/_pyio.py:410
+                                       if msg is None else msg)
+
+None-coalescing ternary: /usr/lib/python3.4/_pyio.py:424
+                                       if msg is None else msg)
+
+None-coalescing ternary: /usr/lib/python3.4/_pyio.py:439
+                             if msg is None else msg)
+
+None-coalescing `if` block: /usr/lib/python3.4/traceback.py:27
+    if file is None:
+        file = sys.stderr
+
+None-coalescing `if` block: /usr/lib/python3.4/traceback.py:54
+    if limit is None:
+        limit = getattr(sys, 'tracebacklimit', None)
+
+None-coalescing `if` block: /usr/lib/python3.4/traceback.py:121
+    if seen is None:
+        seen = set()
+
+None-coalescing `if` block: /usr/lib/python3.4/traceback.py:167
+    if file is None:
+        file = sys.stderr
+
+None-coalescing `if` block: /usr/lib/python3.4/traceback.py:275
+    if f is None:
+        f = sys._getframe().f_back.f_back
+
+None-coalescing `if` block: /usr/lib/python3.4/uuid.py:585
+    if node is None:
+        node = getnode()
+
+None-coalescing `if` block: /usr/lib/python3.4/calendar.py:468
+        if encoding is None:
+            encoding = sys.getdefaultencoding()
+
+None-coalescing `if` block: /usr/lib/python3.4/calendar.py:510
+        if locale is None:
+            locale = _locale.getdefaultlocale()
+
+None-coalescing `if` block: /usr/lib/python3.4/calendar.py:540
+        if locale is None:
+            locale = _locale.getdefaultlocale()
+
+None-coalescing `if` block: /usr/lib/python3.4/importlib/_bootstrap.py:535
+        if name is None:
+            name = self.name
+
+None-coalescing `if` block: /usr/lib/python3.4/importlib/_bootstrap.py:611
+    if name is not None:
+        exc_details['name'] = name
+    else:
+        # To prevent having to make all messages have a conditional name.
+        name = '<bytecode>'
+
+None-coalescing `if` block: /usr/lib/python3.4/importlib/_bootstrap.py:1114
+        if module is None:
+            # This must be done before open() is ever called as the 'io'
+            # module implicitly imports 'locale' and would otherwise
+            # trigger an infinite loop.
+            module = _new_module(spec.name)
+
+None-coalescing `if` block: /usr/lib/python3.4/importlib/_bootstrap.py:1938
+        if path is None:
+            path = sys.path
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/importlib/_bootstrap.py:1988
+        self.path = path or '.'
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/importlib/_bootstrap.py:2065
+            contents = _os.listdir(path or _os.getcwd())
+
+None-coalescing ternary: /usr/lib/python3.4/importlib/_bootstrap.py:450
+    debug = not sys.flags.optimize if debug_override is None else debug_override
+
+None-coalescing ternary: /usr/lib/python3.4/importlib/_bootstrap.py:2336
+        globals_ = globals if globals is not None else {}
+
+Safe navigation ternary: /usr/lib/python3.4/importlib/_bootstrap.py:1285
+        return spec.loader if spec is not None else None
+
+Safe navigation ternary: /usr/lib/python3.4/importlib/abc.py:60
+        return found.loader if found is not None else None
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/fancy_getopt.py:221
+        if args is None:
+            args = sys.argv[1:]
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/fancy_getopt.py:361
+        if file is None:
+            file = sys.stdout
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:313
+        if outdir is None:
+            outdir = self.output_dir
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:318
+        if macros is None:
+            macros = self.macros
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:325
+        if incdirs is None:
+            incdirs = self.include_dirs
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:333
+        if extra is None:
+            extra = []
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:372
+        if output_dir is None:
+            output_dir = self.output_dir
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:377
+        if macros is None:
+            macros = self.macros
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:384
+        if include_dirs is None:
+            include_dirs = self.include_dirs
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:420
+        if output_dir is None:
+            output_dir = self.output_dir
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:434
+        if libraries is None:
+            libraries = self.libraries
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:442
+        if library_dirs is None:
+            library_dirs = self.library_dirs
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:450
+        if runtime_library_dirs is None:
+            runtime_library_dirs = self.runtime_library_dirs
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:770
+        if includes is None:
+            includes = []
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:772
+        if include_dirs is None:
+            include_dirs = []
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:774
+        if libraries is None:
+            libraries = []
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:776
+        if library_dirs is None:
+            library_dirs = []
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:847
+        if output_dir is None:
+            output_dir = ''
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:946
+    if osname is None:
+        osname = os.name
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:948
+    if platform is None:
+        platform = sys.platform
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:1000
+    if plat is None:
+        plat = os.name
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/ccompiler.py:1004
+        if compiler is None:
+            compiler = get_default_compiler(plat)
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/versionpredicate.py:155
+    if _provision_rx is None:
+        _provision_rx = re.compile(
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/archive_util.py:220
+    if base_dir is None:
+        base_dir = os.curdir
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/msvc9compiler.py:341
+        if plat_name is None:
+            plat_name = get_platform()
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/msvc9compiler.py:430
+        if output_dir is None: output_dir = ''
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/msvc9compiler.py:465
+        compile_opts = extra_preargs or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/msvc9compiler.py:610
+            for sym in (export_symbols or []):
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/cygwinccompiler.py:254
+        if output_dir is None:
+            output_dir = ''
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/cygwinccompiler.py:180
+        extra_preargs = copy.copy(extra_preargs or [])
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/cygwinccompiler.py:181
+        libraries = copy.copy(libraries or [])
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/cygwinccompiler.py:182
+        objects = copy.copy(objects or [])
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/util.py:367
+    if direct is None:
+        direct = (__debug__ and optimize == 0)
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/util.py:498
+    if fixer_names is None:
+        fixer_names = get_fixers_from_package('lib2to3.fixes')
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/util.py:456
+                if force or newer(file, cfile):
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/msvccompiler.py:315
+        if output_dir is None: output_dir = ''
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/msvccompiler.py:350
+        compile_opts = extra_preargs or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/msvccompiler.py:495
+            for sym in (export_symbols or []):
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/dist.py:296
+        if commands is None:             # dump all command option dicts
+            commands = sorted(self.command_options.keys())
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/dist.py:384
+        if filenames is None:
+            filenames = self.find_config_files()
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/dist.py:871
+        if option_dict is None:
+            option_dict = self.get_option_dict(command_name)
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/command/bdist_rpm.py:356
+                if source_rpm is None:
+                    source_rpm = l[0]
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/bcppcompiler.py:338
+        if output_dir is None: output_dir = ''
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/bcppcompiler.py:88
+        compile_opts = extra_preargs or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/bcppcompiler.py:226
+                for sym in (export_symbols or []):
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:112
+        self.include_dirs = include_dirs or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:113
+        self.define_macros = define_macros or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:114
+        self.undef_macros = undef_macros or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:115
+        self.library_dirs = library_dirs or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:116
+        self.libraries = libraries or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:117
+        self.runtime_library_dirs = runtime_library_dirs or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:118
+        self.extra_objects = extra_objects or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:119
+        self.extra_compile_args = extra_compile_args or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:120
+        self.extra_link_args = extra_link_args or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:121
+        self.export_symbols = export_symbols or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:122
+        self.swig_opts = swig_opts or []
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/distutils/extension.py:123
+        self.depends = depends or []
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/text_file.py:128
+        if line is None:
+            line = self.current_line
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/cmd.py:153
+        if header is None:
+            header = "command options for '%s':" % self.get_command_name()
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/cmd.py:384
+        if skip_msg is None:
+            skip_msg = "skipping %s (inputs unchanged)" % outfile
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/cmd.py:394
+        if exec_msg is None:
+            exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles))
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/sysconfig.py:97
+    if prefix is None:
+        prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/sysconfig.py:292
+    if g is None:
+        g = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/sysconfig.py:330
+    if g is None:
+        g = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/distutils/spawn.py:178
+    if path is None:
+        path = os.environ['PATH']
+
+None-coalescing `if` block: /usr/lib/python3.4/shlex.py:256
+        if infile is None:
+            infile = self.infile
+
+None-coalescing `if` block: /usr/lib/python3.4/shlex.py:258
+        if lineno is None:
+            lineno = self.lineno
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/csv.py:200
+        dialect.quotechar = quotechar or '"'
+
+[Possible] None-coalescing `or`: /usr/lib/python3.4/lib2to3/refactor.py:191
+        self.explicit = explicit or []
+
+None-coalescing `if` block: /usr/lib/python3.4/lib2to3/pgen2/driver.py:33
+        if logger is None:
+            logger = logging.getLogger()
+
+None-coalescing `if` block: /usr/lib/python3.4/lib2to3/pgen2/driver.py:112
+    if logger is None:
+        logger = logging.getLogger()
+
+None-coalescing `if` block: /usr/lib/python3.4/lib2to3/pgen2/pgen.py:165
+            if startsymbol is None:
+                startsymbol = name
+
+Safe navigation `if` block: /usr/lib/python3.4/lib2to3/pgen2/pgen.py:22
+        if close_stream is not None:
+            close_stream()
+
+None-coalescing `if` block: /usr/lib/python3.4/lib2to3/pgen2/parse.py:102
+        if start is None:
+            start = self.grammar.start
+
+None-coalescing `if` block: /usr/lib/python3.4/lib2to3/fixes/fix_tuple_params.py:158
+    if d is None:
+        d = {}
+
+None-coalescing `if` block: /usr/lib/python3.4/ipaddress.py:1718
+        if ip_int is None:
+            ip_int = int(self._ip)
+
+None-coalescing `if` block: /usr/lib/python3.4/bisect.py:14
+    if hi is None:
+        hi = len(a)
+
+None-coalescing `if` block: /usr/lib/python3.4/bisect.py:37
+    if hi is None:
+        hi = len(a)
+
+None-coalescing `if` block: /usr/lib/python3.4/bisect.py:58
+    if hi is None:
+        hi = len(a)
+
+None-coalescing `if` block: /usr/lib/python3.4/bisect.py:80
+    if hi is None:
+        hi = len(a)
+
+Total None-coalescing `if` blocks: 426
+Total [possible] None-coalescing `or`: 119
+Total None-coalescing ternaries: 21
+Total Safe navigation `and`: 9
+Total Safe navigation `if` blocks: 55
+Total Safe navigation ternaries: 7
diff --git a/pep-0505/find-pep505.py b/pep-0505/find-pep505.py
new file mode 100644
--- /dev/null
+++ b/pep-0505/find-pep505.py
@@ -0,0 +1,434 @@
+'''
+Find code patterns that PEP-505 attempts to make more concise.
+
+Example usage:
+
+    $ find /usr/lib/python3.4 -name '*.py' | xargs python3 find-pep505.py
+
+'''
+
+import ast
+import sys
+
+
+class NoneCoalesceIfBlockVisitor(ast.NodeVisitor):
+    '''
+    Look for if blocks of the form:
+
+        >>> if a is None:
+        ...     a = b
+
+        >>> if a is not None:
+        ...     b
+        ... else:
+        ...     a = c
+
+        >>> def foo(self, a=None):
+        ...     if a is None:
+        ...         self.b = c
+        ...     else:
+        ...         self.b = a
+
+        >>> def foo(self, a=None):
+        ...     if a is not None:
+        ...         self.b = a
+        ...     else:
+        ...         self.b = c
+
+    ...where `a` is a name and other characters represent any abitrary
+    expression.
+
+    In the two latter forms, the search criterion is an assignment of `a`
+    to any identifier in the `a is not None` block.
+    '''
+
+    def __init__(self, file_, callback):
+        self.__file = file_
+        self.__callback = callback
+
+    def visit_If(self, if_):
+
+        if not isinstance(if_.test, ast.Compare):
+            return
+
+        op = if_.test.ops[0]
+
+        # Match `if a is None:` or `if a is not None:`, where `a` is a name.
+        if isinstance(op, (ast.Is, ast.IsNot)) and \
+           isinstance(if_.test.left, ast.Name) and \
+           isinstance(if_.test.comparators[0], ast.NameConstant) and \
+           if_.test.comparators[0].value is None:
+
+            test_name = if_.test.left.id
+        else:
+            return
+
+        # Keep track of which block handles the `a is None` condition and which
+        # handles the `a is not None` condition.
+        if isinstance(op, ast.Is):
+            none_block = if_.body
+            value_block = if_.orelse
+        elif isinstance(op, ast.IsNot):
+            none_block = if_.orelse
+            value_block = if_.body
+
+        if len(none_block) != 1:
+            return
+
+        none_stmt = none_block[0]
+
+        # If there is no `a is not None` block, handle gracefully.
+        if len(value_block) == 1:
+            value_stmt = value_block[0]
+        else:
+            value_stmt = None
+
+        # Assigning a value to `a` when it is `None`?
+        if isinstance(none_stmt, ast.Assign) and \
+           len(none_stmt.targets) == 1:
+
+            target = none_stmt.targets[0]
+
+            if isinstance(target, ast.Name):
+                if test_name == target.id:
+                    self.__callback(self.__file, if_.test.lineno,
+                                    target.lineno)
+                    return
+
+        # Assigning value of `a` to another identifier when a is not `None`?
+        if isinstance(value_stmt, ast.Assign) and \
+             isinstance(value_stmt.value, ast.Name) and \
+             test_name == value_stmt.value.id:
+
+            end_line = max(value_stmt.lineno, none_stmt.lineno)
+            self.__callback(self.__file, if_.test.lineno, end_line)
+
+
+class NoneCoalesceOrVisitor(ast.NodeVisitor):
+    '''
+    Look for expressions of the form:
+
+        >>> a or '1'
+        >>> a or []
+        >>> a or {}
+
+    ...where `a` is any name. More formally, match a plain name on the left side
+    of `or` and something that looks like a default on the right, e.g. a
+    constant or a constructor invocation.
+    '''
+
+    def __init__(self, file_, callback):
+        self.__file = file_
+        self.__callback = callback
+
+    def visit_BoolOp(self, bool_op):
+        if not isinstance(bool_op.op, ast.Or) or \
+           not isinstance(bool_op.values[0], ast.Name):
+            return
+
+        defaults = ast.Call, ast.Dict, ast.List, ast.Num, ast.Set, ast.Str
+
+        if isinstance(bool_op.values[1], defaults):
+            start_line = bool_op.values[0].lineno
+            end_line = bool_op.values[-1].lineno
+            self.__callback(self.__file, start_line, end_line)
+
+
+class NoneCoalesceTernaryVisitor(ast.NodeVisitor):
+    '''
+    Look for ternary expressions of the form:
+
+        >>> a if a is not None else b
+        >>> b if a is None else a
+
+    ...where a is an identifier and b is an abitrary expression.
+    '''
+
+    def __init__(self, file_, callback):
+        self.__file = file_
+        self.__callback = callback
+
+    def visit_IfExp(self, ifexp):
+        if isinstance(ifexp.test, ast.Compare):
+            op = ifexp.test.ops[0]
+
+            # Match `a is None` or `a is not None`, where `a` is a name.
+            if isinstance(op, (ast.Is, ast.IsNot)) and \
+               isinstance(ifexp.test.left, ast.Name) and \
+               isinstance(ifexp.test.comparators[0], ast.NameConstant) and \
+               ifexp.test.comparators[0].value is None:
+
+                test_name = ifexp.test.left.id
+            else:
+                return
+
+            if isinstance(op, ast.IsNot) and isinstance(ifexp.body, ast.Name):
+                # Match `a if a is not None else ...`.
+                result_name = ifexp.body.id
+            elif isinstance(op, ast.Is) and isinstance(ifexp.orelse, ast.Name):
+                # Match `... if a is None else a`.
+               result_name = ifexp.orelse.id
+            else:
+                return
+
+            if test_name == result_name:
+                self.__callback(self.__file, ifexp.test.lineno, None)
+
+
+class SafeNavAndVisitor(ast.NodeVisitor):
+    '''
+    Look for expressions where `and` is used to avoid attribute/index access on
+    ``None``:
+
+        >>> a and a.foo
+        >>> a and a[foo]
+        >>> a and a.foo()
+        >>> a and a.foo.bar
+
+    ...where `a` is any name and `foo`, `bar` are any attribute or keys.
+    '''
+
+    def __init__(self, file_, callback):
+        self.__file = file_
+        self.__callback = callback
+
+    def visit_BoolOp(self, bool_op):
+        if not isinstance(bool_op.op, ast.And) or \
+           not isinstance(bool_op.values[0], ast.Name):
+            return
+
+        left_name = bool_op.values[0].id
+        right = bool_op.values[1]
+
+        if isinstance(right, (ast.Attribute, ast.Call, ast.Subscript)):
+            right_name = get_name_from_node(right)
+        else:
+            return
+
+        if left_name == right_name:
+            start_line = bool_op.values[0].lineno
+            end_line = bool_op.values[-1].lineno
+            self.__callback(self.__file, start_line, end_line)
+
+
+class SafeNavIfBlockVisitor(ast.NodeVisitor):
+    '''
+    Look for blocks where `if` is used to avoid attribute/index access on
+    ``None``:
+
+        >>> if a is not None:
+        ...     a.foo
+
+        >>> if a is None:
+        ...     pass
+        ... else:
+        ...     a.foo
+
+    ...where `a` is any name. Index access and function calls are also matched.
+    '''
+
+    def __init__(self, file_, callback):
+        self.__file = file_
+        self.__callback = callback
+
+    def visit_If(self, if_):
+
+        if not isinstance(if_.test, ast.Compare):
+            return
+
+        op = if_.test.ops[0]
+
+        # Match `if a is None:` or `if a is not None:`, where `a` is a name.
+        if isinstance(op, (ast.Is, ast.IsNot)) and \
+           isinstance(if_.test.left, ast.Name) and \
+           isinstance(if_.test.comparators[0], ast.NameConstant) and \
+           if_.test.comparators[0].value is None:
+
+            test_name = if_.test.left.id
+        else:
+            return
+
+        # Keep track of which block handles the `a is None` condition and which
+        # handles the `a is not None` condition.
+        if isinstance(op, ast.Is):
+            none_block = if_.body
+            value_block = if_.orelse
+        elif isinstance(op, ast.IsNot):
+            none_block = if_.orelse
+            value_block = if_.body
+
+        if len(none_block) > 0:
+            none_lineno = none_block[0].lineno
+        else:
+            none_lineno = 0
+
+        # If there is no `a is not None` block, then it's definitely not a
+        # match.
+        if len(value_block) == 1:
+            value_stmt = value_block[0]
+        else:
+            return
+
+        # Assigning the value of `a.foo` or `a[foo]` or calling `a.foo()` in the
+        # `a is not None` block. (But don't match bare `a` -- that's already
+        # covered by the None coalesce visitors.)
+        if isinstance(value_stmt, (ast.Assign, ast.Expr)) and \
+           not isinstance(value_stmt.value, ast.Name):
+            expr_name = get_name_from_node(value_stmt.value)
+        else:
+            return
+
+        # Assigning value of `a` to another identifier when a is not `None`?
+        if test_name == expr_name:
+            end_line = max(value_stmt.lineno, none_lineno)
+            self.__callback(self.__file, if_.test.lineno, end_line)
+
+
+class SafeNavTernaryVisitor(ast.NodeVisitor):
+    '''
+    Look for ternary expressions of the form:
+
+        >>> a.foo if a is not None else b
+        >>> b if a is None else a.foo
+
+    ...where `a` is an identifier, `b` is an abitrary expression, and `foo` is
+    an attribute, index, or function invocation.
+    '''
+
+    def __init__(self, file_, callback):
+        self.__file = file_
+        self.__callback = callback
+
+    def visit_IfExp(self, ifexp):
+        if isinstance(ifexp.test, ast.Compare):
+            op = ifexp.test.ops[0]
+
+            # Match `a is None` or `a is not None`, where `a` is a name.
+            if isinstance(op, (ast.Is, ast.IsNot)) and \
+               isinstance(ifexp.test.left, ast.Name) and \
+               isinstance(ifexp.test.comparators[0], ast.NameConstant) and \
+               ifexp.test.comparators[0].value is None:
+
+                test_name = ifexp.test.left.id
+            else:
+                return
+
+            exprs = ast.Attribute, ast.Call, ast.Subscript
+
+            if isinstance(op, ast.IsNot) and isinstance(ifexp.body, exprs):
+                # Match `a.foo if a is not None else ...`.
+                result_name = get_name_from_node(ifexp.body)
+            elif isinstance(op, ast.Is) and isinstance(ifexp.orelse, exprs):
+                # Match `... if a is None else a.foo`.
+                result_name = get_name_from_node(ifexp.orelse)
+            else:
+                return
+
+            if test_name == result_name:
+                self.__callback(self.__file, ifexp.test.lineno, None)
+
+
+def count_calls_decorator(callback):
+    '''
+    Decorator for a callback that counts how many time that callback
+    was invoked.
+    '''
+
+    def invoke(*args):
+        callback(*args)
+        invoke.count += 1
+
+    invoke.count = 0
+
+    return invoke
+
+
+def get_call_count(invoke):
+    '''
+    In tandem with `count_calls_decorator`, return the number of times that
+    a callback was invoked.
+    '''
+
+    return invoke.count
+
+
+def get_name_from_node(node):
+    '''
+    Return the left-most name from an Attribute or Subscript node.
+    '''
+
+    while isinstance(node, (ast.Attribute, ast.Call, ast.Subscript)):
+        if isinstance(node, ast.Call):
+            node = node.func
+        else:
+            node = node.value
+
+    if isinstance(node, ast.Name):
+        return node.id
+    else:
+        return None
+
+
+def log(text, file_, start_line, stop_line=None):
+    '''
+    Display a match, including file name, line number, and code excerpt.
+    '''
+
+    print('{}: {}:{}'.format(text, file_, start_line))
+
+    if stop_line is None:
+        stop_line = start_line
+
+    with open(file_) as source:
+        print(''.join(source.readlines()[start_line-1:stop_line]))
+
+
+def main():
+    if len(sys.argv) < 2:
+        sys.stderr.write('Usage: python3 parse.py <files>\n')
+        sys.exit(1)
+
+    def make_callback(text):
+        return count_calls_decorator(
+            lambda file_, start, stop: log(text, file_, start, stop)
+        )
+
+    nci_callback = make_callback('None-coalescing `if` block')
+    nco_callback = make_callback('[Possible] None-coalescing `or`')
+    nct_callback = make_callback('None-coalescing ternary')
+    sna_callback = make_callback('Safe navigation `and`')
+    sni_callback = make_callback('Safe navigation `if` block')
+    snt_callback = make_callback('Safe navigation ternary')
+
+    for file_ in sys.argv[1:]:
+        with open(file_) as source:
+            tree = ast.parse(source.read(), filename=file_)
+
+            NoneCoalesceIfBlockVisitor(file_, nci_callback).visit(tree)
+            NoneCoalesceOrVisitor(file_, nco_callback).visit(tree)
+            NoneCoalesceTernaryVisitor(file_, nct_callback).visit(tree)
+            SafeNavAndVisitor(file_, sna_callback).visit(tree)
+            SafeNavIfBlockVisitor(file_, sni_callback).visit(tree)
+            SafeNavTernaryVisitor(file_, snt_callback).visit(tree)
+
+    print('Total None-coalescing `if` blocks: {}'
+          .format(get_call_count(nci_callback)))
+
+    print('Total [possible] None-coalescing `or`: {}'
+          .format(get_call_count(nco_callback)))
+
+    print('Total None-coalescing ternaries: {}'
+          .format(get_call_count(nct_callback)))
+
+    print('Total Safe navigation `and`: {}'
+          .format(get_call_count(sna_callback)))
+
+    print('Total Safe navigation `if` blocks: {}'
+          .format(get_call_count(sni_callback)))
+
+    print('Total Safe navigation ternaries: {}'
+          .format(get_call_count(snt_callback)))
+
+
+if __name__ == '__main__':
+    main()
diff --git a/pep-0505/test.py b/pep-0505/test.py
new file mode 100644
--- /dev/null
+++ b/pep-0505/test.py
@@ -0,0 +1,92 @@
+'''
+This file is used for testing find-pep505.out.
+
+nc_* and Nc* are examples of null coalescing.
+sn_* and Sn* are examples of save navigation.
+'''
+
+def nc_ifblock1(a=None):
+    if a is None:
+        a = 'foo'
+
+def nc_ifblock2(a=None):
+    if a is not None:
+        pass
+    else:
+        a = 'foo'
+
+class NcIfBlock3:
+    def __init__(self, a=None):
+        if a is None:
+            self.b = {}
+        else:
+            self.b = a
+
+class NcIfBlock4:
+    def __init__(self, a=None):
+        if a is not None:
+            self.b = a
+        else:
+            self.b = {}
+
+def nc_or1(a=None):
+    return a or 'foo'
+
+def nc_or2(a=None):
+    return a or []
+
+def nc_ternary1(a=None):
+    return a if a is not None else 'foo'
+
+def nc_ternary2(a=None):
+    return 'foo' if a is None else a
+
+def sn_and1(a=None):
+    return a and a.foo
+
+def sn_and2(a=None):
+    return a and a['foo']
+
+def sn_and3(a=None):
+    return a and a.foo()
+
+def sn_and3(a=None):
+    return a and a.foo.bar
+
+class SnIfBlock1:
+    def __init__(self, a=None):
+        if a is not None:
+            a.foo()
+
+class SnIfBlock2:
+    def __init__(self, a=None):
+        if a is None:
+            pass
+        else:
+            a.foo()
+
+class SnIfBlock3:
+    def __init__(self, a=None):
+        if a is None:
+            b = 'foo'
+        else:
+            b = a.foo
+
+class SnIfBlock4:
+    def __init__(self, a=None):
+        if a is None:
+            b = 'foo'
+        else:
+            b = a['foo']
+
+def sn_ternary1(a=None):
+    return a.foo if a is not None else None
+
+def sn_ternary2(a=None):
+    return None if a is None else a.foo
+
+def sn_ternary3(a=None):
+    return a['foo'] if a is not None else None
+
+def sn_ternary4(a=None):
+    return None if a is None else a.foo()

-- 
Repository URL: https://hg.python.org/peps


More information about the Python-checkins mailing list