Skip to content

Commit 3e1daa8

Browse files
committed
stdlib/tempfile: first import
Signed-off-by: Sebastien Binet <binet@cern.ch>
1 parent fda1751 commit 3e1daa8

File tree

5 files changed

+422
-0
lines changed

5 files changed

+422
-0
lines changed

stdlib/stdlib.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
_ "github.com/go-python/gpython/stdlib/os"
2626
_ "github.com/go-python/gpython/stdlib/string"
2727
_ "github.com/go-python/gpython/stdlib/sys"
28+
_ "github.com/go-python/gpython/stdlib/tempfile"
2829
_ "github.com/go-python/gpython/stdlib/time"
2930
)
3031

stdlib/tempfile/tempfile.go

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// Copyright 2022 The go-python Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package tempfile provides the implementation of the python's 'tempfile' module.
6+
package tempfile
7+
8+
import (
9+
"fmt"
10+
"os"
11+
12+
"github.com/go-python/gpython/py"
13+
)
14+
15+
var (
16+
gblTempDir py.Object = py.None
17+
)
18+
19+
const tempfile_doc = `Temporary files.
20+
21+
This module provides generic, low- and high-level interfaces for
22+
creating temporary files and directories. All of the interfaces
23+
provided by this module can be used without fear of race conditions
24+
except for 'mktemp'. 'mktemp' is subject to race conditions and
25+
should not be used; it is provided for backward compatibility only.
26+
27+
The default path names are returned as str. If you supply bytes as
28+
input, all return values will be in bytes. Ex:
29+
30+
>>> tempfile.mkstemp()
31+
(4, '/tmp/tmptpu9nin8')
32+
>>> tempfile.mkdtemp(suffix=b'')
33+
b'/tmp/tmppbi8f0hy'
34+
35+
This module also provides some data items to the user:
36+
37+
TMP_MAX - maximum number of names that will be tried before
38+
giving up.
39+
tempdir - If this is set to a string before the first use of
40+
any routine from this module, it will be considered as
41+
another candidate location to store temporary files.`
42+
43+
func init() {
44+
py.RegisterModule(&py.ModuleImpl{
45+
Info: py.ModuleInfo{
46+
Name: "tempfile",
47+
Doc: tempfile_doc,
48+
},
49+
Methods: []*py.Method{
50+
py.MustNewMethod("gettempdir", gettempdir, 0, gettempdir_doc),
51+
py.MustNewMethod("gettempdirb", gettempdirb, 0, gettempdirb_doc),
52+
py.MustNewMethod("mkdtemp", mkdtemp, 0, mkdtemp_doc),
53+
py.MustNewMethod("mkstemp", mkstemp, 0, mkstemp_doc),
54+
},
55+
Globals: py.StringDict{
56+
"tempdir": gblTempDir,
57+
},
58+
})
59+
}
60+
61+
const gettempdir_doc = `Returns tempfile.tempdir as str.`
62+
63+
func gettempdir(self py.Object) (py.Object, error) {
64+
// FIXME(sbinet): lock access to glbTempDir?
65+
if gblTempDir != py.None {
66+
switch dir := gblTempDir.(type) {
67+
case py.String:
68+
return dir, nil
69+
case py.Bytes:
70+
return py.String(dir), nil
71+
default:
72+
return nil, py.ExceptionNewf(py.TypeError, "expected str, bytes or os.PathLike object, not %s", dir.Type().Name)
73+
}
74+
}
75+
return py.String(os.TempDir()), nil
76+
}
77+
78+
const gettempdirb_doc = `Returns tempfile.tempdir as bytes.`
79+
80+
func gettempdirb(self py.Object) (py.Object, error) {
81+
// FIXME(sbinet): lock access to glbTempDir?
82+
if gblTempDir != py.None {
83+
switch dir := gblTempDir.(type) {
84+
case py.String:
85+
return py.Bytes(dir), nil
86+
case py.Bytes:
87+
return dir, nil
88+
default:
89+
return nil, py.ExceptionNewf(py.TypeError, "expected str, bytes or os.PathLike object, not %s", dir.Type().Name)
90+
}
91+
}
92+
return py.Bytes(os.TempDir()), nil
93+
}
94+
95+
const mkdtemp_doc = `mkdtemp(suffix=None, prefix=None, dir=None)
96+
User-callable function to create and return a unique temporary
97+
directory. The return value is the pathname of the directory.
98+
99+
Arguments are as for mkstemp, except that the 'text' argument is
100+
not accepted.
101+
102+
The directory is readable, writable, and searchable only by the
103+
creating user.
104+
105+
Caller is responsible for deleting the directory when done with it.`
106+
107+
func mkdtemp(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
108+
var (
109+
pysuffix py.Object = py.None
110+
pyprefix py.Object = py.None
111+
pydir py.Object = py.None
112+
)
113+
err := py.ParseTupleAndKeywords(args, kwargs,
114+
"|z#z#z#:mkdtemp",
115+
[]string{"suffix", "prefix", "dir"},
116+
&pysuffix, &pyprefix, &pydir,
117+
)
118+
if err != nil {
119+
return nil, err
120+
}
121+
122+
str := func(v py.Object, typ *uint8) string {
123+
switch v := v.(type) {
124+
case py.Bytes:
125+
*typ = 2
126+
return string(v)
127+
case py.String:
128+
*typ = 1
129+
return string(v)
130+
case py.NoneType:
131+
*typ = 0
132+
return ""
133+
default:
134+
panic(fmt.Errorf("tempfile: invalid type %T (v=%+v)", v, v))
135+
}
136+
}
137+
138+
var (
139+
t1, t2, t3 uint8
140+
141+
suffix = str(pysuffix, &t1)
142+
prefix = str(pyprefix, &t2)
143+
dir = str(pydir, &t3)
144+
pattern = prefix + "*" + suffix
145+
)
146+
147+
cmp := func(t1, t2 uint8) bool {
148+
if t1 > 0 && t2 > 0 {
149+
return t1 == t2
150+
}
151+
return true
152+
}
153+
154+
if !cmp(t1, t2) || !cmp(t1, t3) || !cmp(t2, t3) {
155+
return nil, py.ExceptionNewf(py.TypeError, "Can't mix bytes and non-bytes in path components")
156+
}
157+
158+
tmp, err := os.MkdirTemp(dir, pattern)
159+
if err != nil {
160+
return nil, err
161+
}
162+
163+
typ := t1
164+
if typ == 0 {
165+
typ = t2
166+
}
167+
if typ == 0 {
168+
typ = t3
169+
}
170+
171+
switch typ {
172+
case 2:
173+
return py.Bytes(tmp), nil
174+
default:
175+
return py.String(tmp), nil
176+
}
177+
}
178+
179+
const mkstemp_doc = `mkstemp(suffix=None, prefix=None, dir=None, text=False)
180+
181+
User-callable function to create and return a unique temporary
182+
file. The return value is a pair (fd, name) where fd is the
183+
file descriptor returned by os.open, and name is the filename.
184+
185+
If 'suffix' is not None, the file name will end with that suffix,
186+
otherwise there will be no suffix.
187+
188+
If 'prefix' is not None, the file name will begin with that prefix,
189+
otherwise a default prefix is used.
190+
191+
If 'dir' is not None, the file will be created in that directory,
192+
otherwise a default directory is used.
193+
194+
If 'text' is specified and true, the file is opened in text
195+
mode. Else (the default) the file is opened in binary mode.
196+
197+
If any of 'suffix', 'prefix' and 'dir' are not None, they must be the
198+
same type. If they are bytes, the returned name will be bytes; str
199+
otherwise.
200+
201+
The file is readable and writable only by the creating user ID.
202+
If the operating system uses permission bits to indicate whether a
203+
file is executable, the file is executable by no one. The file
204+
descriptor is not inherited by children of this process.
205+
206+
Caller is responsible for deleting the file when done with it.`
207+
208+
func mkstemp(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
209+
var (
210+
pysuffix py.Object = py.None
211+
pyprefix py.Object = py.None
212+
pydir py.Object = py.None
213+
pytext py.Object = py.False // FIXME(sbinet): can we do something with that?
214+
)
215+
216+
err := py.ParseTupleAndKeywords(args, kwargs,
217+
"|z#z#z#p:mkstemp",
218+
[]string{"suffix", "prefix", "dir", "text"},
219+
&pysuffix, &pyprefix, &pydir, &pytext,
220+
)
221+
if err != nil {
222+
return nil, err
223+
}
224+
225+
str := func(v py.Object, typ *uint8) string {
226+
switch v := v.(type) {
227+
case py.Bytes:
228+
*typ = 2
229+
return string(v)
230+
case py.String:
231+
*typ = 1
232+
return string(v)
233+
case py.NoneType:
234+
*typ = 0
235+
return ""
236+
default:
237+
panic(fmt.Errorf("tempfile: invalid type %T (v=%+v)", v, v))
238+
}
239+
}
240+
241+
var (
242+
t1, t2, t3 uint8
243+
244+
suffix = str(pysuffix, &t1)
245+
prefix = str(pyprefix, &t2)
246+
dir = str(pydir, &t3)
247+
pattern = prefix + "*" + suffix
248+
)
249+
250+
cmp := func(t1, t2 uint8) bool {
251+
if t1 > 0 && t2 > 0 {
252+
return t1 == t2
253+
}
254+
return true
255+
}
256+
257+
if !cmp(t1, t2) || !cmp(t1, t3) || !cmp(t2, t3) {
258+
return nil, py.ExceptionNewf(py.TypeError, "Can't mix bytes and non-bytes in path components")
259+
}
260+
261+
f, err := os.CreateTemp(dir, pattern)
262+
if err != nil {
263+
return nil, err
264+
}
265+
266+
typ := t1
267+
if typ == 0 {
268+
typ = t2
269+
}
270+
if typ == 0 {
271+
typ = t3
272+
}
273+
274+
tuple := py.Tuple{py.Int(f.Fd())}
275+
switch typ {
276+
case 2:
277+
tuple = append(tuple, py.Bytes(f.Name()))
278+
default:
279+
tuple = append(tuple, py.String(f.Name()))
280+
}
281+
282+
return tuple, nil
283+
}

stdlib/tempfile/tempfile_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2022 The go-python Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package tempfile_test
6+
7+
import (
8+
"testing"
9+
10+
"github.com/go-python/gpython/pytest"
11+
)
12+
13+
func TestTempfile(t *testing.T) {
14+
pytest.RunScript(t, "./testdata/test.py")
15+
}

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