Skip to content

Add support for print to file and file flush. #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 12, 2018
Merged
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
35 changes: 29 additions & 6 deletions builtin/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package builtin

import (
"fmt"
"unicode/utf8"

"github.com/go-python/gpython/compile"
Expand Down Expand Up @@ -177,7 +176,7 @@ func builtin_print(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Obje
var (
sepObj py.Object = py.String(" ")
endObj py.Object = py.String("\n")
file py.Object
file py.Object = py.MustGetModule("sys").Globals["stdout"]
flush py.Object
)
kwlist := []string{"sep", "end", "file", "flush"}
Expand All @@ -187,19 +186,43 @@ func builtin_print(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Obje
}
sep := sepObj.(py.String)
end := endObj.(py.String)
// FIXME ignoring file and flush

write, err := py.GetAttrString(file, "write")
if err != nil {
return nil, err
}

for i, v := range args {
v, err := py.Str(v)
if err != nil {
return nil, err
}

fmt.Printf("%v", v)
_, err = py.Call(write, py.Tuple{v}, nil)
if err != nil {
return nil, err
}

if i != len(args)-1 {
fmt.Print(sep)
_, err = py.Call(write, py.Tuple{sep}, nil)
if err != nil {
return nil, err
}
}
}
fmt.Print(end)

_, err = py.Call(write, py.Tuple{end}, nil)
if err != nil {
return nil, err
}

if shouldFlush, _ := py.MakeBool(flush); shouldFlush == py.True {
fflush, err := py.GetAttrString(file, "flush")
if err == nil {
return py.Call(fflush, nil, nil)
}
}

return py.None, nil
}

Expand Down
39 changes: 36 additions & 3 deletions builtin/tests/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,42 @@ def gen2():
assert repr("hello") == "'hello'"

doc="print"
# FIXME - need io redirection to test
#print("hello world")
#print(1,2,3,sep=",",end=",\n")
ok = False
try:
print("hello", sep=1)
except TypeError as e:
#if e.args[0] != "sep must be None or a string, not int":
# raise
ok = True
assert ok, "TypeError not raised"

try:
print("hello", sep=" ", end=1)
except TypeError as e:
#if e.args[0] != "end must be None or a string, not int":
# raise
ok = True
assert ok, "TypeError not raised"

try:
print("hello", sep=" ", end="\n", file=1)
except AttributeError as e:
#if e.args[0] != "'int' object has no attribute 'write'":
# raise
ok = True
assert ok, "AttributeError not raised"

with open("testfile", "w") as f:
print("hello", "world", sep=" ", end="\n", file=f)

with open("testfile", "r") as f:
assert f.read() == "hello world\n"

with open("testfile", "w") as f:
print(1,2,3,sep=",",end=",\n", file=f)

with open("testfile", "r") as f:
assert f.read() == "1,2,3,\n"

doc="round"
assert round(1.1) == 1.0
Expand Down
41 changes: 36 additions & 5 deletions py/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
)

var FileType = NewType("file", `represents an open file`)
var errClosed = ExceptionNewf(ValueError, "I/O operation on closed file.")

func init() {
FileType.Dict["write"] = MustNewMethod("write", func(self Object, value Object) (Object, error) {
Expand All @@ -28,6 +29,9 @@ func init() {
FileType.Dict["close"] = MustNewMethod("close", func(self Object) (Object, error) {
return self.(*File).Close()
}, 0, "close() -> None or (perhaps) an integer. Close the file.\n\nSets data attribute .closed to True. A closed file cannot be used for\nfurther I/O operations. close() may be called more than once without\nerror. Some kinds of file objects (for example, opened by popen())\nmay return an exit status upon closing.")
FileType.Dict["flush"] = MustNewMethod("flush", func(self Object) (Object, error) {
return self.(*File).Flush()
}, 0, "flush() -> Flush the write buffers of the stream if applicable. This does nothing for read-only and non-blocking streams.")
}

type FileMode int
Expand Down Expand Up @@ -71,6 +75,9 @@ func (o *File) Write(value Object) (Object, error) {
}

n, err := o.File.Write(b)
if err != nil && err.(*os.PathError).Err == os.ErrClosed {
return nil, errClosed
}
return Int(n), err
}

Expand Down Expand Up @@ -123,10 +130,14 @@ func (o *File) Read(args Tuple, kwargs StringDict) (Object, error) {
}

b, err := ioutil.ReadAll(r)
if err == io.EOF {
return o.readResult(nil)
}
if err != nil {
if err == io.EOF {
return o.readResult(nil)
}
if perr, ok := err.(*os.PathError); ok && perr.Err == os.ErrClosed {
return nil, errClosed
}

return nil, err
}

Expand All @@ -138,6 +149,23 @@ func (o *File) Close() (Object, error) {
return None, nil
}

func (o *File) Flush() (Object, error) {
err := o.File.Sync()
if perr, ok := err.(*os.PathError); ok && perr.Err == os.ErrClosed {
return nil, errClosed
}

return None, nil
}

func (o *File) M__enter__() (Object, error) {
return o, nil
}

func (o *File) M__exit__(exc_type, exc_value, traceback Object) (Object, error) {
return o.Close()
}

func OpenFile(filename, mode string, buffering int) (Object, error) {
var fileMode FileMode
var truncate bool
Expand Down Expand Up @@ -177,8 +205,8 @@ func OpenFile(filename, mode string, buffering int) (Object, error) {
return nil, ExceptionNewf(ValueError, "Must have exactly one of create/read/write/append mode and at most one plus")
}

truncate = (fileMode & FileWrite) != 0
fileMode |= FileReadWrite
truncate = false

case 'b':
if fileMode&FileReadWrite == 0 {
Expand Down Expand Up @@ -229,14 +257,15 @@ func OpenFile(filename, mode string, buffering int) (Object, error) {

f, err := os.OpenFile(filename, fmode, 0666)
if err != nil {
// XXX: should check for different types of errors
switch {
case os.IsExist(err):
return nil, ExceptionNewf(FileExistsError, err.Error())

case os.IsNotExist(err):
return nil, ExceptionNewf(FileNotFoundError, err.Error())
}

return nil, ExceptionNewf(OSError, err.Error())
}

if finfo, err := f.Stat(); err == nil {
Expand All @@ -250,3 +279,5 @@ func OpenFile(filename, mode string, buffering int) (Object, error) {
}

// Check interface is satisfied
var _ I__enter__ = (*File)(nil)
var _ I__exit__ = (*File)(nil)
8 changes: 6 additions & 2 deletions py/tests/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@
assert n == 5

doc = "close"
f.close()
assert f.close() == None

assertRaises(ValueError, f.read, 1)
assertRaises(ValueError, f.write, "")
assertRaises(ValueError, f.flush)

# closing a closed file should not throw an error
f.close()
assert f.close() == None

doc = "finished"
1 change: 0 additions & 1 deletion pytest/pytest.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"testing"

_ "github.com/go-python/gpython/builtin"
_ "github.com/go-python/gpython/sys"
"github.com/go-python/gpython/compile"
"github.com/go-python/gpython/py"
_ "github.com/go-python/gpython/sys"
Expand Down