Skip to content

Commit 6e7b5ec

Browse files
raffsbinet
authored andcommitted
Initial work at implementing file methods:
- open (builtin) - File.read - File.write - File.close
1 parent 09f14d0 commit 6e7b5ec

File tree

7 files changed

+354
-4
lines changed

7 files changed

+354
-4
lines changed

builtin/builtin.go

+63
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func init() {
5151
// py.MustNewMethod("max", builtin_max, 0, max_doc),
5252
// py.MustNewMethod("min", builtin_min, 0, min_doc),
5353
py.MustNewMethod("next", builtin_next, 0, next_doc),
54+
py.MustNewMethod("open", builtin_open, 0, open_doc),
5455
// py.MustNewMethod("oct", builtin_oct, 0, oct_doc),
5556
py.MustNewMethod("ord", builtin_ord, 0, ord_doc),
5657
py.MustNewMethod("pow", builtin_pow, 0, pow_doc),
@@ -437,6 +438,68 @@ fromlist is not empty. Level is used to determine whether to perform
437438
absolute or relative imports. 0 is absolute while a positive number
438439
is the number of parent directories to search relative to the current module.`
439440

441+
const open_doc = `open(name[, mode[, buffering]]) -> file object
442+
443+
Open a file using the file() type, returns a file object. This is the
444+
preferred way to open a file. See file.__doc__ for further information.`
445+
446+
func builtin_open(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
447+
kwlist := []string{
448+
"file",
449+
"mode",
450+
"buffering",
451+
"encoding",
452+
"errors",
453+
"newline",
454+
"closefd",
455+
"opener",
456+
}
457+
458+
var (
459+
filename py.Object
460+
mode py.Object = py.String("r")
461+
buffering py.Object = py.Int(-1)
462+
encoding py.Object = py.None
463+
errors py.Object = py.None
464+
newline py.Object = py.None
465+
closefd py.Object = py.Bool(true)
466+
opener py.Object = py.None
467+
)
468+
469+
err := py.ParseTupleAndKeywords(args, kwargs, "s|sizzzpO:open", kwlist,
470+
&filename,
471+
&mode,
472+
&buffering,
473+
&encoding,
474+
&errors,
475+
&newline,
476+
&closefd,
477+
&opener)
478+
if err != nil {
479+
return nil, err
480+
}
481+
482+
if encoding != py.None && encoding.(py.String) != py.String("utf-8") {
483+
return nil, py.ExceptionNewf(py.NotImplementedError, "encoding not implemented yet")
484+
}
485+
486+
if errors != py.None {
487+
return nil, py.ExceptionNewf(py.NotImplementedError, "errors not implemented yet")
488+
}
489+
490+
if newline != py.None {
491+
return nil, py.ExceptionNewf(py.NotImplementedError, "newline not implemented yet")
492+
}
493+
494+
if opener != py.None {
495+
return nil, py.ExceptionNewf(py.NotImplementedError, "opener not implemented yet")
496+
}
497+
498+
return py.OpenFile(string(filename.(py.String)),
499+
string(mode.(py.String)),
500+
int(buffering.(py.Int)))
501+
}
502+
440503
const ord_doc = `ord(c) -> integer
441504
442505
Return the integer ordinal of a one-character string.`

builtin/tests/builtin.py

+3
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ def gen2():
135135
ok = True
136136
assert ok, "TypeError not raised"
137137

138+
doc="open"
139+
assert open(__file__) is not None
140+
138141
doc="pow"
139142
assert pow(2, 10) == 1024
140143
assert pow(2, 10, 17) == 4

py/args.go

+11
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,12 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist
452452
switch op {
453453
case "O":
454454
*result = arg
455+
case "Z", "z":
456+
if _, ok := arg.(NoneType); ok {
457+
*result = arg
458+
break
459+
}
460+
fallthrough
455461
case "U", "s":
456462
if _, ok := arg.(String); !ok {
457463
return ExceptionNewf(TypeError, "%s() argument %d must be str, not %s", name, i+1, arg.Type().Name)
@@ -462,6 +468,11 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist
462468
return ExceptionNewf(TypeError, "%s() argument %d must be int, not %s", name, i+1, arg.Type().Name)
463469
}
464470
*result = arg
471+
case "p":
472+
if _, ok := arg.(Bool); !ok {
473+
return ExceptionNewf(TypeError, "%s() argument %d must be bool, not %s", name, i+1, arg.Type().Name)
474+
}
475+
*result = arg
465476
case "d":
466477
switch x := arg.(type) {
467478
case Int:

py/file.go

+229-3
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,243 @@
1010
package py
1111

1212
import (
13+
"io"
14+
"io/ioutil"
1315
"os"
1416
)
1517

16-
var FileType = NewTypeX("file", `represents an open file`,
17-
nil, nil)
18+
var FileType = NewType("file", `represents an open file`)
1819

19-
type File os.File
20+
func init() {
21+
FileType.Dict["write"] = MustNewMethod("write", func(self Object, value Object) (Object, error) {
22+
return self.(*File).Write(value)
23+
}, 0, "write(arg) -> writes the contents of arg to the file, returning the number of characters written.")
24+
25+
FileType.Dict["read"] = MustNewMethod("read", func(self Object, args Tuple, kwargs StringDict) (Object, error) {
26+
return self.(*File).Read(args, kwargs)
27+
}, 0, "read([size]) -> read at most size bytes, returned as a string.\n\nIf the size argument is negative or omitted, read until EOF is reached.\nNotice that when in non-blocking mode, less data than what was requested\nmay be returned, even if no size parameter was given.")
28+
FileType.Dict["close"] = MustNewMethod("close", func(self Object) (Object, error) {
29+
return self.(*File).Close()
30+
}, 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.")
31+
}
32+
33+
type FileMode int
34+
35+
const (
36+
FileRead FileMode = 0x01
37+
FileWrite FileMode = 0x02
38+
FileText FileMode = 0x4000
39+
FileBinary FileMode = 0x8000
40+
41+
FileReadWrite = FileRead + FileWrite
42+
)
43+
44+
type File struct {
45+
*os.File
46+
FileMode
47+
}
2048

2149
// Type of this object
2250
func (o *File) Type() *Type {
2351
return FileType
2452
}
2553

54+
func (o *File) Can(mode FileMode) bool {
55+
return o.FileMode&mode == mode
56+
}
57+
58+
func (o *File) Write(value Object) (Object, error) {
59+
var b []byte
60+
61+
switch v := value.(type) {
62+
// FIXME Bytearray
63+
case Bytes:
64+
b = v
65+
66+
case String:
67+
b = []byte(v)
68+
69+
default:
70+
return nil, ExceptionNewf(TypeError, "expected a string or other character buffer object")
71+
}
72+
73+
n, err := o.File.Write(b)
74+
return Int(n), err
75+
}
76+
77+
func (o *File) readResult(b []byte) (Object, error) {
78+
if o.Can(FileBinary) {
79+
if b != nil {
80+
return Bytes(b), nil
81+
}
82+
83+
return Bytes{}, nil
84+
}
85+
86+
if b != nil {
87+
return String(b), nil
88+
}
89+
90+
return String(""), nil
91+
}
92+
93+
func (o *File) Read(args Tuple, kwargs StringDict) (Object, error) {
94+
var arg Object = None
95+
96+
err := UnpackTuple(args, kwargs, "read", 0, 1, &arg)
97+
if err != nil {
98+
return nil, err
99+
}
100+
101+
var r io.Reader = o.File
102+
103+
switch pyN, ok := arg.(Int); {
104+
case arg == None:
105+
// read all
106+
107+
case ok:
108+
// number of bytes to read
109+
// 0: read nothing
110+
// < 0: read all
111+
// > 0: read n
112+
n, _ := pyN.GoInt64()
113+
if n == 0 {
114+
return o.readResult(nil)
115+
}
116+
if n > 0 {
117+
r = io.LimitReader(r, n)
118+
}
119+
120+
default:
121+
// invalid type
122+
return nil, ExceptionNewf(TypeError, "read() argument 1 must be int, not %s", arg.Type().Name)
123+
}
124+
125+
b, err := ioutil.ReadAll(r)
126+
if err == io.EOF {
127+
return o.readResult(nil)
128+
}
129+
if err != nil {
130+
return nil, err
131+
}
132+
133+
return o.readResult(b)
134+
}
135+
136+
func (o *File) Close() (Object, error) {
137+
_ = o.File.Close()
138+
return None, nil
139+
}
140+
141+
func OpenFile(filename, mode string, buffering int) (Object, error) {
142+
var fileMode FileMode
143+
var truncate bool
144+
var exclusive bool
145+
146+
for _, m := range mode {
147+
switch m {
148+
case 'r':
149+
if fileMode&FileReadWrite != 0 {
150+
return nil, ExceptionNewf(ValueError, "must have exactly one of create/read/write/append mode")
151+
}
152+
fileMode |= FileRead
153+
154+
case 'w':
155+
if fileMode&FileReadWrite != 0 {
156+
return nil, ExceptionNewf(ValueError, "must have exactly one of create/read/write/append mode")
157+
}
158+
fileMode |= FileWrite
159+
truncate = true
160+
161+
case 'x':
162+
if fileMode&FileReadWrite != 0 {
163+
return nil, ExceptionNewf(ValueError, "must have exactly one of create/read/write/append mode")
164+
}
165+
fileMode |= FileWrite
166+
exclusive = true
167+
168+
case 'a':
169+
if fileMode&FileReadWrite != 0 {
170+
return nil, ExceptionNewf(ValueError, "must have exactly one of create/read/write/append mode")
171+
}
172+
fileMode |= FileWrite
173+
truncate = false
174+
175+
case '+':
176+
if fileMode&FileReadWrite == 0 {
177+
return nil, ExceptionNewf(ValueError, "Must have exactly one of create/read/write/append mode and at most one plus")
178+
}
179+
180+
fileMode |= FileReadWrite
181+
truncate = false
182+
183+
case 'b':
184+
if fileMode&FileReadWrite == 0 {
185+
return nil, ExceptionNewf(ValueError, "Must have exactly one of create/read/write/append mode and at most one plus")
186+
}
187+
188+
if fileMode&FileText != 0 {
189+
return nil, ExceptionNewf(ValueError, "can't have text and binary mode at once")
190+
}
191+
192+
fileMode |= FileBinary
193+
194+
case 't':
195+
if fileMode&FileReadWrite == 0 {
196+
return nil, ExceptionNewf(ValueError, "Must have exactly one of create/read/write/append mode and at most one plus")
197+
}
198+
199+
if fileMode&FileBinary != 0 {
200+
return nil, ExceptionNewf(ValueError, "can't have text and binary mode at once")
201+
}
202+
203+
fileMode |= FileText
204+
}
205+
}
206+
207+
var fmode int
208+
209+
switch fileMode & FileReadWrite {
210+
case FileReadWrite:
211+
fmode = os.O_RDWR
212+
213+
case FileRead:
214+
fmode = os.O_RDONLY
215+
216+
case FileWrite:
217+
fmode = os.O_WRONLY
218+
}
219+
220+
if exclusive {
221+
fmode |= os.O_EXCL
222+
}
223+
224+
if truncate {
225+
fmode |= os.O_CREATE | os.O_TRUNC
226+
} else {
227+
fmode |= os.O_APPEND
228+
}
229+
230+
f, err := os.OpenFile(filename, fmode, 0666)
231+
if err != nil {
232+
// XXX: should check for different types of errors
233+
switch {
234+
case os.IsExist(err):
235+
return nil, ExceptionNewf(FileExistsError, err.Error())
236+
237+
case os.IsNotExist(err):
238+
return nil, ExceptionNewf(FileNotFoundError, err.Error())
239+
}
240+
}
241+
242+
if finfo, err := f.Stat(); err == nil {
243+
if finfo.IsDir() {
244+
f.Close()
245+
return nil, ExceptionNewf(IsADirectoryError, "Is a directory: '%s'", filename)
246+
}
247+
}
248+
249+
return &File{f, fileMode}, nil
250+
}
251+
26252
// Check interface is satisfied

0 commit comments

Comments
 (0)