Skip to content

Commit 85d6bc1

Browse files
committed
Fix dir() for modules and types
dir() is supposed to treat modules and types differently than normal objects: https://docs.python.org/2/library/functions.html#dir This change fix that inconsistency. This change activated a bunch of tests that were broken, because test_* functions in base classes of TestCase objects are now properly discovered. To make some of the test cases at least runnable, I added dict.popitem. Many others are broken and are now being skipped.
1 parent 9b8d487 commit 85d6bc1

10 files changed

+1068
-976
lines changed

runtime/builtin_types.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -342,14 +342,26 @@ func builtinDir(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
342342
}
343343
d := NewDict()
344344
o := args[0]
345-
if dict := o.Dict(); dict != nil {
346-
if raised := d.Update(f, dict.ToObject()); raised != nil {
347-
return nil, raised
345+
switch {
346+
case o.isInstance(TypeType):
347+
for _, t := range toTypeUnsafe(o).mro {
348+
if raised := d.Update(f, t.Dict().ToObject()); raised != nil {
349+
return nil, raised
350+
}
348351
}
349-
}
350-
for _, t := range o.typ.mro {
351-
if raised := d.Update(f, t.Dict().ToObject()); raised != nil {
352-
return nil, raised
352+
case o.isInstance(ModuleType):
353+
d.Update(f, o.Dict().ToObject())
354+
default:
355+
d = NewDict()
356+
if dict := o.Dict(); dict != nil {
357+
if raised := d.Update(f, dict.ToObject()); raised != nil {
358+
return nil, raised
359+
}
360+
}
361+
for _, t := range o.typ.mro {
362+
if raised := d.Update(f, t.Dict().ToObject()); raised != nil {
363+
return nil, raised
364+
}
353365
}
354366
}
355367
l := d.Keys(f)

runtime/builtin_types_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ func TestBuiltinFuncs(t *testing.T) {
6464
fooDir := NewList(fooTypeDir.elems...)
6565
fooDir.Append(NewStr("baz").ToObject())
6666
fooDir.Sort(f)
67+
dirModule := newTestModule("foo", "foo.py")
68+
if raised := dirModule.Dict().SetItemString(NewRootFrame(), "bar", newObject(ObjectType)); raised != nil {
69+
panic(raised)
70+
}
71+
dirModuleDir := dirModule.Dict().Keys(NewRootFrame())
72+
if raised := dirModuleDir.Sort(NewRootFrame()); raised != nil {
73+
panic(raised)
74+
}
6775
iter := mustNotRaise(Iter(f, mustNotRaise(xrangeType.Call(f, wrapArgs(5), nil))))
6876
neg := wrapFuncForTest(func(f *Frame, i int) int { return -i })
6977
raiseKey := wrapFuncForTest(func(f *Frame, o *Object) *BaseException { return f.RaiseType(RuntimeErrorType, "foo") })
@@ -157,7 +165,9 @@ func TestBuiltinFuncs(t *testing.T) {
157165
{f: "chr", args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'chr' requires 1 arguments")},
158166
{f: "dir", args: wrapArgs(newObject(ObjectType)), want: objectDir.ToObject()},
159167
{f: "dir", args: wrapArgs(newObject(fooType)), want: fooTypeDir.ToObject()},
168+
{f: "dir", args: wrapArgs(fooType), want: fooTypeDir.ToObject()},
160169
{f: "dir", args: wrapArgs(foo), want: fooDir.ToObject()},
170+
{f: "dir", args: wrapArgs(dirModule), want: dirModuleDir.ToObject()},
161171
{f: "dir", args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'dir' requires 1 arguments")},
162172
{f: "divmod", args: wrapArgs(12, 7), want: NewTuple2(NewInt(1).ToObject(), NewInt(5).ToObject()).ToObject()},
163173
{f: "divmod", args: wrapArgs(-12, 7), want: NewTuple2(NewInt(-2).ToObject(), NewInt(2).ToObject()).ToObject()},

runtime/dict.go

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,13 @@ func dictContains(f *Frame, seq, value *Object) (*Object, *BaseException) {
539539
return GetBool(item != nil).ToObject(), nil
540540
}
541541

542+
func dictCopy(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
543+
if raised := checkMethodArgs(f, "copy", args, DictType); raised != nil {
544+
return nil, raised
545+
}
546+
return DictType.Call(f, args, nil)
547+
}
548+
542549
func dictDelItem(f *Frame, o, key *Object) *BaseException {
543550
deleted, raised := toDictUnsafe(o).DelItem(f, key)
544551
if raised != nil {
@@ -634,28 +641,6 @@ func dictKeys(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
634641
return toDictUnsafe(args[0]).Keys(f).ToObject(), nil
635642
}
636643

637-
func dictPop(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
638-
expectedTypes := []*Type{DictType, ObjectType, ObjectType}
639-
argc := len(args)
640-
if argc == 2 {
641-
expectedTypes = expectedTypes[:2]
642-
}
643-
if raised := checkMethodArgs(f, "pop", args, expectedTypes...); raised != nil {
644-
return nil, raised
645-
}
646-
key := args[1]
647-
d := toDictUnsafe(args[0])
648-
item, raised := d.Pop(f, key)
649-
if raised == nil && item == nil {
650-
if argc > 2 {
651-
item = args[2]
652-
} else {
653-
raised = raiseKeyError(f, key)
654-
}
655-
}
656-
return item, raised
657-
}
658-
659644
func dictGetItem(f *Frame, o, key *Object) (*Object, *BaseException) {
660645
item, raised := toDictUnsafe(o).GetItem(f, key)
661646
if raised != nil {
@@ -721,6 +706,48 @@ func dictNew(f *Frame, t *Type, _ Args, _ KWArgs) (*Object, *BaseException) {
721706
return d.ToObject(), nil
722707
}
723708

709+
func dictPop(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
710+
expectedTypes := []*Type{DictType, ObjectType, ObjectType}
711+
argc := len(args)
712+
if argc == 2 {
713+
expectedTypes = expectedTypes[:2]
714+
}
715+
if raised := checkMethodArgs(f, "pop", args, expectedTypes...); raised != nil {
716+
return nil, raised
717+
}
718+
key := args[1]
719+
d := toDictUnsafe(args[0])
720+
item, raised := d.Pop(f, key)
721+
if raised == nil && item == nil {
722+
if argc > 2 {
723+
item = args[2]
724+
} else {
725+
raised = raiseKeyError(f, key)
726+
}
727+
}
728+
return item, raised
729+
}
730+
731+
func dictPopItem(f *Frame, args Args, _ KWArgs) (item *Object, raised *BaseException) {
732+
if raised := checkMethodArgs(f, "popitem", args, DictType); raised != nil {
733+
return nil, raised
734+
}
735+
d := toDictUnsafe(args[0])
736+
d.mutex.Lock(f)
737+
iter := newDictEntryIterator(d)
738+
entry := iter.next()
739+
if entry == nil {
740+
raised = f.RaiseType(KeyErrorType, "popitem(): dictionary is empty")
741+
} else {
742+
item = NewTuple(entry.key, entry.value).ToObject()
743+
d.table.storeEntry(int(iter.index-1), deletedEntry)
744+
d.table.incUsed(-1)
745+
d.incVersion()
746+
}
747+
d.mutex.Unlock(f)
748+
return item, raised
749+
}
750+
724751
func dictRepr(f *Frame, o *Object) (*Object, *BaseException) {
725752
d := toDictUnsafe(o)
726753
if f.reprEnter(d.ToObject()) {
@@ -825,6 +852,7 @@ func dictValues(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
825852

826853
func initDictType(dict map[string]*Object) {
827854
dict["clear"] = newBuiltinFunction("clear", dictClear).ToObject()
855+
dict["copy"] = newBuiltinFunction("copy", dictCopy).ToObject()
828856
dict["get"] = newBuiltinFunction("get", dictGet).ToObject()
829857
dict["has_key"] = newBuiltinFunction("has_key", dictHasKey).ToObject()
830858
dict["items"] = newBuiltinFunction("items", dictItems).ToObject()
@@ -833,6 +861,7 @@ func initDictType(dict map[string]*Object) {
833861
dict["itervalues"] = newBuiltinFunction("itervalues", dictIterValues).ToObject()
834862
dict["keys"] = newBuiltinFunction("keys", dictKeys).ToObject()
835863
dict["pop"] = newBuiltinFunction("pop", dictPop).ToObject()
864+
dict["popitem"] = newBuiltinFunction("popitem", dictPopItem).ToObject()
836865
dict["setdefault"] = newBuiltinFunction("setdefault", dictSetDefault).ToObject()
837866
dict["update"] = newBuiltinFunction("update", dictUpdate).ToObject()
838867
dict["values"] = newBuiltinFunction("values", dictValues).ToObject()

runtime/dict_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,37 @@ func TestDictPop(t *testing.T) {
585585
}
586586
}
587587

588+
func TestDictPopItem(t *testing.T) {
589+
popItem := mustNotRaise(GetAttr(NewRootFrame(), DictType.ToObject(), NewStr("popitem"), nil))
590+
fun := wrapFuncForTest(func(f *Frame, d *Dict) (*Object, *BaseException) {
591+
result := NewDict()
592+
item, raised := popItem.Call(f, wrapArgs(d), nil)
593+
for ; raised == nil; item, raised = popItem.Call(f, wrapArgs(d), nil) {
594+
t := toTupleUnsafe(item)
595+
result.SetItem(f, t.GetItem(0), t.GetItem(1))
596+
}
597+
if raised != nil {
598+
if !raised.isInstance(KeyErrorType) {
599+
return nil, raised
600+
}
601+
f.RestoreExc(nil, nil)
602+
}
603+
if raised = Assert(f, GetBool(d.Len() == 0).ToObject(), nil); raised != nil {
604+
return nil, raised
605+
}
606+
return result.ToObject(), nil
607+
})
608+
cases := []invokeTestCase{
609+
{args: wrapArgs(newTestDict("foo", 42)), want: newTestDict("foo", 42).ToObject()},
610+
{args: wrapArgs(newTestDict("foo", 42, 123, "bar")), want: newTestDict("foo", 42, 123, "bar").ToObject()},
611+
}
612+
for _, cas := range cases {
613+
if err := runInvokeTestCase(fun, &cas); err != "" {
614+
t.Error(err)
615+
}
616+
}
617+
}
618+
588619
func TestDictNewInit(t *testing.T) {
589620
cases := []invokeTestCase{
590621
{args: wrapArgs(), want: NewDict().ToObject()},

0 commit comments

Comments
 (0)