* 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 @@
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 @@ def __calc_timezone(self):
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 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 @@ def __init__(self):
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 @@
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 @@ def check_all(self, modname):
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 @@ def get_vars_f2():
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 @@ def test_argumentcheck(self):
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 @@
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 @@ def glob(self, *parts):
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 @@
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 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 @@ def test_sample(self):
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 @@ def factorial(n):
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 @@ def test_bigrand_ranges(self):
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 @@ def testMagicConstants(self):
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 @@ def test_inputtypes(self):
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 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
@ -17,10 +16,10 @@ def test_basic(self):
class LocaleTime_Tests(unittest.TestCase): class LocaleTime_Tests(unittest.TestCase):
"""Tests for _strptime.LocaleTime. """Tests for _strptime.LocaleTime.
All values are lower-cased when stored in LocaleTime, so make sure to All values are lower-cased when stored in LocaleTime, so make sure to
compare values after running ``lower`` on them. compare values after running ``lower`` on them.
""" """
def setUp(self): def setUp(self):
@ -167,7 +166,7 @@ def test_blankpattern(self):
# 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 @@ def check_iterandlist(iter, lst, ref):
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 @@ def items(self):
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 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 @@ def test_weak_keys(self):
"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);