PEP: 290
Title: Code Migration and Modernization
Version: $Revision$
Last-Modified: $Date$
Author: python at rcn.com (Raymond D. Hettinger)
Status: Active
Type: Informational
Created: 6-Jun-2002
Post-History: 

Abstract

    This PEP is a collection of procedures and ideas for updating
    Python applications when newer versions of Python are installed.

    The migration tips highlight possible areas of incompatibility and
    make suggestions on how to find and resolve those differences. The
    modernization procedures show how older code can be updated to
    take advantage of new language features.


Rationale

    This repository of procedures serves as a catalog or checklist of
    known migration issues and procedures for addressing those issues.

    Migration issues can arise for several reasons.  Some obsolete
    features are slowly deprecated according to the guidelines in PEP 4.
    Also, some code relies on undocumented behaviors which are subject to
    change between versions.  Some code may rely on behavior which was
    subsequently shown to be a bug and that behavior changes when the bug
    is fixed.

    Modernization options arise when new versions of Python add features
    that allow improved clarity or higher performance than previously
    available.



Guidelines for New Entries

    Developers with commit access may update the PEP directly.  Others
    can send their ideas to a developer for possible inclusion.

    While a consistent format makes the repository easier to use, feel
    free to add or subtract sections to improve clarity.

    Grep patterns may be supplied as tool to help maintainers locate
    code for possible updates.  However, fully automated search/replace
    style regular expressions are not recommended.  Instead, each code
    fragment should be evaluated individually.

    The contra-indications section is the most important part of a new
    entry.  It lists known situations where the update SHOULD NOT be
    applied.


Modernization Procedures

    Pattern:    if d.has_key(k):  --> if k in d:
    Idea:       For testing dictionary membership, use the 'in' keyword
                instead of the 'has_key()' method.
    Version:    2.2 or greater
    Benefits:   The result is shorter and more readable. The style becomes
                consistent with tests for membership in lists.  The result is
                slightly faster because has_key requires an attribute search
                and uses a relatively expensive CALL FUNCTION op code.
    Locating:   grep has_key
    Contra-indications:
        1.  if dictlike.has_key(k) ## objects like shelve do not define
            __contains__()


    Pattern:    for k in d.keys()  -->  for k in d
                for k in d.items() --> for k in d.iteritems()
                for k in d.values() -->  for k in d.itervalues()
    Idea:       Use the new iter methods for looping over dictionaries
    Version:    2.2 or greater
    Benefits:   The iter methods are faster because the do not have to create
                a new list object with a complete copy of all of the keys,
                values, or items. Selecting only keys, items, or values as
                needed saves the time for creating throwaway object references
                and, in the case of items, saves a second hash look-up of
                the key.
    Contra-indications:
        1.  def getids():  return d.keys()  ## do not change the return type
        2.  for k in dictlike.keys() ## objects like shelve do not define
            itermethods
        3.  k = d.keys(); j = k[:]   ## iterators do not support slicing,
            sorting or other operations
        4.  for k in d.keys(): del[k] ## dict iterators prohibit modifying the
            dictionary


    Pattern:    if v == None  -->  if v is None:
                if v != None  -->  if v is not None:
    Idea:       Since there is only one None object, equality can be tested
                with identity.
    Version:    All
    Benefits:   Identity tests are slightly faster than equality tests. Also,
                some object types may overload comparison to be much slower.
    Locating:   grep '== None' or grep '!= None'


    Pattern:    os.stat("foo")[stat.ST_MTIME] --> os.stat("foo").st_mtime
                    os.stat("foo")[stat.ST_MTIME] --> os.path.getmtime("foo")
    Idea:       Replace stat contants or indices with new stat methods
    Version:    2.2 or greater
    Benefits:   The methods are not order dependent and do not require an
                import of the stat module
    Locating:   grep os.stat


    Pattern:    import whrandom --> import random
    Idea:       Replace deprecated module
    Version:    2.1 or greater
    Benefits:   All random methods collected in one place
    Locating:   grep whrandom


    Pattern:    import types ; type(v, types.IntType)  -->  isinstance(v, int)
                type(s, types.StringTypes --> isinstance(s, basestring)
    Idea:       The types module will likely to be deprecated in the future.
    Version:    2.2 or greater
    Benefits:   May be slightly faster, avoid a deprecated feature.
    Locating:   grep types *.py | grep import


    Pattern:    import string ; string.method(s, ...)  -->  s.method(...)
                c in string.whitespace --> c.isspace()
    Idea:       The string module will likely to be deprecated in the future.
    Version:    2.0 or greater
    Benefits:   Slightly faster, avoid a deprecated feature.
    Locating:   grep string *.py | grep import


    Pattern:    NewError = 'NewError' --> class NewError(Exception): pass
    Idea:       String exceptions are deprecated, derive from the Exception
                base class.
    Version:    Any
    Benefits:   Unlike the obsolete string exceptions, class exceptions all
                derive from another exception or the Exception base class.
                This allows meaningful groupings of exceptions.  It also
                allows an "except Exception" clause to catch all exceptions.
    Locating:   Use PyChecker

    Pattern:    "foobar"[:3] == "foo" -> "foobar".startswith("foo")
                "foobar"[-3:] == "bar" -> "foobar".endswith("bar")
    Version:    2.0 or greater
    Benefits:   Faster because no slice has to be created.  Less risk of
                miscounting.


References

    [1] PEP 4, Deprecation of Standard Modules
        http://www.python.org/peps/pep-0004.html



Copyright

    This document has been placed in the public domain.