* Migrate set() and frozenset() from the sandbox.

* Install the unittests, docs, newsitem, include file, and makefile update.
* Exercise the new functions whereever sets.py was being used.

Includes the docs for libfuncs.tex.  Separate docs for the types are
forthcoming.
This commit is contained in:
Raymond Hettinger 2003-11-16 16:17:49 +00:00
parent d456849f19
commit a690a9967e
21 changed files with 2338 additions and 40 deletions

View file

@ -477,6 +477,17 @@ class C:
and is known to vary.} and is known to vary.}
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{frozenset}{\optional{iterable}}
Return a frozenset object whose elements are taken from \var{iterable}.
Frozensets are sets that have no update methods but can be hashed and
used as members of other sets or as dictionary keys. The elements of
a frozenset must be immutable themselves. To represent sets of sets,
the inner sets should also be \class{frozenset} objects. If
\var{iterable} is not specified, returns a new empty set,
\code{frozenset([])}.
\versionadded{2.4}
\end{funcdesc}
\begin{funcdesc}{getattr}{object, name\optional{, default}} \begin{funcdesc}{getattr}{object, name\optional{, default}}
Return the value of the named attributed of \var{object}. \var{name} Return the value of the named attributed of \var{object}. \var{name}
must be a string. If the string is the name of one of the object's must be a string. If the string is the name of one of the object's
@ -897,6 +908,14 @@ class C(object):
\code{round(0.5)} is \code{1.0} and \code{round(-0.5)} is \code{-1.0}). \code{round(0.5)} is \code{1.0} and \code{round(-0.5)} is \code{-1.0}).
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{set}{\optional{iterable}}
Return a set whose elements are taken from \var{iterable}. The elements
must be immutable. To represent sets of sets, the inner sets should
be \class{frozenset} objects. If \var{iterable} is not specified,
returns a new empty set, \code{set([])}.
\versionadded{2.4}
\end{funcdesc}
\begin{funcdesc}{setattr}{object, name, value} \begin{funcdesc}{setattr}{object, name, value}
This is the counterpart of \function{getattr()}. The arguments are an This is the counterpart of \function{getattr()}. The arguments are an
object, a string and an arbitrary value. The string may name an object, a string and an arbitrary value. The string may name an

View file

@ -86,6 +86,7 @@
#include "listobject.h" #include "listobject.h"
#include "dictobject.h" #include "dictobject.h"
#include "enumobject.h" #include "enumobject.h"
#include "setobject.h"
#include "methodobject.h" #include "methodobject.h"
#include "moduleobject.h" #include "moduleobject.h"
#include "funcobject.h" #include "funcobject.h"

26
Include/setobject.h Normal file
View file

@ -0,0 +1,26 @@
/* Set object interface */
#ifndef Py_SETOBJECT_H
#define Py_SETOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*
This data structure is shared by set and frozenset objects.
*/
typedef struct {
PyObject_HEAD
PyObject *data;
long hash; /* only used by frozenset objects */
} PySetObject;
PyAPI_DATA(PyTypeObject) PySet_Type;
PyAPI_DATA(PyTypeObject) PyFrozenSet_Type;
#ifdef __cplusplus
}
#endif
#endif /* !Py_SETOBJECT_H */

View file

@ -16,7 +16,6 @@ import calendar
from re import compile as re_compile from re import compile as re_compile
from re import IGNORECASE from re import IGNORECASE
from datetime import date as datetime_date from datetime import date as datetime_date
from sets import ImmutableSet as sets_ImmutableSet
try: try:
from thread import allocate_lock as _thread_allocate_lock from thread import allocate_lock as _thread_allocate_lock
except: except:
@ -165,11 +164,11 @@ class LocaleTime(object):
time.tzset() time.tzset()
except AttributeError: except AttributeError:
pass pass
no_saving = sets_ImmutableSet(["utc", "gmt", time.tzname[0].lower()]) no_saving = frozenset(["utc", "gmt", time.tzname[0].lower()])
if time.daylight: if time.daylight:
has_saving = sets_ImmutableSet([time.tzname[1].lower()]) has_saving = frozenset([time.tzname[1].lower()])
else: else:
has_saving = sets_ImmutableSet() has_saving = frozenset()
self.timezone = (no_saving, has_saving) self.timezone = (no_saving, has_saving)

