Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

Fixes #290: pyPrint accepts any filelike, with sys.stdout fallback #304

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions lib/_gomodulehacks.py
Original file line number Diff line number Diff line change
@@ -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
68 changes: 43 additions & 25 deletions lib/sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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())
4 changes: 2 additions & 2 deletions runtime/builtin_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}
Expand Down
41 changes: 27 additions & 14 deletions runtime/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion runtime/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
26 changes: 26 additions & 0 deletions runtime/sysmodule.go
Original file line number Diff line number Diff line change
@@ -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("<native>").ToObject(),
"__name__": NewStr("_sys").ToObject(),
"stdin": Stdin.ToObject(),
"stdout": Stdout.ToObject(),
"stderr": Stderr.ToObject(),
})
)
15 changes: 15 additions & 0 deletions testing/sysmodule_test.py
Original file line number Diff line number Diff line change
@@ -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'
3 changes: 2 additions & 1 deletion third_party/stdlib/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down