summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/hmac.py2
-rw-r--r--Lib/operator.py412
-rw-r--r--Lib/test/test_operator.py91
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/Setup.dist2
-rw-r--r--Modules/_operator.c (renamed from Modules/operator.c)112
-rw-r--r--PC/VS9.0/pythoncore.vcproj2
-rw-r--r--PC/config.c4
-rw-r--r--PCbuild/pythoncore.vcxproj2
-rw-r--r--PCbuild/pythoncore.vcxproj.filters2
10 files changed, 536 insertions, 96 deletions
diff --git a/Lib/hmac.py b/Lib/hmac.py
index 4297a71..6bd0de5 100644
--- a/Lib/hmac.py
+++ b/Lib/hmac.py
@@ -4,7 +4,7 @@ Implements the HMAC algorithm as described by RFC 2104.
"""
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
index 0000000..ee81266
--- /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
index b445ee0..ab58a98 100644
--- 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 @@ class Seq2(object):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
self.assertTrue(operator.is_not(a,c))
def test_attrgetter(self):
+ operator = self.module
class A:
pass
a = A()
@@ -316,6 +350,7 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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 @@ class OperatorTestCase(unittest.TestCase):
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)
+@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
index c7a5414..a65c92c 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,9 @@ Core and Builtins
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
index f45de5c..8ea2b5e 100644
--- a/Modules/Setup.dist
+++ b/Modules/Setup.dist
@@ -113,7 +113,7 @@ _sre _sre.c # Fredrik Lundh's new regular expressions
_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
index c5369a5..ab89c6d 100644
--- a/Modules/operator.c
+++ b/Modules/_operator.c
@@ -322,17 +322,15 @@ compare_digest(PyObject *self, PyObject *args)
/* 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(countOf,
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 @@ PyDoc_STRVAR(itemgetter_doc,
"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 @@ PyDoc_STRVAR(attrgetter_doc,
"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 @@ PyDoc_STRVAR(methodcaller_doc,
"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 @@ static PyTypeObject methodcaller_type = {
};
-/* 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 @@ static struct PyModuleDef operatormodule = {
};
PyMODINIT_FUNC
-PyInit_operator(void)
+PyInit__operator(void)
{
PyObject *m;
diff --git a/PC/VS9.0/pythoncore.vcproj b/PC/VS9.0/pythoncore.vcproj
index ab7feca..c5ee9a6 100644
--- 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
index 57b5073..9de876e 100644
--- a/PC/config.c
+++ b/PC/config.c
@@ -17,7 +17,7 @@ extern PyObject* PyInit_gc(void);
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 @@ struct _inittab _PyImport_Inittab[] = {
{"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
index b00991e..bba98bb 100644
--- 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
index 915fec5..736a9c8 100644
--- 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">