Skip to content

Commit 24e39a3

Browse files
committed
Add support for print to file and file flush.
1 parent 8cee534 commit 24e39a3

File tree

5 files changed

+108
-17
lines changed

5 files changed

+108
-17
lines changed

builtin/builtin.go

+29-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package builtin
77

88
import (
9-
"fmt"
109
"unicode/utf8"
1110

1211
"github.com/go-python/gpython/compile"
@@ -177,7 +176,7 @@ func builtin_print(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Obje
177176
var (
178177
sepObj py.Object = py.String(" ")
179178
endObj py.Object = py.String("\n")
180-
file py.Object
179+
file py.Object = py.MustGetModule("sys").Globals["stdout"]
181180
flush py.Object
182181
)
183182
kwlist := []string{"sep", "end", "file", "flush"}
@@ -187,19 +186,43 @@ func builtin_print(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Obje
187186
}
188187
sep := sepObj.(py.String)
189188
end := endObj.(py.String)
190-
// FIXME ignoring file and flush
189+
190+
write, err := py.GetAttrString(file, "write")
191+
if err != nil {
192+
return nil, err
193+
}
194+
191195
for i, v := range args {
192196
v, err := py.Str(v)
193197
if err != nil {
194198
return nil, err
195199
}
196200

197-
fmt.Printf("%v", v)
201+
_, err = py.Call(write, py.Tuple{v}, nil)
202+
if err != nil {
203+
return nil, err
204+
}
205+
198206
if i != len(args)-1 {
199-
fmt.Print(sep)
207+
_, err = py.Call(write, py.Tuple{sep}, nil)
208+
if err != nil {
209+
return nil, err
210+
}
200211
}
201212
}
202-
fmt.Print(end)
213+
214+
_, err = py.Call(write, py.Tuple{end}, nil)
215+
if err != nil {
216+
return nil, err
217+
}
218+
219+
if shouldFlush, _ := py.MakeBool(flush); shouldFlush == py.True {
220+
fflush, err := py.GetAttrString(file, "flush")
221+
if err == nil {
222+
return py.Call(fflush, nil, nil)
223+
}
224+
}
225+
203226
return py.None, nil
204227
}
205228

builtin/tests/builtin.py

+36-3
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,42 @@ def gen2():
147147
assert repr("hello") == "'hello'"
148148

149149
doc="print"
150-
# FIXME - need io redirection to test
151-
#print("hello world")
152-
#print(1,2,3,sep=",",end=",\n")
150+
ok = False
151+
try:
152+
print("hello", sep=1)
153+
except TypeError as e:
154+
#if e.args[0] != "sep must be None or a string, not int":
155+
# raise
156+
ok = True
157+
assert ok, "TypeError not raised"
158+
159+
try:
160+
print("hello", sep=" ", end=1)
161+
except TypeError as e:
162+
#if e.args[0] != "end must be None or a string, not int":
163+
# raise
164+
ok = True
165+
assert ok, "TypeError not raised"
166+
167+
try:
168+
print("hello", sep=" ", end="\n", file=1)
169+
except AttributeError as e:
170+
#if e.args[0] != "'int' object has no attribute 'write'":
171+
# raise
172+
ok = True
173+
assert ok, "AttributeError not raised"
174+
175+
with open("testfile", "w") as f:
176+
print("hello", "world", sep=" ", end="\n", file=f)
177+
178+
with open("testfile", "r") as f:
179+
assert f.read() == "hello world\n"
180+
181+
with open("testfile", "w") as f:
182+
print(1,2,3,sep=",",end=",\n", file=f)
183+
184+
with open("testfile", "r") as f:
185+
assert f.read() == "1,2,3,\n"
153186

154187
doc="round"
155188
assert round(1.1) == 1.0

py/file.go

+37-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
)
1717

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

