From d0595044d2db6b5a6569ce3678195ed4a4469d39 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Sat, 23 May 2015 12:13:49 +0100 Subject: [PATCH] vm: implement IMPORT_NAME, IMPORT_FROM, IMPORT_STAR; py: factor Attribute code * Re-arrange and factor Attribute handling * Fix bug in import from curent directory * Implement IMPORT_* --- builtin/builtin.go | 15 ++------------- py/import.go | 6 +++--- py/internal.go | 38 ++++++++++++++++++++++---------------- vm/eval.go | 20 ++++++++++++++++++-- vm/tests/import_from.py | 12 ++++++++++++ vm/tests/import_name.py | 12 ++++++++++++ vm/tests/import_star.py | 41 +++++++++++++++++++++++++++++++++++++++++ vm/tests/lib.py | 14 ++++++++++++++ vm/tests/lib1.py | 19 +++++++++++++++++++ vm/vm_test.go | 7 ++++--- 10 files changed, 147 insertions(+), 37 deletions(-) create mode 100644 vm/tests/import_from.py create mode 100644 vm/tests/import_name.py create mode 100644 vm/tests/import_star.py create mode 100644 vm/tests/lib.py create mode 100644 vm/tests/lib1.py diff --git a/builtin/builtin.go b/builtin/builtin.go index 1ebf0b4e..94b8186c 100644 --- a/builtin/builtin.go +++ b/builtin/builtin.go @@ -411,12 +411,7 @@ func builtin_getattr(self py.Object, args py.Tuple) py.Object { py.UnpackTuple(args, nil, "getattr", 2, 3, &v, &name, &dflt) - nameStr, ok := name.(py.String) - if !ok { - panic(py.ExceptionNewf(py.TypeError, "getattr(): attribute name must be string")) - } - - result, err := py.GetAttrErr(v, string(nameStr)) + result, err := py.GetAttrErr(v, name) if err != nil { if dflt == nil { panic(err) @@ -435,13 +430,7 @@ func builtin_hasattr(self py.Object, args py.Tuple) py.Object { var v py.Object var name py.Object py.UnpackTuple(args, nil, "hasattr", 2, 2, &v, &name) - - nameStr, ok := name.(py.String) - if !ok { - panic(py.ExceptionNewf(py.TypeError, "hasattr(): attribute name must be string")) - } - - _, err := py.GetAttrErr(v, string(nameStr)) + _, err := py.GetAttrErr(v, name) return py.NewBool(err == nil) } diff --git a/py/import.go b/py/import.go index 07f2f568..dd443409 100644 --- a/py/import.go +++ b/py/import.go @@ -12,7 +12,7 @@ import ( var ( // This will become sys.path one day ;-) - modulePath = []string{"", "/usr/lib/python3.3", "/usr/local/lib/python3.3/dist-packages", "/usr/lib/python3/dist-packages"} + modulePath = []string{"", "/usr/lib/python3.4", "/usr/local/lib/python3.4/dist-packages", "/usr/lib/python3/dist-packages"} ) // The workings of __import__ @@ -93,7 +93,7 @@ func ImportModuleLevelObject(name string, globals, locals StringDict, fromlist T if !ok { panic(ExceptionNewf(SystemError, "Couldn't find __file__ in globals")) } - mpath = string(mpathObj.(String)) + mpath = path.Dir(string(mpathObj.(String))) } fullPath := path.Join(mpath, pathParts) // FIXME Read pyc/pyo too @@ -277,7 +277,7 @@ func XImportModuleLevelObject(nameObj, given_globals, locals, given_fromlist Obj // NOTE: because of this, __initializing__ must be set *before* // stuffing the new module in sys.modules. - value, err = GetAttrErr(mod, "__initializing__") + value, err = GetAttrStringErr(mod, "__initializing__") if err == nil { initializing = bool(MakeBool(value).(Bool)) } diff --git a/py/internal.go b/py/internal.go index 218de3f2..6b0d6195 100644 --- a/py/internal.go +++ b/py/internal.go @@ -4,6 +4,15 @@ package py +// AttributeName converts an Object to a string, raising a TypeError +// if it wasn't a String +func AttributeName(keyObj Object) string { + if key, ok := keyObj.(String); ok { + return string(key) + } + panic(ExceptionNewf(TypeError, "attribute name must be string, not '%s'", keyObj.Type().Name)) +} + // Bool is called to implement truth value testing and the built-in // operation bool(); should return False or True. When this method is // not defined, __len__() is called, if it is defined, and the object @@ -142,10 +151,10 @@ func DelItem(self Object, key Object) Object { panic(ExceptionNewf(TypeError, "'%s' object does not support item deletion", self.Type().Name)) } -// GetAttrErr - returns the result or an err to be raised if not found +// GetAttrStringErr - returns the result or an err to be raised if not found // // Only AttributeErrors will be returned in err, everything else will be raised -func GetAttrErr(self Object, key string) (res Object, err error) { +func GetAttrStringErr(self Object, key string) (res Object, err error) { defer func() { if r := recover(); r != nil { if IsException(AttributeError, r) { @@ -201,9 +210,16 @@ func GetAttrErr(self Object, key string) (res Object, err error) { return } +// GetAttrErr - returns the result or an err to be raised if not found +// +// Only AttributeErrors will be returned in err, everything else will be raised +func GetAttrErr(self Object, keyObj Object) (res Object, err error) { + return GetAttrStringErr(self, AttributeName(keyObj)) +} + // GetAttrString gets the attribute, raising an error if not found func GetAttrString(self Object, key string) Object { - res, err := GetAttrErr(self, key) + res, err := GetAttrStringErr(self, key) if err != nil { panic(err) } @@ -213,10 +229,7 @@ func GetAttrString(self Object, key string) Object { // GetAttr gets the attribute rasing an error if key isn't a string or // attribute not found func GetAttr(self Object, keyObj Object) Object { - if key, ok := keyObj.(String); ok { - return GetAttrString(self, string(key)) - } - panic(ExceptionNewf(TypeError, "attribute name must be string, not '%s'", self.Type().Name)) + return GetAttrString(self, AttributeName(keyObj)) } // SetAttrString @@ -255,10 +268,7 @@ func SetAttrString(self Object, key string, value Object) Object { // SetAttr func SetAttr(self Object, keyObj Object, value Object) Object { - if key, ok := keyObj.(String); ok { - return GetAttrString(self, string(key)) - } - panic(ExceptionNewf(TypeError, "attribute name must be string, not '%s'", self.Type().Name)) + return GetAttrString(self, AttributeName(keyObj)) } // DeleteAttrString @@ -301,9 +311,5 @@ func DeleteAttrString(self Object, key string) { // DeleteAttr func DeleteAttr(self Object, keyObj Object) { - if key, ok := keyObj.(String); ok { - DeleteAttrString(self, string(key)) - return - } - panic(ExceptionNewf(TypeError, "attribute name must be string, not '%s'", self.Type().Name)) + DeleteAttrString(self, AttributeName(keyObj)) } diff --git a/vm/eval.go b/vm/eval.go index 71d615e3..35628b1a 100644 --- a/vm/eval.go +++ b/vm/eval.go @@ -35,6 +35,7 @@ objects so they can be GCed import ( "runtime/debug" + "strings" "github.com/ncw/gpython/py" ) @@ -673,7 +674,22 @@ func do_YIELD_VALUE(vm *Vm, arg int32) { // names. This opcode implements from module import *. func do_IMPORT_STAR(vm *Vm, arg int32) { defer vm.CheckException() - vm.NotImplemented("IMPORT_STAR", arg) + from := vm.POP() + module := from.(*py.Module) + if all, ok := module.Globals["__all__"]; ok { + py.Iterate(all, func(item py.Object) bool { + name := py.AttributeName(item) + vm.frame.Locals[name] = py.GetAttrString(module, name) + return false + }) + } else { + for name, value := range module.Globals { + if !strings.HasPrefix(name, "_") { + vm.frame.Locals[name] = value + } + } + } + // FIXME implement STORE_FAST stuff } // Removes one block from the block stack. Per frame, there is a stack @@ -1010,7 +1026,7 @@ func do_IMPORT_FROM(vm *Vm, namei int32) { defer vm.CheckException() name := vm.frame.Code.Names[namei] module := vm.TOP() - res, err := py.GetAttrErr(module, name) + res, err := py.GetAttrStringErr(module, name) if err != nil { // Catch AttributeError and rethrow as ImportError if py.IsException(py.AttributeError, err) { diff --git a/vm/tests/import_from.py b/vm/tests/import_from.py new file mode 100644 index 00000000..01180ba0 --- /dev/null +++ b/vm/tests/import_from.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3.4 + +# test IMPORT_FROM + +from lib import libfn, libvar, libclass + +assert libfn() == 42 +assert libvar == 43 +assert libclass().method() == 44 + +# End with this +finished = True diff --git a/vm/tests/import_name.py b/vm/tests/import_name.py new file mode 100644 index 00000000..faf17d9d --- /dev/null +++ b/vm/tests/import_name.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3.4 + +# test IMPORT_NAME + +import lib + +assert lib.libfn() == 42 +assert lib.libvar == 43 +assert lib.libclass().method() == 44 + +# End with this +finished = True diff --git a/vm/tests/import_star.py b/vm/tests/import_star.py new file mode 100644 index 00000000..16e02224 --- /dev/null +++ b/vm/tests/import_star.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3.4 + +# test IMPORT_STAR + +from lib import * + +assert libfn() == 42 +assert libvar == 43 +assert libclass().method() == 44 + +# FIXME - exception catching not working +# ok = False +# try: +# _libprivate +# except NameError: +# ok = True +# assert ok + +from lib1 import * + +assert lib1fn() == 42 +assert lib1var == 43 + +# FIXME - exception handling broken +# ok = False +# try: +# lib1class +# except NameError: +# ok = True +# assert ok + +# FIXME - exception handling broken +# ok = False +# try: +# _libprivate +# except NameError: +# ok = True +# assert ok + +# End with this +finished = True diff --git a/vm/tests/lib.py b/vm/tests/lib.py new file mode 100644 index 00000000..7448fbb7 --- /dev/null +++ b/vm/tests/lib.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3.4 + +# Some targets to be imported + +def libfn(): + return 42 + +libvar = 43 + +class libclass: + def method(self): + return 44 + +_libprivate = 45 diff --git a/vm/tests/lib1.py b/vm/tests/lib1.py new file mode 100644 index 00000000..2fdf9479 --- /dev/null +++ b/vm/tests/lib1.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3.4 + +# Some targets to be imported + +__all__ = [ + "lib1fn", + "lib1var", +] + +def lib1fn(): + return 42 + +lib1var = 43 + +class lib1class: + def method(self): + return 44 + +_lib1private = 45 diff --git a/vm/vm_test.go b/vm/vm_test.go index f354cea1..9c54c033 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -55,9 +55,10 @@ func TestVm(t *testing.T) { t.Fatalf("ReadDir failed: %v", err) } for _, f := range files { - name := path.Join(testDir, f.Name()) - if strings.HasSuffix(name, ".py") { - t.Logf("%s: Starting", name) + name := f.Name() + if !strings.HasPrefix(name, "lib") && strings.HasSuffix(name, ".py") { + name := path.Join(testDir, name) + t.Logf("%s: Running", name) run(t, name) } }