diff --git a/lib/_gomodulehacks.py b/lib/_gomodulehacks.py new file mode 100644 index 00000000..3412e485 --- /dev/null +++ b/lib/_gomodulehacks.py @@ -0,0 +1,39 @@ +# coding: utf-8 + +from __go__.grumpy import SysModules +import errno + +# For some reason, importing and extending grumpy.Module does not work. +Module = type(errno) + + +def hybrid_module(modulename, modulefile, moduledict, all_attrs, globals_): + """ + Augment native 'moduledict' with Python-sourced parts + + Allows 'modulename' to use 'moduledict' from outside, + for example a Grumpy dict from native module. + + And does include the resulting module on sys.modules at the end. + + Should be called as: + hybrid_module(__name__, __file__, YourmoduleDict, __all__, globals()) + On the last line of the Python-part of the module + """ + class HybridModule(Module): + def __init__(self): + moduledict['__name__'] = modulename + moduledict['__file__'] = modulefile + for k in all_attrs: + moduledict[k] = globals_[k] + def __setattr__(self, name, value): + moduledict[name] = value + def __getattribute__(self, name): # TODO: replace w/ __getattr__ when implemented + resp = moduledict.get(name) + if resp is None and name not in moduledict: + return super(HybridModule, self).__getattribute__(name) + return resp + + finalmodule = HybridModule() + SysModules[modulename] = finalmodule + return finalmodule diff --git a/lib/sys.py b/lib/sys.py index e59ca69c..5b79a6c6 100644 --- a/lib/sys.py +++ b/lib/sys.py @@ -15,43 +15,57 @@ """System-specific parameters and functions.""" from __go__.os import Args -from __go__.grumpy import SysModules, MaxInt, Stdin as stdin, Stdout as stdout, Stderr as stderr # pylint: disable=g-multiple-import +from __go__.grumpy import SysmoduleDict, SysModules, MaxInt # pylint: disable=g-multiple-import from __go__.runtime import (GOOS as platform, Version) from __go__.unicode import MaxRune +import _gomodulehacks + + +__all__ = ('stdin', 'stdout', 'stderr', 'argv', 'goversion', 'version', + 'maxint', 'maxsize', 'maxunicode', 'modules', 'platform', 'py3kwarning', + 'warnoptions', 'byteorder', 'flags', 'exc_clear', 'exc_info', 'exit') + argv = [] for arg in Args: - argv.append(arg) + argv.append(arg) goversion = Version() + +stdin = SysmoduleDict['stdin'] +stdout = SysmoduleDict['stdout'] +stderr = SysmoduleDict['stderr'] + maxint = MaxInt maxsize = maxint maxunicode = MaxRune modules = SysModules + py3kwarning = False warnoptions = [] # TODO: Support actual byteorder byteorder = 'little' version = '2.7.13' + class _Flags(object): - """Container class for sys.flags.""" - debug = 0 - py3k_warning = 0 - division_warning = 0 - division_new = 0 - inspect = 0 - interactive = 0 - optimize = 0 - dont_write_bytecode = 0 - no_user_site = 0 - no_site = 0 - ignore_environment = 0 - tabcheck = 0 - verbose = 0 - unicode = 0 - bytes_warning = 0 - hash_randomization = 0 + """Container class for sys.flags.""" + debug = 0 + py3k_warning = 0 + division_warning = 0 + division_new = 0 + inspect = 0 + interactive = 0 + optimize = 0 + dont_write_bytecode = 0 + no_user_site = 0 + no_site = 0 + ignore_environment = 0 + tabcheck = 0 + verbose = 0 + unicode = 0 + bytes_warning = 0 + hash_randomization = 0 flags = _Flags() @@ -62,12 +76,16 @@ def exc_clear(): def exc_info(): - e, tb = __frame__().__exc_info__() # pylint: disable=undefined-variable - t = None - if e: - t = type(e) - return t, e, tb + e, tb = __frame__().__exc_info__() # pylint: disable=undefined-variable + t = None + if e: + t = type(e) + return t, e, tb def exit(code=None): # pylint: disable=redefined-builtin - raise SystemExit(code) + raise SystemExit(code) + + +# Should be the last line of the python part of hybrid stuff +_gomodulehacks.hybrid_module(__name__, __file__, SysmoduleDict, __all__, globals()) diff --git a/runtime/builtin_types.go b/runtime/builtin_types.go index 0c31305f..c954125e 100644 --- a/runtime/builtin_types.go +++ b/runtime/builtin_types.go @@ -563,7 +563,7 @@ func builtinPrint(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) // to the file descriptor probably } } - return nil, pyPrint(f, args, sep, end, file) + return nil, pyPrint(f, args, sep, end, file.ToObject()) } func builtinRange(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { @@ -591,7 +591,7 @@ func builtinRawInput(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseExceptio } if len(args) == 1 { - err := pyPrint(f, args, "", "", Stdout) + err := pyPrint(f, args, "", "", Stdout.ToObject()) if err != nil { return nil, err } diff --git a/runtime/core.go b/runtime/core.go index 966a2dc9..0abdb9ce 100644 --- a/runtime/core.go +++ b/runtime/core.go @@ -683,7 +683,11 @@ func Print(f *Frame, args Args, nl bool) *BaseException { } else if len(args) > 0 { end = " " } - return pyPrint(f, args, " ", end, Stdout) + file, raised := SysmoduleDict.GetItemString(f, "stdout") + if raised != nil { + return raised + } + return pyPrint(f, args, " ", end, file) } // Repr returns a string containing a printable representation of o. This is @@ -1258,29 +1262,38 @@ func hashNotImplemented(f *Frame, o *Object) (*Object, *BaseException) { } // pyPrint encapsulates the logic of the Python print function. -func pyPrint(f *Frame, args Args, sep, end string, file *File) *BaseException { +func pyPrint(f *Frame, args Args, sep, end string, filelike *Object) *BaseException { + writeFunc, raised := GetAttr(f, filelike, NewStr("write"), nil) + if raised != nil { + return raised + } + + pySep := NewStr(sep) + pyEnd := NewStr(end) + callArgs := f.MakeArgs(1) for i, arg := range args { if i > 0 { - err := file.writeString(sep) - if err != nil { - return f.RaiseType(IOErrorType, err.Error()) + callArgs[0] = pySep.ToObject() + _, raised := writeFunc.Call(f, callArgs, nil) + if raised != nil { + return raised } } - s, raised := ToStr(f, arg) - if raised != nil { - return raised + s, raised2 := ToStr(f, arg) + if raised2 != nil { + return raised2 } - err := file.writeString(s.Value()) - if err != nil { - return f.RaiseType(IOErrorType, err.Error()) + callArgs[0] = s.ToObject() + if _, raised := writeFunc.Call(f, callArgs, nil); raised != nil { + return raised } } - err := file.writeString(end) - if err != nil { - return f.RaiseType(IOErrorType, err.Error()) + callArgs[0] = pyEnd.ToObject() + if _, raised := writeFunc.Call(f, callArgs, nil); raised != nil { + return raised } return nil diff --git a/runtime/core_test.go b/runtime/core_test.go index d41b682b..e48df14f 100644 --- a/runtime/core_test.go +++ b/runtime/core_test.go @@ -818,7 +818,7 @@ func TestPos(t *testing.T) { func TestPyPrint(t *testing.T) { fun := wrapFuncForTest(func(f *Frame, args *Tuple, sep, end string) (string, *BaseException) { return captureStdout(f, func() *BaseException { - return pyPrint(NewRootFrame(), args.elems, sep, end, Stdout) + return pyPrint(NewRootFrame(), args.elems, sep, end, Stdout.ToObject()) }) }) cases := []invokeTestCase{ diff --git a/runtime/sysmodule.go b/runtime/sysmodule.go new file mode 100644 index 00000000..a3622336 --- /dev/null +++ b/runtime/sysmodule.go @@ -0,0 +1,26 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package grumpy + +var ( + // SysmoduleDict is the __dict__ of the native part of sys.py. + SysmoduleDict = newStringDict(map[string]*Object{ + "__file__": NewStr("").ToObject(), + "__name__": NewStr("_sys").ToObject(), + "stdin": Stdin.ToObject(), + "stdout": Stdout.ToObject(), + "stderr": Stderr.ToObject(), + }) +) diff --git a/testing/sysmodule_test.py b/testing/sysmodule_test.py new file mode 100644 index 00000000..6d4bc3a1 --- /dev/null +++ b/testing/sysmodule_test.py @@ -0,0 +1,15 @@ +from StringIO import StringIO +import sys + +print 'To sys.stdout' + +old_stdout = sys.stdout +sio = StringIO() +sys.stdout = sio + +print 'To replaced sys.stdout' + +sys.stdout = old_stdout +print 'To original sys.stdout' + +assert sio.tell() == len('To replaced sys.stdout')+1, 'Should had printed to StringIO, not STDOUT' \ No newline at end of file diff --git a/third_party/stdlib/types.py b/third_party/stdlib/types.py index 65e1fb62..fe55eed3 100644 --- a/third_party/stdlib/types.py +++ b/third_party/stdlib/types.py @@ -3,6 +3,7 @@ Types that are part of optional modules (e.g. array) are not listed. """ import sys +import errno # Iterators in Python aren't a matter of type but of protocol. A large # and changing number of builtin types implement *some* flavor of @@ -59,7 +60,7 @@ def _m(self): pass BuiltinFunctionType = type(len) BuiltinMethodType = type([].append) # Same as BuiltinFunctionType -ModuleType = type(sys) +ModuleType = type(errno) FileType = file XRangeType = xrange