View file

@ -75,7 +75,6 @@ import traceback
import random import random
import cStringIO import cStringIO
import warnings import warnings
from sets import Set
# I see no other way to suppress these warnings; # I see no other way to suppress these warnings;
# putting them in test_grammar.py has no effect: # putting them in test_grammar.py has no effect:
@ -306,7 +305,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0,
e = _ExpectedSkips() e = _ExpectedSkips()
plat = sys.platform plat = sys.platform
if e.isvalid(): if e.isvalid():
surprise = Set(skipped) - e.getexpected() - Set(resource_denieds) surprise = set(skipped) - e.getexpected() - set(resource_denieds)
if surprise: if surprise:
print count(len(surprise), "skip"), \ print count(len(surprise), "skip"), \
"unexpected on", plat + ":" "unexpected on", plat + ":"
@ -948,7 +947,7 @@ class _ExpectedSkips:
self.valid = False self.valid = False
if sys.platform in _expectations: if sys.platform in _expectations:
s = _expectations[sys.platform] s = _expectations[sys.platform]
self.expected = Set(s.split()) self.expected = set(s.split())
if not os.path.supports_unicode_filenames: if not os.path.supports_unicode_filenames:
self.expected.add('test_pep277') self.expected.add('test_pep277')

View file

@ -2,7 +2,6 @@ import unittest
from test import test_support from test import test_support
from test.test_support import verify, verbose from test.test_support import verify, verbose
from sets import Set
import sys import sys
import warnings import warnings
@ -43,8 +42,8 @@ class AllTest(unittest.TestCase):
exec "from %s import *" % modname in names exec "from %s import *" % modname in names
if names.has_key("__builtins__"): if names.has_key("__builtins__"):
del names["__builtins__"] del names["__builtins__"]
keys = Set(names) keys = set(names)
all = Set(sys.modules[modname].__all__) all = set(sys.modules[modname].__all__)
verify(keys==all, "%s != %s" % (keys, all)) verify(keys==all, "%s != %s" % (keys, all))
def test_all(self): def test_all(self):

View file

