1. Marc Abramowitz
  2. six_msabramo_urllib_parse

Commits

Marc Abramowitz  committed 5c50dbe Merge

Merged with upstream.

Resolved documentation/index.rst conflicts by borrowing from @jaraco's fork.

Thanks, Jason!!!

  • Participants
  • Parent commits cbae96a, b2efc6a
  • Branches default

Comments (0)

Files changed (8)

File .hgtags

View file
 fc2e1d6fd89750ccabaf8c543d96694e9e23451f 1.0.0
 365531cc8a43375c60753cc9ea6dfc9afc5bedf1 1.1.0
 9f8bc06fbfec758d6930987bf60ae48c6c0bbeea 1.2.0
+2f26b0b44e7e5b2e5ed41a286c222c210d96fcba 1.3.0

File CHANGES

View file
 Development version
 -------------------
 
+- Issue #31: Add six.moves mapping for UserString.
+
+- Pull request #12: Add six.add_metaclass, a decorator for adding a metaclass to
+  a class.
+
+- Add six.moves.zip_longest and six.moves.filterfalse, which correspond
+  respectively to itertools.izip_longest and itertools.ifilterfalse on Python 2
+  and itertools.zip_longest and itertools.filterfalse on Python 3.
+
+- Issue #25: Add the unichr function, which returns a string for a Unicode
+  codepoint.
+
+- Issue #26: Add byte2int function, which complements int2byte.
+
+- Add a PY2 constant with obvious semantics.
+
+- Add helpers for indexing and iterating over bytes: iterbytes and indexbytes.
+
+- Add create_bound_method() wrapper.
+
+- Issue #23: Allow multiple base classes to be passed to with_metaclass.
+
+- Issue #24: Add six.moves.range alias. This exactly the same as the current
+  xrange alias.
+
+1.3.0
+-----
+
+- Issue #21: Add methods to access the closure and globals of a function.
+
+- In six.iter(items/keys/values/lists), passed keyword arguments through to the
+  underlying method.
+
+- Add six.iterlists().
+
+- Issue #20: Fix tests if tkinter is not available.
+
+- Issue #17: Define callable to be builtin callable when it is available again
+  in Python 3.2+.
+
+- Issue #16: Rename Python 2 exec_'s arguments, so casually calling exec_ with
+  keyword arguments will raise.
+
 - Issue #14: Put the six.moves package in sys.modules based on the name six is
   imported under.
 
 
 - Issue #3: Add six.moves mappings for filter, map, and zip.
 
-
 1.0.0
 -----
 
 
 - Expose an API for adding mappings to six.moves.
 
-
 1.0 beta 1
 ----------
 

File CONTRIBUTORS

View file
+The primary author and maintainer of six is Benjamin Peterson. He would like to
+acknowledge the following people who submitted bug reports, pull requests, and
+otherwised worked to improve six:
+
+Marc Abramowitz
+Aymeric Augustin
+Ned Batchelder
+Jason R. Coombs
+Ben Darnell
+James Mills
+Sridhar Ratnakumar
+Miroslav Shubernetskiy
+
+If you think you belong on this list, please let me know! --Benjamin

File LICENSE

View file
-Copyright (c) 2010-2012 Benjamin Peterson
+Copyright (c) 2010-2013 Benjamin Peterson
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in

File README

View file
 writing Python code that is compatible on both Python versions.  See the
 documentation for more information on what is provided.
 
-Six supports Python 2.4+.
+Six supports every Python version since 2.4.  It is contained in only one Python
+file, so it can be easily copied into your project. (The copyright and license
+notice must be retained.)
 
-Online documentation is at http://packages.python.org/six/.
+Online documentation is at http://pythonhosted.org/six/.
 
 Bugs can be reported to http://bitbucket.org/gutworth/six.  The code can also be
 found there.

File documentation/index.rst

View file
       class MyClass(with_metaclass(Meta, Base)):
           pass
 