2021
func init() {
2122
FileType.Dict["write"] = MustNewMethod("write", func(self Object, value Object) (Object, error) {
@@ -28,6 +29,9 @@ func init() {
2829
FileType.Dict["close"] = MustNewMethod("close", func(self Object) (Object, error) {
2930
return self.(*File).Close()
3031
}, 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.")
32+
FileType.Dict["flush"] = MustNewMethod("flush", func(self Object) (Object, error) {
33+
return self.(*File).Flush()
34+
}, 0, "flush() -> Flush the write buffers of the stream if applicable. This does nothing for read-only and non-blocking streams.")
3135
}
3236

3337
type FileMode int
@@ -71,6 +75,9 @@ func (o *File) Write(value Object) (Object, error) {
7175
}
7276

7377
n, err := o.File.Write(b)
78+
if err != nil && err.(*os.PathError).Err == os.ErrClosed {
79+
return nil, errClosed
80+
}
7481
return Int(n), err
7582
}
7683

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

125132
b, err := ioutil.ReadAll(r)
126-
if err == io.EOF {
127-
return o.readResult(nil)
128-
}
129133
if err != nil {
134+
if err == io.EOF {
135+
return o.readResult(nil)
136+
}
137+
if perr, ok := err.(*os.PathError); ok && perr.Err == os.ErrClosed {
138+
return nil, errClosed
139+
}
140+
130141
return nil, err
131142
}
132143

@@ -138,6 +149,24 @@ func (o *File) Close() (Object, error) {
138149
return None, nil
139150
}
140151

152+
func (o *File) Flush() (Object, error) {
153+
err := o.File.Sync()
154+
if perr, ok := err.(*os.PathError); ok && perr.Err == os.ErrClosed {
155+
return nil, errClosed
156+
}
157+
158+
return None, nil
159+
}
160+
161+
func (o *File) M__enter__() (Object, error) {
162+
return o, nil
163+
}
164+
165+
func (o *File) M__exit__(exc_type, exc_value, traceback Object) (Object, error) {
166+
o.Close()
167+
return None, nil
168+
}
169+
141170
func OpenFile(filename, mode string, buffering int) (Object, error) {
142171
var fileMode FileMode
143172
var truncate bool
@@ -177,8 +206,8 @@ func OpenFile(filename, mode string, buffering int) (Object, error) {
177206
return nil, ExceptionNewf(ValueError, "Must have exactly one of create/read/write/append mode and at most one plus")
178207
}
179208

209+
truncate = (fileMode & FileWrite) != 0
180210
fileMode |= FileReadWrite
181-
truncate = false
182211

183212
case 'b':
184213
if fileMode&FileReadWrite == 0 {
@@ -229,14 +258,15 @@ func OpenFile(filename, mode string, buffering int) (Object, error) {
229258

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

237265
case os.IsNotExist(err):
238266
return nil, ExceptionNewf(FileNotFoundError, err.Error())
239267
}
268+
269+
return nil, ExceptionNewf(OSError, err.Error())
240270
}
241271

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

252282
// Check interface is satisfied
283+
var _ I__enter__ = (*File)(nil)
284+
var _ I__exit__ = (*File)(nil)

py/tests/file.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@
3535
assert n == 5
3636

3737
doc = "close"
38-
f.close()
38+
assert f.close() == None
39+
40+
assertRaises(ValueError, f.read, 1)
41+
assertRaises(ValueError, f.write, "")
42+
assertRaises(ValueError, f.flush)
3943

4044
# closing a closed file should not throw an error
41-
f.close()
45+
assert f.close() == None
4246

4347
doc = "finished"

pytest/pytest.go

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"testing"
1313

1414
_ "github.com/go-python/gpython/builtin"
15-
_ "github.com/go-python/gpython/sys"
1615
"github.com/go-python/gpython/compile"
1716
"github.com/go-python/gpython/py"
1817
_ "github.com/go-python/gpython/sys"

0 commit comments

Comments
 (0)