@ -2,7 +2,6 @@
import test.test_support, unittest import test.test_support, unittest
from test.test_support import fcmp, have_unicode, TESTFN, unlink from test.test_support import fcmp, have_unicode, TESTFN, unlink
from sets import Set
import sys, warnings, cStringIO import sys, warnings, cStringIO
warnings.filterwarnings("ignore", "hex../oct.. of negative int", warnings.filterwarnings("ignore", "hex../oct.. of negative int",
@ -1104,9 +1103,9 @@ class BuiltinTest(unittest.TestCase):
get_vars_f2 = staticmethod(get_vars_f2) get_vars_f2 = staticmethod(get_vars_f2)
def test_vars(self): def test_vars(self):
self.assertEqual(Set(vars()), Set(dir())) self.assertEqual(set(vars()), set(dir()))
import sys import sys
self.assertEqual(Set(vars(sys)), Set(dir(sys))) self.assertEqual(set(vars(sys)), set(dir(sys)))
self.assertEqual(self.get_vars_f0(), {}) self.assertEqual(self.get_vars_f0(), {})
self.assertEqual(self.get_vars_f2(), {'a': 1, 'b': 2}) self.assertEqual(self.get_vars_f2(), {'a': 1, 'b': 2})
self.assertRaises(TypeError, vars, 42, 42) self.assertRaises(TypeError, vars, 42, 42)

View file

@ -1,5 +1,4 @@
import unittest import unittest
from sets import Set
from test import test_support from test import test_support
@ -105,8 +104,8 @@ class EnumerateTestCase(unittest.TestCase):
def test_tuple_reuse(self): def test_tuple_reuse(self):
# Tests an implementation detail where tuple is reused # Tests an implementation detail where tuple is reused
# whenever nothing else holds a reference to it # whenever nothing else holds a reference to it
self.assertEqual(len(Set(map(id, list(enumerate(self.seq))))), len(self.seq)) self.assertEqual(len(set(map(id, list(enumerate(self.seq))))), len(self.seq))
self.assertEqual(len(Set(map(id, enumerate(self.seq)))), min(1,len(self.seq))) self.assertEqual(len(set(map(id, enumerate(self.seq)))), min(1,len(self.seq)))
class MyEnum(enumerate): class MyEnum(enumerate):
pass pass

View file

@ -2,7 +2,6 @@ import unittest
from test.test_support import run_unittest, TESTFN from test.test_support import run_unittest, TESTFN
import glob import glob
import os import os
from sets import Set
def mkdirs(fname): def mkdirs(fname):
if os.path.exists(fname) or fname == '': if os.path.exists(fname) or fname == '':
@ -62,7 +61,7 @@ class GlobTests(unittest.TestCase):
return glob.glob(p) return glob.glob(p)
def assertSequencesEqual_noorder(self, l1, l2): def assertSequencesEqual_noorder(self, l1, l2):
self.assertEqual(Set(l1), Set(l2)) self.assertEqual(set(l1), set(l2))
def test_glob_literal(self): def test_glob_literal(self):
eq = self.assertSequencesEqual_noorder eq = self.assertSequencesEqual_noorder

View file

@ -7,7 +7,6 @@ import unittest, sys
from types import ClassType, FunctionType, MethodType from types import ClassType, FunctionType, MethodType
import pyclbr import pyclbr
from unittest import TestCase from unittest import TestCase
from sets import Set
# This next line triggers an error on old versions of pyclbr. # This next line triggers an error on old versions of pyclbr.
@ -24,7 +23,7 @@ class PyclbrTest(TestCase):
def assertListEq(self, l1, l2, ignore): def assertListEq(self, l1, l2, ignore):
''' succeed iff {l1} - {ignore} == {l2} - {ignore} ''' ''' succeed iff {l1} - {ignore} == {l2} - {ignore} '''
missing = (Set(l1) ^ Set(l2)) - Set(ignore) missing = (set(l1) ^ set(l2)) - set(ignore)
if missing: if missing:
print >>sys.stderr, "l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore) print >>sys.stderr, "l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore)
self.fail("%r missing" % missing.pop()) self.fail("%r missing" % missing.pop())

View file

@ -6,7 +6,6 @@ import time
import pickle import pickle
import warnings import warnings
from math import log, exp, sqrt, pi from math import log, exp, sqrt, pi
from sets import Set
from test import test_support from test import test_support
class TestBasicOps(unittest.TestCase): class TestBasicOps(unittest.TestCase):
@ -64,9 +63,9 @@ class TestBasicOps(unittest.TestCase):
for k in xrange(N+1): for k in xrange(N+1):
s = self.gen.sample(population, k) s = self.gen.sample(population, k)
self.assertEqual(len(s), k) self.assertEqual(len(s), k)
uniq = Set(s) uniq = set(s)
self.assertEqual(len(uniq), k) self.assertEqual(len(uniq), k)
self.failUnless(uniq <= Set(population)) self.failUnless(uniq <= set(population))
self.assertEqual(self.gen.sample([], 0), []) # test edge case N==k==0 self.assertEqual(self.gen.sample([], 0), []) # test edge case N==k==0
def test_sample_distribution(self): def test_sample_distribution(self):
@ -89,8 +88,7 @@ class TestBasicOps(unittest.TestCase):
def test_sample_inputs(self): def test_sample_inputs(self):
# SF bug #801342 -- population can be any iterable defining __len__() # SF bug #801342 -- population can be any iterable defining __len__()
from sets import Set self.gen.sample(set(range(20)), 2)
self.gen.sample(Set(range(20)), 2)
self.gen.sample(range(20), 2) self.gen.sample(range(20), 2)
self.gen.sample(xrange(20), 2) self.gen.sample(xrange(20), 2)
self.gen.sample(dict.fromkeys('abcdefghijklmnopqrst'), 2) self.gen.sample(dict.fromkeys('abcdefghijklmnopqrst'), 2)
@ -256,8 +254,8 @@ class MersenneTwister_TestBasicOps(TestBasicOps):
def test_rangelimits(self): def test_rangelimits(self):
for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]: for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]:
self.assertEqual(Set(range(start,stop)), self.assertEqual(set(range(start,stop)),
Set([self.gen.randrange(start,stop) for i in xrange(100)])) set([self.gen.randrange(start,stop) for i in xrange(100)]))
def test_genrandbits(self): def test_genrandbits(self):
# Verify cross-platform repeatability # Verify cross-platform repeatability
@ -364,7 +362,7 @@ class TestModule(unittest.TestCase):
def test__all__(self): def test__all__(self):
# tests validity but not completeness of the __all__ list # tests validity but not completeness of the __all__ list
self.failUnless(Set(random.__all__) <= Set(dir(random))) self.failUnless(set(random.__all__) <= set(dir(random)))
def test_main(verbose=None): def test_main(verbose=None):
testclasses = (WichmannHill_TestBasicOps, testclasses = (WichmannHill_TestBasicOps,

1183
Lib/test/test_set.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
from test.test_support import verbose from test.test_support import verbose
import random import random
from UserList import UserList from UserList import UserList
from sets import Set
nerrors = 0 nerrors = 0
@ -230,7 +229,7 @@ class TestSorted(unittest.TestCase):
self.assertEqual(list.sorted(s), list.sorted(T(s))) self.assertEqual(list.sorted(s), list.sorted(T(s)))
s = ''.join(dict.fromkeys(s).keys()) # unique letters only s = ''.join(dict.fromkeys(s).keys()) # unique letters only
for T in [unicode, Set, list, tuple, dict.fromkeys]: for T in [unicode, set, frozenset, list, tuple, dict.fromkeys]:
self.assertEqual(list.sorted(s), list.sorted(T(s))) self.assertEqual(list.sorted(s), list.sorted(T(s)))
def test_baddecorator(self): def test_baddecorator(self):

View file

@ -4,7 +4,6 @@ import unittest
import time import time
import locale import locale
import re import re
import sets
import sys import sys
from test import test_support from test import test_support
@ -167,7 +166,7 @@ class TimeRETests(unittest.TestCase):
# Make sure when tuple or something has no values no regex is generated. # Make sure when tuple or something has no values no regex is generated.
# Fixes bug #661354 # Fixes bug #661354
test_locale = _strptime.LocaleTime() test_locale = _strptime.LocaleTime()
test_locale.timezone = (sets.ImmutableSet(), sets.ImmutableSet()) test_locale.timezone = (frozenset(), frozenset())
self.failUnless(_strptime.TimeRE(test_locale).pattern("%Z") == '', self.failUnless(_strptime.TimeRE(test_locale).pattern("%Z") == '',
"with timezone == ('',''), TimeRE().pattern('%Z') != ''") "with timezone == ('',''), TimeRE().pattern('%Z') != ''")

View file

@ -1,8 +1,6 @@
# Check every path through every method of UserDict # Check every path through every method of UserDict
import test.test_support, unittest import test.test_support, unittest
from sets import Set
import UserDict import UserDict
class TestMappingProtocol(unittest.TestCase): class TestMappingProtocol(unittest.TestCase):
@ -69,7 +67,7 @@ class TestMappingProtocol(unittest.TestCase):
self.assert_(hasattr(iter, 'next')) self.assert_(hasattr(iter, 'next'))
self.assert_(hasattr(iter, '__iter__')) self.assert_(hasattr(iter, '__iter__'))
x = list(iter) x = list(iter)
self.assert_(Set(x)==Set(lst)==Set(ref)) self.assert_(set(x)==set(lst)==set(ref))
check_iterandlist(d.iterkeys(), d.keys(), self.reference.keys()) check_iterandlist(d.iterkeys(), d.keys(), self.reference.keys())
check_iterandlist(iter(d), d.keys(), self.reference.keys()) check_iterandlist(iter(d), d.keys(), self.reference.keys())
check_iterandlist(d.itervalues(), d.values(), self.reference.values()) check_iterandlist(d.itervalues(), d.values(), self.reference.values())
@ -242,7 +240,7 @@ class UserDictTest(TestMappingProtocol):
for k in u2: for k in u2:
ikeys.append(k) ikeys.append(k)
keys = u2.keys() keys = u2.keys()
self.assertEqual(Set(ikeys), Set(keys)) self.assertEqual(set(ikeys), set(keys))
# Test setdefault # Test setdefault
t = UserDict.UserDict() t = UserDict.UserDict()

View file

@ -4,7 +4,6 @@ import UserList
import weakref import weakref
from test import test_support from test import test_support
from sets import Set
class C: class C:
@ -392,7 +391,7 @@ class MappingTestCase(TestBase):
"wrong object returned by weak dict!") "wrong object returned by weak dict!")
items1 = dict.items() items1 = dict.items()
items2 = dict.copy().items() items2 = dict.copy().items()
self.assert_(Set(items1) == Set(items2), self.assert_(set(items1) == set(items2),
"cloning of weak-keyed dictionary did not work!") "cloning of weak-keyed dictionary did not work!")
del items1, items2 del items1, items2
self.assert_(len(dict) == self.COUNT) self.assert_(len(dict) == self.COUNT)