+   Another way to set a metaclass on a class is with the :func:`add_metaclass`
+   decorator.
+
+
+.. function:: add_metaclass(metaclass)
+
+   Class decorator that replaces a normally-constructed class with a
+   metaclass-constructed one.  Unlike :func:`with_metaclass`,
+   :func:`add_metaclass` does not create an intermediate base class between the
+   class being created and its bases. Example usage: ::
+
+       @add_metaclass(Meta)
+       class MyClass(object):
+           pass
+
+   That code produces a class equivalent to ::
+
+       class MyClass(object, metaclass=Meta):
+           pass
+
+   on Python 3 or ::
+
+       class MyClass(object):
+           __metaclass__ = MyMeta
+
+   on Python 2.
+
+   Note that class decorators require Python 2.6. However, the effect of the
+   decorator can be emulated on Python 2.4 and 2.5 like so::
+
+       class MyClass(object):
+           pass
+       MyClass = add_metaclass(Meta)(MyClass)
+
 
 Binary and text data
 >>>>>>>>>>>>>>>>>>>>
 
 Supported renames:
 
-+------------------------------+-------------------------------------+---------------------------------+
-| Name                         | Python 2 name                       | Python 3 name                   |
-+==============================+=====================================+=================================+
-| ``builtins``                 | :mod:`py2:__builtin__`              | :mod:`py3:builtins`             |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``configparser``             | :mod:`py2:ConfigParser`             | :mod:`py3:configparser`         |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``copyreg``                  | :mod:`py2:copy_reg`                 | :mod:`py3:copyreg`              |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``cPickle``                  | :mod:`py2:cPickle`                  | :mod:`py3:pickle`               |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``cStringIO``                | :func:`py2:cStringIO.StringIO`      | :class:`py3:io.StringIO`        |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``email_mime_multipart``     | :mod:`py2:email.MIMEMultipart`      | :mod:`py3:email.mime.multipart` |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``email_mime_text``          | :mod:`py2:email.MIMEText`           | :mod:`py3:email.mime.text`      |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``email_mime_base``          | :mod:`py2:email.MIMEBase`           | :mod:`py3:email.mime.base`      |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``filter``                   | :func:`py2:itertools.ifilter`       | :func:`py3:filter`              |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``http_cookiejar``           | :mod:`py2:cookielib`                | :mod:`py3:http.cookiejar`       |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``http_cookies``             | :mod:`py2:Cookie`                   | :mod:`py3:http.cookies`         |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``html_entities``            | :mod:`py2:htmlentitydefs`           | :mod:`py3:html.entities`        |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``html_parser``              | :mod:`py2:HTMLParser`               | :mod:`py3:html.parser`          |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``http_client``              | :mod:`py2:httplib`                  | :mod:`py3:http.client`          |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``BaseHTTPServer``           | :mod:`py2:BaseHTTPServer`           | :mod:`py3:http.server`          |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``CGIHTTPServer``            | :mod:`py2:CGIHTTPServer`            | :mod:`py3:http.server`          |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``SimpleHTTPServer``         | :mod:`py2:SimpleHTTPServer`         | :mod:`py3:http.server`          |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``input``                    | :func:`py2:raw_input`               | :func:`py3:input`               |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``map``                      | :func:`py2:itertools.imap`          | :func:`py3:map`                 |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``queue``                    | :mod:`py2:Queue`                    | :mod:`py3:queue`                |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``range``                    | :func:`py2:xrange`                  | :func:`py3:range`               |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``reduce``                   | :func:`py2:reduce`                  | :func:`py3:functools.reduce`    |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``reload_module``            | :func:`py2:reload`                  | :func:`py3:imp.reload`          |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``reprlib``                  | :mod:`py2:repr`                     | :mod:`py3:reprlib`              |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``socketserver``             | :mod:`py2:SocketServer`             | :mod:`py3:socketserver`         |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter``                  | :mod:`py2:Tkinter`                  | :mod:`py3:tkinter`              |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_dialog``           | :mod:`py2:Dialog`                   | :mod:`py3:tkinter.dialog`       |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_filedialog``       | :mod:`py2:FileDialog`               | :mod:`py3:tkinter.FileDialog`   |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_scrolledtext``     | :mod:`py2:ScrolledText`             | :mod:`py3:tkinter.scolledtext`  |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_simpledialog``     | :mod:`py2:SimpleDialog`             | :mod:`py2:tkinter.simpledialog` |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_tix``              | :mod:`py2:Tix`                      | :mod:`py3:tkinter.tix`          |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_constants``        | :mod:`py2:Tkconstants`              | :mod:`py3:tkinter.constants`    |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_dnd``              | :mod:`py2:Tkdnd`                    | :mod:`py3:tkinter.dnd`          |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_colorchooser``     | :mod:`py2:tkColorChooser`           | :mod:`py3:tkinter.colorchooser` |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_commondialog``     | :mod:`py2:tkCommonDialog`           | :mod:`py3:tkinter.commondialog` |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_tkfiledialog``     | :mod:`py2:tkFileDialog`             | :mod:`py3:tkinter.filedialog`   |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_font``             | :mod:`py2:tkFont`                   | :mod:`py3:tkinter.font`         |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_messagebox``       | :mod:`py2:tkMessageBox`             | :mod:`py3:tkinter.messagebox`   |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``tkinter_tksimpledialog``   | :mod:`py2:tkSimpleDialog`           | :mod:`py3:tkinter.simpledialog` |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``urllib.parse``             | See :mod:`six.moves.urllib.parse`   | :mod:`py3:urllib.parse`         |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``urllib.error``             | See :mod:`six.moves.urllib.error`   | :mod:`py3:urllib.error`         |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``urllib.request``           | See :mod:`six.moves.urllib.request` | :mod:`py3:urllib.request`       |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``urllib.response``          | See :mod:`six.moves.urllib.response`| :mod:`py3:urllib.response`      |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``urllib.robotparser``       | :mod:`py2:robotparser`              | :mod:`py3:urllib.robotparser`   |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``urllib_robotparser``       | :mod:`py2:robotparser`              | :mod:`py3:urllib.robotparser`   |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``winreg``                   | :mod:`py2:_winreg`                  | :mod:`py3:winreg`               |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``xrange``                   | :func:`py2:xrange`                  | :func:`py3:range`               |
-+------------------------------+-------------------------------------+---------------------------------+
-| ``zip``                      | :func:`py2:itertools.izip`          | :func:`py3:zip`                 |
-+------------------------------+-------------------------------------+---------------------------------+
++------------------------------+-------------------------------------+-----------------------------------+
+| Name                         | Python 2 name                       | Python 3 name                     |
++==============================+=====================================+===================================+
+| ``builtins``                 | :mod:`py2:__builtin__`              | :mod:`py3:builtins`               |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``configparser``             | :mod:`py2:ConfigParser`             | :mod:`py3:configparser`           |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``copyreg``                  | :mod:`py2:copy_reg`                 | :mod:`py3:copyreg`                |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``cPickle``                  | :mod:`py2:cPickle`                  | :mod:`py3:pickle`                 |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``cStringIO``                | :func:`py2:cStringIO.StringIO`      | :class:`py3:io.StringIO`          |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``email_mime_multipart``     | :mod:`py2:email.MIMEMultipart`      | :mod:`py3:email.mime.multipart`   |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``email_mime_text``          | :mod:`py2:email.MIMEText`           | :mod:`py3:email.mime.text`        |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``email_mime_base``          | :mod:`py2:email.MIMEBase`           | :mod:`py3:email.mime.base`        |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``filter``                   | :func:`py2:itertools.ifilter`       | :func:`py3:filter`                |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``filterfalse``              | :func:`py2:itertools.ifilterfalse`  | :func:`py3:itertools.filterfalse` |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``http_cookiejar``           | :mod:`py2:cookielib`                | :mod:`py3:http.cookiejar`         |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``http_cookies``             | :mod:`py2:Cookie`                   | :mod:`py3:http.cookies`           |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``html_entities``            | :mod:`py2:htmlentitydefs`           | :mod:`py3:html.entities`          |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``html_parser``              | :mod:`py2:HTMLParser`               | :mod:`py3:html.parser`            |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``http_client``              | :mod:`py2:httplib`                  | :mod:`py3:http.client`            |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``BaseHTTPServer``           | :mod:`py2:BaseHTTPServer`           | :mod:`py3:http.server`            |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``CGIHTTPServer``            | :mod:`py2:CGIHTTPServer`            | :mod:`py3:http.server`            |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``SimpleHTTPServer``         | :mod:`py2:SimpleHTTPServer`         | :mod:`py3:http.server`            |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``input``                    | :func:`py2:raw_input`               | :func:`py3:input`                 |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``map``                      | :func:`py2:itertools.imap`          | :func:`py3:map`                   |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``queue``                    | :mod:`py2:Queue`                    | :mod:`py3:queue`                  |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``range``                    | :func:`py2:xrange`                  | :func:`py3:range`                 |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``reduce``                   | :func:`py2:reduce`                  | :func:`py3:functools.reduce`      |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``reload_module``            | :func:`py2:reload`                  | :func:`py3:imp.reload`            |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``reprlib``                  | :mod:`py2:repr`                     | :mod:`py3:reprlib`                |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``socketserver``             | :mod:`py2:SocketServer`             | :mod:`py3:socketserver`           |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter``                  | :mod:`py2:Tkinter`                  | :mod:`py3:tkinter`                |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_dialog``           | :mod:`py2:Dialog`                   | :mod:`py3:tkinter.dialog`         |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_filedialog``       | :mod:`py2:FileDialog`               | :mod:`py3:tkinter.FileDialog`     |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_scrolledtext``     | :mod:`py2:ScrolledText`             | :mod:`py3:tkinter.scolledtext`    |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_simpledialog``     | :mod:`py2:SimpleDialog`             | :mod:`py2:tkinter.simpledialog`   |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_tix``              | :mod:`py2:Tix`                      | :mod:`py3:tkinter.tix`            |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_constants``        | :mod:`py2:Tkconstants`              | :mod:`py3:tkinter.constants`      |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_dnd``              | :mod:`py2:Tkdnd`                    | :mod:`py3:tkinter.dnd`            |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_colorchooser``     | :mod:`py2:tkColorChooser`           | :mod:`py3:tkinter.colorchooser`   |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_commondialog``     | :mod:`py2:tkCommonDialog`           | :mod:`py3:tkinter.commondialog`   |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_tkfiledialog``     | :mod:`py2:tkFileDialog`             | :mod:`py3:tkinter.filedialog`     |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_font``             | :mod:`py2:tkFont`                   | :mod:`py3:tkinter.font`           |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_messagebox``       | :mod:`py2:tkMessageBox`             | :mod:`py3:tkinter.messagebox`     |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``tkinter_tksimpledialog``   | :mod:`py2:tkSimpleDialog`           | :mod:`py3:tkinter.simpledialog`   |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``urllib.parse``             | See :mod:`six.moves.urllib.parse`   | :mod:`py3:urllib.parse`           |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``urllib.error``             | See :mod:`six.moves.urllib.error`   | :mod:`py3:urllib.error`           |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``urllib.request``           | See :mod:`six.moves.urllib.request` | :mod:`py3:urllib.request`         |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``urllib.response``          | See :mod:`six.moves.urllib.response`| :mod:`py3:urllib.response`        |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``urllib.robotparser``       | :mod:`py2:robotparser`              | :mod:`py3:urllib.robotparser`     |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``urllib_robotparser``       | :mod:`py2:robotparser`              | :mod:`py3:urllib.robotparser`     |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``winreg``                   | :mod:`py2:_winreg`                  | :mod:`py3:winreg`                 |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``xrange``                   | :func:`py2:xrange`                  | :func:`py3:range`                 |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``zip``                      | :func:`py2:itertools.izip`          | :func:`py3:zip`                   |
++------------------------------+-------------------------------------+-----------------------------------+
+| ``zip_longest``              | :func:`py2:itertools.izip_longest`  | :func:`py3:itertools.zip_longest` |
++------------------------------+-------------------------------------+-----------------------------------+
 
 six.moves.urllib.parse
 <<<<<<<<<<<<<<<<<<<<<<<<<<<<

