Skip to content

Commit 9f92dd7

Browse files
committed
Initial work at implementing file methods:
- open (builtin) - File.read - File.write - File.close
1 parent 09f14d0 commit 9f92dd7

File tree

7 files changed

+354
-4
lines changed

7 files changed

+354
-4
lines changed

builtin/builtin.go

Lines changed: 63 additions & 0 deletions
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, "errors 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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 11 additions & 0 deletions
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

Lines changed: 229 additions & 3 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy