[Python-checkins] cpython: Issue #16694: Add a pure Python implementation of the operator module.

antoine.pitrou python-checkins at python.org
Sat Apr 20 19:21:55 CEST 2013


http://hg.python.org/cpython/rev/97834382c6cc
changeset:   83465:97834382c6cc
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Sat Apr 20 19:21:44 2013 +0200
summary:
  Issue #16694: Add a pure Python implementation of the operator module.
Patch by Zachary Ware.

files:
  Lib/hmac.py                        |    2 +-
  Lib/operator.py                    |  412 +++++++++++++++++
  Lib/test/test_operator.py          |   91 ++-
  Misc/NEWS                          |    3 +
  Modules/Setup.dist                 |    2 +-
  Modules/operator.c                 |  112 ++--
  PC/VS9.0/pythoncore.vcproj         |    2 +-
  PC/config.c                        |    4 +-
  PCbuild/pythoncore.vcxproj         |    2 +-
  PCbuild/pythoncore.vcxproj.filters |    2 +-
  10 files changed, 536 insertions(+), 96 deletions(-)


diff --git a/Lib/hmac.py b/Lib/hmac.py
--- a/Lib/hmac.py
+++ b/Lib/hmac.py
@@ -4,7 +4,7 @@
 """
 
 import warnings as _warnings
-from operator import _compare_digest as compare_digest
+from _operator import _compare_digest as compare_digest
 
 trans_5C = bytes((x ^ 0x5C) for x in range(256))
 trans_36 = bytes((x ^ 0x36) for x in range(256))
diff --git a/Lib/operator.py b/Lib/operator.py
new file mode 100644
--- /dev/null
+++ b/Lib/operator.py
@@ -0,0 +1,412 @@
+#!/usr/bin/env python3
+"""
+Operator Interface
+
+This module exports a set of functions corresponding to the intrinsic
+operators of Python.  For example, operator.add(x, y) is equivalent
+to the expression x+y.  The function names are those used for special
+methods; variants without leading and trailing '__' are also provided
+for convenience.
+
+This is the pure Python implementation of the module.
+"""
+
+__all__ = ['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf',
+           'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand',
+           'iconcat', 'ifloordiv', 'ilshift', 'imod', 'imul', 'index',
+           'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_',
+           'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le',
+           'length_hint', 'lshift', 'lt', 'methodcaller', 'mod', 'mul', 'ne',
+           'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub',
+           'truediv', 'truth', 'xor']
+
+from builtins import abs as _abs
+
+
+# Comparison Operations *******************************************************#
+
+def lt(a, b):
+    "Same as a < b."
+    return a < b
+
+def le(a, b):
+    "Same as a <= b."
+    return a <= b
+
+def eq(a, b):
+    "Same as a == b."
+    return a == b
+
+def ne(a, b):
+    "Same as a != b."
+    return a != b
+
+def ge(a, b):
+    "Same as a >= b."
+    return a >= b
+
+def gt(a, b):
+    "Same as a > b."
+    return a > b
+
+# Logical Operations **********************************************************#
+
+def not_(a):
+    "Same as not a."
+    return not a
+
+def truth(a):
+    "Return True if a is true, False otherwise."
+    return True if a else False
+
+def is_(a, b):
+    "Same as a is b."
+    return a is b
+
+def is_not(a, b):
+    "Same as a is not b."
+    return a is not b
+
+# Mathematical/Bitwise Operations *********************************************#
+
+def abs(a):
+    "Same as abs(a)."
+    return _abs(a)
+
+def add(a, b):
+    "Same as a + b."
+    return a + b
+
+def and_(a, b):
+    "Same as a & b."
+    return a & b
+
+def floordiv(a, b):
+    "Same as a // b."
+    return a // b
+
+def index(a):
+    "Same as a.__index__()."
+    return a.__index__()
+
+def inv(a):
+    "Same as ~a."
+    return ~a
+invert = inv
+
+def lshift(a, b):
+    "Same as a << b."
+    return a << b
+
+def mod(a, b):
+    "Same as a % b."
+    return a % b
+
+def mul(a, b):
+    "Same as a * b."
+    return a * b
+
+def neg(a):
+    "Same as -a."
+    return -a
+
+def or_(a, b):
+    "Same as a | b."
+    return a | b
+
+def pos(a):
+    "Same as +a."
+    return +a
+
+def pow(a, b):
+    "Same as a ** b."
+    return a ** b
+
+def rshift(a, b):
+    "Same as a >> b."
+    return a >> b
+
+def sub(a, b):
+    "Same as a - b."
+    return a - b
+
+def truediv(a, b):
+    "Same as a / b."
+    return a / b
+
+def xor(a, b):
+    "Same as a ^ b."
+    return a ^ b
+
+# Sequence Operations *********************************************************#
+
+def concat(a, b):
+    "Same as a + b, for a and b sequences."
+    if not hasattr(a, '__getitem__'):
+        msg = "'%s' object can't be concatenated" % type(a).__name__
+        raise TypeError(msg)
+    return a + b
+
+def contains(a, b):
+    "Same as b in a (note reversed operands)."
+    return b in a
+
+def countOf(a, b):
+    "Return the number of times b occurs in a."
+    count = 0
+    for i in a:
+        if i == b:
+            count += 1
+    return count
+
+def delitem(a, b):
+    "Same as del a[b]."
+    del a[b]
+
+def getitem(a, b):
+    "Same as a[b]."
+    return a[b]
+
+def indexOf(a, b):
+    "Return the first index of b in a."
+    for i, j in enumerate(a):
+        if j == b:
+            return i
+    else:
+        raise ValueError('sequence.index(x): x not in sequence')
+
+def setitem(a, b, c):
+    "Same as a[b] = c."
+    a[b] = c
+
+def length_hint(obj, default=0):
+    """
+    Return an estimate of the number of items in obj.
+    This is useful for presizing containers when building from an iterable.
+
+    If the object supports len(), the result will be exact. Otherwise, it may
+    over- or under-estimate by an arbitrary amount. The result will be an
+    integer >= 0.
+    """
+    if not isinstance(default, int):
+        msg = ("'%s' object cannot be interpreted as an integer" %
+               type(default).__name__)
+        raise TypeError(msg)
+
+    try:
+        return len(obj)
+    except TypeError:
+        pass
+
+    try:
+        hint = type(obj).__length_hint__
+    except AttributeError:
+        return default
+
+    try:
+        val = hint(obj)
+    except TypeError:
+        return default
+    if val is NotImplemented:
+        return default
+    if not isinstance(val, int):
+        msg = ('__length_hint__ must be integer, not %s' %
+               type(val).__name__)
+        raise TypeError(msg)
+    if val < 0:
+        msg = '__length_hint__() should return >= 0'
+        raise ValueError(msg)
+    return val
+
+# Generalized Lookup Objects **************************************************#
+
+class attrgetter:
+    """
+    Return a callable object that fetches the given attribute(s) from its operand.
+    After f=attrgetter('name'), the call f(r) returns r.name.
+    After g=attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).
+    After h=attrgetter('name.first', 'name.last'), the call h(r) returns
+    (r.name.first, r.name.last).
+    """
+    def __init__(self, attr, *attrs):
+        if not attrs:
+            if not isinstance(attr, str):
+                raise TypeError('attribute name must be a string')
+            names = attr.split('.')
+            def func(obj):
+                for name in names:
+                    obj = getattr(obj, name)
+                return obj
+            self._call = func
+        else:
+            getters = tuple(map(attrgetter, (attr,) + attrs))
+            def func(obj):
+                return tuple(getter(obj) for getter in getters)
+            self._call = func
+
+    def __call__(self, obj):
+        return self._call(obj)
+
+class itemgetter:
+    """
+    Return a callable object that fetches the given item(s) from its operand.
+    After f=itemgetter(2), the call f(r) returns r[2].
+    After g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])
+    """
+    def __init__(self, item, *items):
+        if not items:
+            def func(obj):
+                return obj[item]
+            self._call = func
+        else:
+            items = (item,) + items
+            def func(obj):
+                return tuple(obj[i] for i in items)
+            self._call = func
+
+    def __call__(self, obj):
+        return self._call(obj)
+
+class methodcaller:
+    """
+    Return a callable object that calls the given method on its operand.
+    After f = methodcaller('name'), the call f(r) returns r.name().
+    After g = methodcaller('name', 'date', foo=1), the call g(r) returns
+    r.name('date', foo=1).
+    """
+
+    def __init__(*args, **kwargs):
+        if len(args) < 2:
+            msg = "methodcaller needs at least one argument, the method name"
+            raise TypeError(msg)
+        self = args[0]
+        self._name = args[1]
+        self._args = args[2:]
+        self._kwargs = kwargs
+
+    def __call__(self, obj):
+        return getattr(obj, self._name)(*self._args, **self._kwargs)
+
+# In-place Operations *********************************************************#
+
+def iadd(a, b):
+    "Same as a += b."
+    a += b
+    return a
+
+def iand(a, b):
+    "Same as a &= b."
+    a &= b
+    return a
+
+def iconcat(a, b):
+    "Same as a += b, for a and b sequences."
+    if not hasattr(a, '__getitem__'):
+        msg = "'%s' object can't be concatenated" % type(a).__name__
+        raise TypeError(msg)
+    a += b
+    return a
+
+def ifloordiv(a, b):
+    "Same as a //= b."
+    a //= b
+    return a
+
+def ilshift(a, b):
+    "Same as a <<= b."
+    a <<= b
+    return a
+
+def imod(a, b):
+    "Same as a %= b."
+    a %= b
+    return a
+
+def imul(a, b):
+    "Same as a *= b."
+    a *= b
+    return a
+
+def ior(a, b):
+    "Same as a |= b."
+    a |= b
+    return a
+
+def ipow(a, b):
+    "Same as a **= b."
+    a **=b
+    return a
+
+def irshift(a, b):
+    "Same as a >>= b."
+    a >>= b
+    return a
+
+def isub(a, b):
+    "Same as a -= b."
+    a -= b
+    return a
+
+def itruediv(a, b):
+    "Same as a /= b."
+    a /= b
+    return a
+
+def ixor(a, b):
+    "Same as a ^= b."
+    a ^= b
+    return a
+
+
+try:
+    from _operator import *
+except ImportError:
+    pass
+else:
+    from _operator import __doc__
+
+# All of these "__func__ = func" assignments have to happen after importing
+# from _operator to make sure they're set to the right function
+__lt__ = lt
+__le__ = le
+__eq__ = eq
+__ne__ = ne
+__ge__ = ge
+__gt__ = gt
+__not__ = not_
+__abs__ = abs
+__add__ = add
+__and__ = and_
+__floordiv__ = floordiv
+__index__ = index
+__inv__ = inv
+__invert__ = invert
+__lshift__ = lshift
+__mod__ = mod
+__mul__ = mul
+__neg__ = neg
+__or__ = or_
+__pos__ = pos
+__pow__ = pow
+__rshift__ = rshift
+__sub__ = sub
+__truediv__ = truediv
+__xor__ = xor
+__concat__ = concat
+__contains__ = contains
+__delitem__ = delitem
+__getitem__ = getitem
+__setitem__ = setitem
+__iadd__ = iadd
+__iand__ = iand
+__iconcat__ = iconcat
+__ifloordiv__ = ifloordiv
+__ilshift__ = ilshift
+__imod__ = imod
+__imul__ = imul
+__ior__ = ior
+__ipow__ = ipow
+__irshift__ = irshift
+__isub__ = isub
+__itruediv__ = itruediv
+__ixor__ = ixor
diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py
--- a/Lib/test/test_operator.py
+++ b/Lib/test/test_operator.py
@@ -1,8 +1,10 @@
-import operator
 import unittest
 
 from test import support
 
+py_operator = support.import_fresh_module('operator', blocked=['_operator'])
+c_operator = support.import_fresh_module('operator', fresh=['_operator'])
+
 class Seq1:
     def __init__(self, lst):
         self.lst = lst
@@ -32,8 +34,9 @@
         return other * self.lst
 
 
-class OperatorTestCase(unittest.TestCase):
+class OperatorTestCase:
     def test_lt(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.lt)
         self.assertRaises(TypeError, operator.lt, 1j, 2j)
         self.assertFalse(operator.lt(1, 0))
@@ -44,6 +47,7 @@
         self.assertTrue(operator.lt(1, 2.0))
 
     def test_le(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.le)
         self.assertRaises(TypeError, operator.le, 1j, 2j)
         self.assertFalse(operator.le(1, 0))
@@ -54,6 +58,7 @@
         self.assertTrue(operator.le(1, 2.0))
 
     def test_eq(self):
+        operator = self.module
         class C(object):
             def __eq__(self, other):
                 raise SyntaxError
@@ -67,6 +72,7 @@
         self.assertFalse(operator.eq(1, 2.0))
 
     def test_ne(self):
+        operator = self.module
         class C(object):
             def __ne__(self, other):
                 raise SyntaxError
@@ -80,6 +86,7 @@
         self.assertTrue(operator.ne(1, 2.0))
 
     def test_ge(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.ge)
         self.assertRaises(TypeError, operator.ge, 1j, 2j)
         self.assertTrue(operator.ge(1, 0))
@@ -90,6 +97,7 @@
         self.assertFalse(operator.ge(1, 2.0))
 
     def test_gt(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.gt)
         self.assertRaises(TypeError, operator.gt, 1j, 2j)
         self.assertTrue(operator.gt(1, 0))
@@ -100,22 +108,26 @@
         self.assertFalse(operator.gt(1, 2.0))
 
     def test_abs(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.abs)
         self.assertRaises(TypeError, operator.abs, None)
         self.assertEqual(operator.abs(-1), 1)
         self.assertEqual(operator.abs(1), 1)
 
     def test_add(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.add)
         self.assertRaises(TypeError, operator.add, None, None)
         self.assertTrue(operator.add(3, 4) == 7)
 
     def test_bitwise_and(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.and_)
         self.assertRaises(TypeError, operator.and_, None, None)
         self.assertTrue(operator.and_(0xf, 0xa) == 0xa)
 
     def test_concat(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.concat)
         self.assertRaises(TypeError, operator.concat, None, None)
         self.assertTrue(operator.concat('py', 'thon') == 'python')
@@ -125,12 +137,14 @@
         self.assertRaises(TypeError, operator.concat, 13, 29)
 
     def test_countOf(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.countOf)
         self.assertRaises(TypeError, operator.countOf, None, None)
         self.assertTrue(operator.countOf([1, 2, 1, 3, 1, 4], 3) == 1)
         self.assertTrue(operator.countOf([1, 2, 1, 3, 1, 4], 5) == 0)
 
     def test_delitem(self):
+        operator = self.module
         a = [4, 3, 2, 1]
         self.assertRaises(TypeError, operator.delitem, a)
         self.assertRaises(TypeError, operator.delitem, a, None)
@@ -138,33 +152,39 @@
         self.assertTrue(a == [4, 2, 1])
 
     def test_floordiv(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.floordiv, 5)
         self.assertRaises(TypeError, operator.floordiv, None, None)
         self.assertTrue(operator.floordiv(5, 2) == 2)
 
     def test_truediv(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.truediv, 5)
         self.assertRaises(TypeError, operator.truediv, None, None)
         self.assertTrue(operator.truediv(5, 2) == 2.5)
 
     def test_getitem(self):
+        operator = self.module
         a = range(10)
         self.assertRaises(TypeError, operator.getitem)
         self.assertRaises(TypeError, operator.getitem, a, None)
         self.assertTrue(operator.getitem(a, 2) == 2)
 
     def test_indexOf(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.indexOf)
         self.assertRaises(TypeError, operator.indexOf, None, None)
         self.assertTrue(operator.indexOf([4, 3, 2, 1], 3) == 1)
         self.assertRaises(ValueError, operator.indexOf, [4, 3, 2, 1], 0)
 
     def test_invert(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.invert)
         self.assertRaises(TypeError, operator.invert, None)
         self.assertEqual(operator.inv(4), -5)
 
     def test_lshift(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.lshift)
         self.assertRaises(TypeError, operator.lshift, None, 42)
         self.assertTrue(operator.lshift(5, 1) == 10)
@@ -172,16 +192,19 @@
         self.assertRaises(ValueError, operator.lshift, 2, -1)
 
     def test_mod(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.mod)
         self.assertRaises(TypeError, operator.mod, None, 42)
         self.assertTrue(operator.mod(5, 2) == 1)
 
     def test_mul(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.mul)
         self.assertRaises(TypeError, operator.mul, None, None)
         self.assertTrue(operator.mul(5, 2) == 10)
 
     def test_neg(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.neg)
         self.assertRaises(TypeError, operator.neg, None)
         self.assertEqual(operator.neg(5), -5)
@@ -190,11 +213,13 @@
         self.assertEqual(operator.neg(-0), 0)
 
     def test_bitwise_or(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.or_)
         self.assertRaises(TypeError, operator.or_, None, None)
         self.assertTrue(operator.or_(0xa, 0x5) == 0xf)
 
     def test_pos(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.pos)
         self.assertRaises(TypeError, operator.pos, None)
         self.assertEqual(operator.pos(5), 5)
@@ -203,14 +228,15 @@
         self.assertEqual(operator.pos(-0), 0)
 
     def test_pow(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.pow)
         self.assertRaises(TypeError, operator.pow, None, None)
         self.assertEqual(operator.pow(3,5), 3**5)
-        self.assertEqual(operator.__pow__(3,5), 3**5)
         self.assertRaises(TypeError, operator.pow, 1)
         self.assertRaises(TypeError, operator.pow, 1, 2, 3)
 
     def test_rshift(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.rshift)
         self.assertRaises(TypeError, operator.rshift, None, 42)
         self.assertTrue(operator.rshift(5, 1) == 2)
@@ -218,12 +244,14 @@
         self.assertRaises(ValueError, operator.rshift, 2, -1)
 
     def test_contains(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.contains)
         self.assertRaises(TypeError, operator.contains, None, None)
         self.assertTrue(operator.contains(range(4), 2))
         self.assertFalse(operator.contains(range(4), 5))
 
     def test_setitem(self):
+        operator = self.module
         a = list(range(3))
         self.assertRaises(TypeError, operator.setitem, a)
         self.assertRaises(TypeError, operator.setitem, a, None, None)
@@ -232,11 +260,13 @@
         self.assertRaises(IndexError, operator.setitem, a, 4, 2)
 
     def test_sub(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.sub)
         self.assertRaises(TypeError, operator.sub, None, None)
         self.assertTrue(operator.sub(5, 2) == 3)
 
     def test_truth(self):
+        operator = self.module
         class C(object):
             def __bool__(self):
                 raise SyntaxError
@@ -248,11 +278,13 @@
         self.assertFalse(operator.truth([]))
 
     def test_bitwise_xor(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.xor)
         self.assertRaises(TypeError, operator.xor, None, None)
         self.assertTrue(operator.xor(0xb, 0xc) == 0x7)
 
     def test_is(self):
+        operator = self.module
         a = b = 'xyzpdq'
         c = a[:3] + b[3:]
         self.assertRaises(TypeError, operator.is_)
@@ -260,6 +292,7 @@
         self.assertFalse(operator.is_(a,c))
 
     def test_is_not(self):
+        operator = self.module
         a = b = 'xyzpdq'
         c = a[:3] + b[3:]
         self.assertRaises(TypeError, operator.is_not)
@@ -267,6 +300,7 @@
         self.assertTrue(operator.is_not(a,c))
 
     def test_attrgetter(self):
+        operator = self.module
         class A:
             pass
         a = A()
@@ -316,6 +350,7 @@
         self.assertEqual(f(a), ('arthur', 'thomas', 'johnson'))
 
     def test_itemgetter(self):
+        operator = self.module
         a = 'ABCDE'
         f = operator.itemgetter(2)
         self.assertEqual(f(a), 'C')
@@ -350,12 +385,15 @@
         self.assertRaises(TypeError, operator.itemgetter(2, 'x', 5), data)
 
     def test_methodcaller(self):
+        operator = self.module
         self.assertRaises(TypeError, operator.methodcaller)
         class A:
             def foo(self, *args, **kwds):
                 return args[0] + args[1]
             def bar(self, f=42):
                 return f
+            def baz(*args, **kwds):
+                return kwds['name'], kwds['self']
         a = A()
         f = operator.methodcaller('foo')
         self.assertRaises(IndexError, f, a)
@@ -366,8 +404,11 @@
         self.assertRaises(TypeError, f, a, a)
         f = operator.methodcaller('bar', f=5)
         self.assertEqual(f(a), 5)
+        f = operator.methodcaller('baz', name='spam', self='eggs')
+        self.assertEqual(f(a), ('spam', 'eggs'))
 
     def test_inplace(self):
+        operator = self.module
         class C(object):
             def __iadd__     (self, other): return "iadd"
             def __iand__     (self, other): return "iand"
@@ -396,21 +437,9 @@
         self.assertEqual(operator.itruediv (c, 5), "itruediv")
         self.assertEqual(operator.ixor     (c, 5), "ixor")
         self.assertEqual(operator.iconcat  (c, c), "iadd")
-        self.assertEqual(operator.__iadd__     (c, 5), "iadd")
-        self.assertEqual(operator.__iand__     (c, 5), "iand")
-        self.assertEqual(operator.__ifloordiv__(c, 5), "ifloordiv")
-        self.assertEqual(operator.__ilshift__  (c, 5), "ilshift")
-        self.assertEqual(operator.__imod__     (c, 5), "imod")
-        self.assertEqual(operator.__imul__     (c, 5), "imul")
-        self.assertEqual(operator.__ior__      (c, 5), "ior")
-        self.assertEqual(operator.__ipow__     (c, 5), "ipow")
-        self.assertEqual(operator.__irshift__  (c, 5), "irshift")
-        self.assertEqual(operator.__isub__     (c, 5), "isub")
-        self.assertEqual(operator.__itruediv__ (c, 5), "itruediv")
-        self.assertEqual(operator.__ixor__     (c, 5), "ixor")
-        self.assertEqual(operator.__iconcat__  (c, c), "iadd")
 
     def test_length_hint(self):
+        operator = self.module
         class X(object):
             def __init__(self, value):
                 self.value = value
@@ -434,24 +463,22 @@
         with self.assertRaises(LookupError):
             operator.length_hint(X(LookupError))
 
+    def test_dunder_is_original(self):
+        operator = self.module
 
-def test_main(verbose=None):
-    import sys
-    test_classes = (
-        OperatorTestCase,
-    )
+        names = [name for name in dir(operator) if not name.startswith('_')]
+        for name in names:
+            orig = getattr(operator, name)
+            dunder = getattr(operator, '__' + name.strip('_') + '__', None)
+            if dunder:
+                self.assertIs(dunder, orig)
 
-    support.run_unittest(*test_classes)
+class PyOperatorTestCase(OperatorTestCase, unittest.TestCase):
+    module = py_operator
 
-    # verify reference counting
-    if verbose and hasattr(sys, "gettotalrefcount"):
-        import gc
-        counts = [None] * 5
-        for i in range(len(counts)):
-            support.run_unittest(*test_classes)
-            gc.collect()
-            counts[i] = sys.gettotalrefcount()
-        print(counts)
+ at unittest.skipUnless(c_operator, 'requires _operator')
+class COperatorTestCase(OperatorTestCase, unittest.TestCase):
+    module = c_operator
 
 if __name__ == "__main__":
-    test_main(verbose=True)
+    unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,9 @@
 Library
 -------
 
+- Issue #16694: Add a pure Python implementation of the operator module.
+  Patch by Zachary Ware.
+
 - Issue #11182: remove the unused and undocumented pydoc.Scanner class.
   Patch by Martin Morrison.
 
diff --git a/Modules/Setup.dist b/Modules/Setup.dist
--- a/Modules/Setup.dist
+++ b/Modules/Setup.dist
@@ -113,7 +113,7 @@
 _codecs _codecsmodule.c		# access to the builtin codecs and codec registry
 _weakref _weakref.c		# weak references
 _functools _functoolsmodule.c   # Tools for working with functions and callable objects
-operator operator.c	        # operator.add() and similar goodies
+_operator _operator.c	        # operator.add() and similar goodies
 _collections _collectionsmodule.c # Container types
 itertools itertoolsmodule.c    # Functions creating iterators for efficient looping 
 
diff --git a/Modules/operator.c b/Modules/_operator.c
rename from Modules/operator.c
rename to Modules/_operator.c
--- a/Modules/operator.c
+++ b/Modules/_operator.c
@@ -322,17 +322,15 @@
 /* operator methods **********************************************************/
 
 #define spam1(OP,DOC) {#OP, OP, METH_VARARGS, PyDoc_STR(DOC)},
-#define spam2(OP,ALTOP,DOC) {#OP, op_##OP, METH_VARARGS, PyDoc_STR(DOC)}, \
-                           {#ALTOP, op_##OP, METH_VARARGS, PyDoc_STR(DOC)},
+#define spam2(OP,DOC) {#OP, op_##OP, METH_VARARGS, PyDoc_STR(DOC)},
 #define spam1o(OP,DOC) {#OP, OP, METH_O, PyDoc_STR(DOC)},
-#define spam2o(OP,ALTOP,DOC) {#OP, op_##OP, METH_O, PyDoc_STR(DOC)}, \
-                           {#ALTOP, op_##OP, METH_O, PyDoc_STR(DOC)},
+#define spam2o(OP,DOC) {#OP, op_##OP, METH_O, PyDoc_STR(DOC)},
 
 static struct PyMethodDef operator_methods[] = {
 
 spam1o(truth,
  "truth(a) -- Return True if a is true, False otherwise.")
-spam2(contains,__contains__,
+spam2(contains,
  "contains(a, b) -- Same as b in a (note reversed operands).")
 spam1(indexOf,
  "indexOf(a, b) -- Return the first index of b in a.")
@@ -341,53 +339,53 @@
 
 spam1(is_, "is_(a, b) -- Same as a is b.")
 spam1(is_not, "is_not(a, b) -- Same as a is not b.")
-spam2o(index, __index__, "index(a) -- Same as a.__index__()")
-spam2(add,__add__, "add(a, b) -- Same as a + b.")
-spam2(sub,__sub__, "sub(a, b) -- Same as a - b.")
-spam2(mul,__mul__, "mul(a, b) -- Same as a * b.")
-spam2(floordiv,__floordiv__, "floordiv(a, b) -- Same as a // b.")
-spam2(truediv,__truediv__, "truediv(a, b) -- Same as a / b.")
-spam2(mod,__mod__, "mod(a, b) -- Same as a % b.")
-spam2o(neg,__neg__, "neg(a) -- Same as -a.")
-spam2o(pos,__pos__, "pos(a) -- Same as +a.")
-spam2o(abs,__abs__, "abs(a) -- Same as abs(a).")
-spam2o(inv,__inv__, "inv(a) -- Same as ~a.")
-spam2o(invert,__invert__, "invert(a) -- Same as ~a.")
-spam2(lshift,__lshift__, "lshift(a, b) -- Same as a << b.")
-spam2(rshift,__rshift__, "rshift(a, b) -- Same as a >> b.")
-spam2o(not_,__not__, "not_(a) -- Same as not a.")
-spam2(and_,__and__, "and_(a, b) -- Same as a & b.")
-spam2(xor,__xor__, "xor(a, b) -- Same as a ^ b.")
-spam2(or_,__or__, "or_(a, b) -- Same as a | b.")
-spam2(iadd,__iadd__, "a = iadd(a, b) -- Same as a += b.")
-spam2(isub,__isub__, "a = isub(a, b) -- Same as a -= b.")
-spam2(imul,__imul__, "a = imul(a, b) -- Same as a *= b.")
-spam2(ifloordiv,__ifloordiv__, "a = ifloordiv(a, b) -- Same as a //= b.")
-spam2(itruediv,__itruediv__, "a = itruediv(a, b) -- Same as a /= b")
-spam2(imod,__imod__, "a = imod(a, b) -- Same as a %= b.")
-spam2(ilshift,__ilshift__, "a = ilshift(a, b) -- Same as a <<= b.")
-spam2(irshift,__irshift__, "a = irshift(a, b) -- Same as a >>= b.")
-spam2(iand,__iand__, "a = iand(a, b) -- Same as a &= b.")
-spam2(ixor,__ixor__, "a = ixor(a, b) -- Same as a ^= b.")
-spam2(ior,__ior__, "a = ior(a, b) -- Same as a |= b.")
-spam2(concat,__concat__,
+spam2o(index, "index(a) -- Same as a.__index__()")
+spam2(add, "add(a, b) -- Same as a + b.")
+spam2(sub, "sub(a, b) -- Same as a - b.")
+spam2(mul, "mul(a, b) -- Same as a * b.")
+spam2(floordiv, "floordiv(a, b) -- Same as a // b.")
+spam2(truediv, "truediv(a, b) -- Same as a / b.")
+spam2(mod, "mod(a, b) -- Same as a % b.")
+spam2o(neg, "neg(a) -- Same as -a.")
+spam2o(pos, "pos(a) -- Same as +a.")
+spam2o(abs, "abs(a) -- Same as abs(a).")
+spam2o(inv, "inv(a) -- Same as ~a.")
+spam2o(invert, "invert(a) -- Same as ~a.")
+spam2(lshift, "lshift(a, b) -- Same as a << b.")
+spam2(rshift, "rshift(a, b) -- Same as a >> b.")
+spam2o(not_, "not_(a) -- Same as not a.")
+spam2(and_, "and_(a, b) -- Same as a & b.")
+spam2(xor, "xor(a, b) -- Same as a ^ b.")
+spam2(or_, "or_(a, b) -- Same as a | b.")
+spam2(iadd, "a = iadd(a, b) -- Same as a += b.")
+spam2(isub, "a = isub(a, b) -- Same as a -= b.")
+spam2(imul, "a = imul(a, b) -- Same as a *= b.")
+spam2(ifloordiv, "a = ifloordiv(a, b) -- Same as a //= b.")
+spam2(itruediv, "a = itruediv(a, b) -- Same as a /= b")
+spam2(imod, "a = imod(a, b) -- Same as a %= b.")
+spam2(ilshift, "a = ilshift(a, b) -- Same as a <<= b.")
+spam2(irshift, "a = irshift(a, b) -- Same as a >>= b.")
+spam2(iand, "a = iand(a, b) -- Same as a &= b.")
+spam2(ixor, "a = ixor(a, b) -- Same as a ^= b.")
+spam2(ior, "a = ior(a, b) -- Same as a |= b.")
+spam2(concat,
  "concat(a, b) -- Same as a + b, for a and b sequences.")
-spam2(iconcat,__iconcat__,
+spam2(iconcat,
  "a = iconcat(a, b) -- Same as a += b, for a and b sequences.")
-spam2(getitem,__getitem__,
+spam2(getitem,
  "getitem(a, b) -- Same as a[b].")
-spam2(setitem,__setitem__,
+spam2(setitem,
  "setitem(a, b, c) -- Same as a[b] = c.")
-spam2(delitem,__delitem__,
+spam2(delitem,
  "delitem(a, b) -- Same as del a[b].")
-spam2(pow,__pow__, "pow(a, b) -- Same as a ** b.")
-spam2(ipow,__ipow__, "a = ipow(a, b) -- Same as a **= b.")
-spam2(lt,__lt__, "lt(a, b) -- Same as a<b.")
-spam2(le,__le__, "le(a, b) -- Same as a<=b.")
-spam2(eq,__eq__, "eq(a, b) -- Same as a==b.")
-spam2(ne,__ne__, "ne(a, b) -- Same as a!=b.")
-spam2(gt,__gt__, "gt(a, b) -- Same as a>b.")
-spam2(ge,__ge__, "ge(a, b) -- Same as a>=b.")
+spam2(pow, "pow(a, b) -- Same as a ** b.")
+spam2(ipow, "a = ipow(a, b) -- Same as a **= b.")
+spam2(lt, "lt(a, b) -- Same as a<b.")
+spam2(le, "le(a, b) -- Same as a<=b.")
+spam2(eq, "eq(a, b) -- Same as a==b.")
+spam2(ne, "ne(a, b) -- Same as a!=b.")
+spam2(gt, "gt(a, b) -- Same as a>b.")
+spam2(ge, "ge(a, b) -- Same as a>=b.")
 
     {"_compare_digest", (PyCFunction)compare_digest, METH_VARARGS,
      compare_digest__doc__},
@@ -487,8 +485,8 @@
 "itemgetter(item, ...) --> itemgetter object\n\
 \n\
 Return a callable object that fetches the given item(s) from its operand.\n\
-After, f=itemgetter(2), the call f(r) returns r[2].\n\
-After, g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])");
+After f=itemgetter(2), the call f(r) returns r[2].\n\
+After g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])");
 
 static PyTypeObject itemgetter_type = {
     PyVarObject_HEAD_INIT(NULL, 0)
@@ -739,9 +737,9 @@
 "attrgetter(attr, ...) --> attrgetter object\n\
 \n\
 Return a callable object that fetches the given attribute(s) from its operand.\n\
-After, f=attrgetter('name'), the call f(r) returns r.name.\n\
-After, g=attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).\n\
-After, h=attrgetter('name.first', 'name.last'), the call h(r) returns\n\
+After f=attrgetter('name'), the call f(r) returns r.name.\n\
+After g=attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).\n\
+After h=attrgetter('name.first', 'name.last'), the call h(r) returns\n\
 (r.name.first, r.name.last).");
 
 static PyTypeObject attrgetter_type = {
@@ -871,8 +869,8 @@
 "methodcaller(name, ...) --> methodcaller object\n\
 \n\
 Return a callable object that calls the given method on its operand.\n\
-After, f = methodcaller('name'), the call f(r) returns r.name().\n\
-After, g = methodcaller('name', 'date', foo=1), the call g(r) returns\n\
+After f = methodcaller('name'), the call f(r) returns r.name().\n\
+After g = methodcaller('name', 'date', foo=1), the call g(r) returns\n\
 r.name('date', foo=1).");
 
 static PyTypeObject methodcaller_type = {
@@ -919,12 +917,12 @@
 };
 
 
-/* Initialization function for the module (*must* be called PyInit_operator) */
+/* Initialization function for the module (*must* be called PyInit__operator) */
 
 
 static struct PyModuleDef operatormodule = {
     PyModuleDef_HEAD_INIT,
-    "operator",
+    "_operator",
     operator_doc,
     -1,
     operator_methods,
@@ -935,7 +933,7 @@
 };
 
 PyMODINIT_FUNC
-PyInit_operator(void)
+PyInit__operator(void)
 {
     PyObject *m;
 
diff --git a/PC/VS9.0/pythoncore.vcproj b/PC/VS9.0/pythoncore.vcproj
--- a/PC/VS9.0/pythoncore.vcproj
+++ b/PC/VS9.0/pythoncore.vcproj
@@ -1119,7 +1119,7 @@
 				>
 			</File>
 			<File
-				RelativePath="..\..\Modules\operator.c"
+				RelativePath="..\..\Modules\_operator.c"
 				>
 			</File>
 			<File
diff --git a/PC/config.c b/PC/config.c
--- a/PC/config.c
+++ b/PC/config.c
@@ -17,7 +17,7 @@
 extern PyObject* PyInit_math(void);
 extern PyObject* PyInit__md5(void);
 extern PyObject* PyInit_nt(void);
-extern PyObject* PyInit_operator(void);
+extern PyObject* PyInit__operator(void);
 extern PyObject* PyInit_signal(void);
 extern PyObject* PyInit__sha1(void);
 extern PyObject* PyInit__sha256(void);
@@ -87,7 +87,7 @@
     {"gc", PyInit_gc},
     {"math", PyInit_math},
     {"nt", PyInit_nt}, /* Use the NT os functions, not posix */
-    {"operator", PyInit_operator},
+    {"_operator", PyInit__operator},
     {"signal", PyInit_signal},
     {"_md5", PyInit__md5},
     {"_sha1", PyInit__sha1},
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -517,7 +517,7 @@
     <ClCompile Include="..\Modules\mathmodule.c" />
     <ClCompile Include="..\Modules\md5module.c" />
     <ClCompile Include="..\Modules\mmapmodule.c" />
-    <ClCompile Include="..\Modules\operator.c" />
+    <ClCompile Include="..\Modules\_operator.c" />
     <ClCompile Include="..\Modules\parsermodule.c" />
     <ClCompile Include="..\Modules\posixmodule.c" />
     <ClCompile Include="..\Modules\rotatingtree.c" />
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -501,7 +501,7 @@
     <ClCompile Include="..\Modules\mmapmodule.c">
       <Filter>Modules</Filter>
     </ClCompile>
-    <ClCompile Include="..\Modules\operator.c">
+    <ClCompile Include="..\Modules\_operator.c">
       <Filter>Modules</Filter>
     </ClCompile>
     <ClCompile Include="..\Modules\parsermodule.c">

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list