View file

@ -275,6 +275,7 @@ OBJECT_OBJS= \
Objects/object.o \ Objects/object.o \
Objects/obmalloc.o \ Objects/obmalloc.o \
Objects/rangeobject.o \ Objects/rangeobject.o \
Objects/setobject.o \
Objects/sliceobject.o \ Objects/sliceobject.o \
Objects/stringobject.o \ Objects/stringobject.o \
Objects/structseq.o \ Objects/structseq.o \
@ -500,6 +501,7 @@ PYTHON_HEADERS= \
Include/pystate.h \ Include/pystate.h \
Include/pythonrun.h \ Include/pythonrun.h \
Include/rangeobject.h \ Include/rangeobject.h \
Include/setobject.h \
Include/sliceobject.h \ Include/sliceobject.h \
Include/stringobject.h \ Include/stringobject.h \
Include/structseq.h \ Include/structseq.h \

View file

@ -12,6 +12,8 @@ What's New in Python 2.4 alpha 1?
Core and builtins Core and builtins
----------------- -----------------
- Added two builtin types, set() and frozenset().
- Critical bugfix, for SF bug 840829: if cyclic garbage collection - Critical bugfix, for SF bug 840829: if cyclic garbage collection
happened to occur during a weakref callback for a new-style class happened to occur during a weakref callback for a new-style class
instance, subtle memory corruption was the result (in a release build; instance, subtle memory corruption was the result (in a release build;

1073
Objects/setobject.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -467,6 +467,10 @@ SOURCE=..\Modules\rotormodule.c
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=..\Objects\setobject.c
# End Source File
# Begin Source File
SOURCE=..\Modules\shamodule.c SOURCE=..\Modules\shamodule.c
# End Source File # End Source File
# Begin Source File # Begin Source File

View file

@ -2116,12 +2116,14 @@ _PyBuiltin_Init(void)
SETBUILTIN("dict", &PyDict_Type); SETBUILTIN("dict", &PyDict_Type);
SETBUILTIN("enumerate", &PyEnum_Type); SETBUILTIN("enumerate", &PyEnum_Type);
SETBUILTIN("float", &PyFloat_Type); SETBUILTIN("float", &PyFloat_Type);
SETBUILTIN("frozenset", &PyFrozenSet_Type);
SETBUILTIN("property", &PyProperty_Type); SETBUILTIN("property", &PyProperty_Type);
SETBUILTIN("int", &PyInt_Type); SETBUILTIN("int", &PyInt_Type);
SETBUILTIN("list", &PyList_Type); SETBUILTIN("list", &PyList_Type);
SETBUILTIN("long", &PyLong_Type); SETBUILTIN("long", &PyLong_Type);
SETBUILTIN("object", &PyBaseObject_Type); SETBUILTIN("object", &PyBaseObject_Type);
SETBUILTIN("reversed", &PyReversed_Type); SETBUILTIN("reversed", &PyReversed_Type);
SETBUILTIN("set", &PySet_Type);
SETBUILTIN("slice", &PySlice_Type); SETBUILTIN("slice", &PySlice_Type);
SETBUILTIN("staticmethod", &PyStaticMethod_Type); SETBUILTIN("staticmethod", &PyStaticMethod_Type);
SETBUILTIN("str", &PyString_Type); SETBUILTIN("str", &PyString_Type);