File six.py

View file
 """Utilities for writing code that runs on Python 2 and 3"""
 
+# Copyright (c) 2010-2013 Benjamin Peterson
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
 import operator
 import sys
 import types
 
 __author__ = "Benjamin Peterson <benjamin@python.org>"
-__version__ = "1.2.0"
+__version__ = "1.3.0"
 
 
-# True if we are running on Python 3.
+# Useful for very coarse version differentiation.
+PY2 = sys.version_info[0] == 2
 PY3 = sys.version_info[0] == 3
 
 if PY3:
         else:
             # 64-bit
             MAXSIZE = int((1 << 63) - 1)
-            del X
+        del X
 
 
 def _add_doc(func, doc):
 _moved_attributes = [
     MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
     MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+    MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
     MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
     MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+    MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
     MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
     MovedAttribute("reduce", "__builtin__", "functools"),
     MovedAttribute("StringIO", "StringIO", "io"),
+    MovedAttribute("UserString", "UserString", "collections"),
     MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
     MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+    MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
 
     MovedModule("builtins", "__builtin__"),
     MovedModule("configparser", "ConfigParser"),
     _meth_func = "__func__"
     _meth_self = "__self__"
 
+    _func_closure = "__closure__"
     _func_code = "__code__"
     _func_defaults = "__defaults__"
+    _func_globals = "__globals__"
 
     _iterkeys = "keys"
     _itervalues = "values"
     _iteritems = "items"
+    _iterlists = "lists"
 else:
     _meth_func = "im_func"
     _meth_self = "im_self"
 
+    _func_closure = "func_closure"
     _func_code = "func_code"
     _func_defaults = "func_defaults"
+    _func_globals = "func_globals"
 
     _iterkeys = "iterkeys"
     _itervalues = "itervalues"
     _iteritems = "iteritems"
+    _iterlists = "iterlists"
 
 
 try:
 next = advance_iterator
 
 
+try:
+    callable = callable
+except NameError:
+    def callable(obj):
+        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+
+
 if PY3:
     def get_unbound_function(unbound):
         return unbound
 
+    create_bound_method = types.MethodType
+
     Iterator = object
-
-    def callable(obj):
-        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
 else:
     def get_unbound_function(unbound):
         return unbound.im_func
 
+    def create_bound_method(func, obj):
+        return types.MethodType(func, obj, obj.__class__)
+
     class Iterator(object):
 
         def next(self):
 
 get_method_function = operator.attrgetter(_meth_func)
 get_method_self = operator.attrgetter(_meth_self)
+get_function_closure = operator.attrgetter(_func_closure)
 get_function_code = operator.attrgetter(_func_code)
 get_function_defaults = operator.attrgetter(_func_defaults)
+get_function_globals = operator.attrgetter(_func_globals)
 
 
-def iterkeys(d):
+def iterkeys(d, **kw):
     """Return an iterator over the keys of a dictionary."""
-    return iter(getattr(d, _iterkeys)())
+    return iter(getattr(d, _iterkeys)(**kw))
 
-def itervalues(d):
+def itervalues(d, **kw):
     """Return an iterator over the values of a dictionary."""
-    return iter(getattr(d, _itervalues)())
+    return iter(getattr(d, _itervalues)(**kw))
 
-def iteritems(d):
+def iteritems(d, **kw):
     """Return an iterator over the (key, value) pairs of a dictionary."""
-    return iter(getattr(d, _iteritems)())
+    return iter(getattr(d, _iteritems)(**kw))
+
+def iterlists(d, **kw):
+    """Return an iterator over the (key, [values]) pairs of a dictionary."""
+    return iter(getattr(d, _iterlists)(**kw))
 
 
 if PY3:
         return s.encode("latin-1")
     def u(s):
         return s
+    unichr = chr
     if sys.version_info[1] <= 1:
         def int2byte(i):
             return bytes((i,))
     else:
         # This is about 2x faster than the implementation above on 3.2+
         int2byte = operator.methodcaller("to_bytes", 1, "big")
+    byte2int = operator.itemgetter(0)
+    indexbytes = operator.getitem
+    iterbytes = iter
     import io
     StringIO = io.StringIO
     BytesIO = io.BytesIO
         return s
     def u(s):
         return unicode(s, "unicode_escape")
+    unichr = unichr
     int2byte = chr
+    def byte2int(bs):
+        return ord(bs[0])
+    def indexbytes(buf, i):
+        return ord(buf[i])
+    def iterbytes(buf):
+        return (ord(byte) for byte in buf)
     import StringIO
     StringIO = BytesIO = StringIO.StringIO
 _add_doc(b, """Byte literal""")
     del builtins
 
 else:
-    def exec_(code, globs=None, locs=None):
+    def exec_(_code_, _globs_=None, _locs_=None):
         """Execute code in a namespace."""
-        if globs is None:
+        if _globs_ is None:
             frame = sys._getframe(1)
-            globs = frame.f_globals
-            if locs is None:
-                locs = frame.f_locals
+            _globs_ = frame.f_globals
+            if _locs_ is None:
+                _locs_ = frame.f_locals
             del frame
-        elif locs is None:
-            locs = globs
-        exec("""exec code in globs, locs""")
+        elif _locs_ is None:
+            _locs_ = _globs_
+        exec("""exec _code_ in _globs_, _locs_""")
 
 
     exec_("""def reraise(tp, value, tb=None):
 _add_doc(reraise, """Reraise an exception.""")
 
 
-def with_metaclass(meta, base=object):
+def with_metaclass(meta, *bases):
     """Create a base class with a metaclass."""
-    return meta("NewBase", (base,), {})
+    return meta("NewBase", bases, {})
+
+def add_metaclass(metaclass):
+    """Class decorator for creating a class with a metaclass."""
+    def wrapper(cls):
+        orig_vars = cls.__dict__.copy()
+        orig_vars.pop('__dict__', None)
+        orig_vars.pop('__weakref__', None)
+        for slots_var in orig_vars.get('__slots__', ()):
+            orig_vars.pop(slots_var)
+        return metaclass(cls.__name__, cls.__bases__, orig_vars)
+    return wrapper

File test_six.py

View file
     assert "htmlparser" not in six._MovedItems.__dict__
 
 
+try:
+    import _tkinter
+except ImportError:
+    have_tkinter = False
+else:
+    have_tkinter = True
+
 @py.test.mark.parametrize("item_name",
                           [item.name for item in six._moved_attributes])
 def test_move_items(item_name):
     except ImportError:
         if item_name == "winreg" and not sys.platform.startswith("win"):
             py.test.skip("Windows only module")
-        if "_tkinter" in str(sys.exc_info()[1]):
+        if item_name.startswith("tkinter") and not have_tkinter:
             py.test.skip("requires tkinter")
         raise
 
     assert six.advance_iterator(f) == 1
 
 
+def test_filter_false():
+    from six.moves import filterfalse
+    f = filterfalse(lambda x: x % 3, range(10))
+    assert six.advance_iterator(f) == 0
+    assert six.advance_iterator(f) == 3
+    assert six.advance_iterator(f) == 6
+
 def test_map():
     from six.moves import map
     assert six.advance_iterator(map(lambda x: x + 1, range(2))) == 1
     assert six.advance_iterator(zip(range(2), range(2))) == (0, 0)
 
 
+def test_zip_longest():
+    from six.moves import zip_longest
+    it = zip_longest(range(2), range(1))
+
+    assert six.advance_iterator(it) == (0, 0)
+    assert six.advance_iterator(it) == (1, None)
+
+
 class TestCustomizedMoves:
 
     def teardown_method(self, meth):
     py.test.raises(AttributeError, six.get_method_function, hasattr)
 
 
+def test_get_function_closure():
+    def f():
+        x = 42
+        def g():
+            return x
+        return g
+    cell = six.get_function_closure(f())[0]
+    assert type(cell).__name__ == "cell"
+
+
 def test_get_function_code():
     def f():
         pass
     assert six.get_function_defaults(f) == (3, 4)
 
 
-def test_dictionary_iterators():
-    d = dict(zip(range(10), reversed(range(10))))
-    for name in "keys", "values", "items":
-        it = getattr(six, "iter" + name)(d)
+def test_get_function_globals():
+    def f():
+        pass
+    assert six.get_function_globals(f) is globals()
+
+
+def test_dictionary_iterators(monkeypatch):
+    class MyDict(dict):
+        if not six.PY3:
+            def lists(self, **kw):
+                return [1, 2, 3]
+        def iterlists(self, **kw):
+            return iter([1, 2, 3])
+    f = MyDict.iterlists
+    del MyDict.iterlists
+    setattr(MyDict, six._iterlists, f)
+    d = MyDict(zip(range(10), reversed(range(10))))
+    for name in "keys", "values", "items", "lists":
+        meth = getattr(six, "iter" + name)
+        it = meth(d)
         assert not isinstance(it, list)
         assert list(it) == list(getattr(d, name)())
         py.test.raises(StopIteration, six.advance_iterator, it)
+        record = []
+        def with_kw(*args, **kw):
+            record.append(kw["kw"])
+            return old(*args)
+        old = getattr(MyDict, getattr(six, "_iter" + name))
+        monkeypatch.setattr(MyDict, getattr(six, "_iter" + name), with_kw)
+        meth(d, kw=42)
+        assert record == [42]
+        monkeypatch.undo()
 
 
 def test_advance_iterator():
     assert not six.callable("string")
 
 
+def test_create_bound_method():
+    class X(object):
+        pass
+    def f(self):
+        return self
+    x = X()
+    b = six.create_bound_method(f, x)
+    assert isinstance(b, types.MethodType)
+    assert b() is x
+
+
 if six.PY3:
 
     def test_b():
     assert len(s) == 1
 
 
+def test_unichr():
+    assert six.u("\u1234") == six.unichr(0x1234)
+    assert type(six.u("\u1234")) is type(six.unichr(0x1234))
+
+
 def test_int2byte():
     assert six.int2byte(3) == six.b("\x03")
     py.test.raises((OverflowError, ValueError), six.int2byte, 256)
 
 
+def test_byte2int():
+    assert six.byte2int(six.b("\x03")) == 3
+    assert six.byte2int(six.b("\x03\x04")) == 3
+    py.test.raises(IndexError, six.byte2int, six.b(""))
+
+
+def test_bytesindex():
+    assert six.indexbytes(six.b("hello"), 3) == ord("l")
+
+
+def test_bytesiter():
+    it = six.iterbytes(six.b("hi"))
+    assert six.next(it) == ord("h")
+    assert six.next(it) == ord("i")
+    py.test.raises(StopIteration, six.next, it)
+
+
 def test_StringIO():
     fp = six.StringIO()
     fp.write(six.u("hello"))
         pass
     assert type(X) is Meta
     assert issubclass(X, Base)
+    class Base2(object):
+        pass
+    class X(six.with_metaclass(Meta, Base, Base2)):
+        pass
+    assert type(X) is Meta
+    assert issubclass(X, Base)
+    assert issubclass(X, Base2)
+
+
+def test_add_metaclass():
+    class Meta(type):
+        pass
+    class X:
+        "success"
+    X = six.add_metaclass(Meta)(X)
+    assert type(X) is Meta
+    assert issubclass(X, object)
+    assert X.__module__ == __name__
+    assert X.__doc__ == "success"
+    class Base(object):
+        pass
+    class X(Base):
+        pass
+    X = six.add_metaclass(Meta)(X)
+    assert type(X) is Meta
+    assert issubclass(X, Base)
+    class Base2(object):
+        pass
+    class X(Base, Base2):
+        pass
+    X = six.add_metaclass(Meta)(X)
+    assert type(X) is Meta
+    assert issubclass(X, Base)
+    assert issubclass(X, Base2)
+
+    # Test a second-generation subclass of a type.
+    class Meta1(type):
+        m1 = "m1"
+    class Meta2(Meta1):
+        m2 = "m2"
+    class Base:
+        b = "b"
+    Base = six.add_metaclass(Meta1)(Base)
+    class X(Base):
+        x = "x"
+    X = six.add_metaclass(Meta2)(X)
+    assert type(X) is Meta2
+    assert issubclass(X, Base)
+    assert type(Base) is Meta1
+    assert "__dict__" not in vars(X)
+    instance = X()
+    instance.attr = "test"
+    assert vars(instance) == {"attr": "test"}
+    assert instance.b == Base.b
+    assert instance.x == X.x
+
+    # test a class with slots
+    class MySlots(object):
+        __slots__ = ["a", "b"]
+    MySlots = six.add_metaclass(Meta1)(MySlots)
+
+    assert MySlots.__slots__ == ["a", "b"]
+    instance = MySlots()
+    instance.a = "foo"
+    py.test.raises(AttributeError, setattr, instance, "c", "baz")