Skip to content

Commit 795df15

Browse files
committed
stdlib/os: add mkdir, makedirs, remove, removedirs and rmdir
Signed-off-by: Sebastien Binet <binet@cern.ch>
1 parent 4b114ff commit 795df15

File tree

3 files changed

+267
-2
lines changed

3 files changed

+267
-2
lines changed

stdlib/os/os.go

Lines changed: 214 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,20 @@ func init() {
4444
initGlobals()
4545

4646
methods := []*py.Method{
47+
py.MustNewMethod("_exit", _exit, 0, "Immediate program termination."),
4748
py.MustNewMethod("getcwd", getCwd, 0, "Get the current working directory"),
4849
py.MustNewMethod("getcwdb", getCwdb, 0, "Get the current working directory in a byte slice"),
4950
py.MustNewMethod("chdir", chdir, 0, "Change the current working directory"),
5051
py.MustNewMethod("getenv", getenv, 0, "Return the value of the environment variable key if it exists, or default if it doesn’t. key, default and the result are str."),
5152
py.MustNewMethod("getpid", getpid, 0, "Return the current process id."),
53+
py.MustNewMethod("makedirs", makedirs, 0, makedirs_doc),
54+
py.MustNewMethod("mkdir", mkdir, 0, mkdir_doc),
5255
py.MustNewMethod("putenv", putenv, 0, "Set the environment variable named key to the string value."),
56+
py.MustNewMethod("remove", remove, 0, remove_doc),
57+
py.MustNewMethod("removedirs", removedirs, 0, removedirs_doc),
58+
py.MustNewMethod("rmdir", rmdir, 0, rmdir_doc),
59+
py.MustNewMethod("system", system, 0, "Run shell commands, prints stdout directly to default"),
5360
py.MustNewMethod("unsetenv", unsetenv, 0, "Unset (delete) the environment variable named key."),
54-
py.MustNewMethod("_exit", _exit, 0, "Immediate program termination."),
55-
py.MustNewMethod("system", system, 0, "Run shell commands, prints stdout directly to deault"),
5661
}
5762
globals := py.StringDict{
5863
"error": py.OSError,
@@ -150,6 +155,105 @@ func getpid(self py.Object, args py.Tuple) (py.Object, error) {
150155
return py.Int(os.Getpid()), nil
151156
}
152157

158+
const makedirs_doc = `makedirs(name [, mode=0o777][, exist_ok=False])
159+
160+
Super-mkdir; create a leaf directory and all intermediate ones. Works like
161+
mkdir, except that any intermediate path segment (not just the rightmost)
162+
will be created if it does not exist. If the target directory already
163+
exists, raise an OSError if exist_ok is False. Otherwise no exception is
164+
raised. This is recursive.`
165+
166+
func makedirs(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
167+
var (
168+
pypath py.Object
169+
pymode py.Object = py.Int(0o777)
170+
pyok py.Object = py.False
171+
)
172+
err := py.ParseTupleAndKeywords(
173+
args, kwargs,
174+
"s#|ip:makedirs", []string{"path", "mode", "exist_ok"},
175+
&pypath, &pymode, &pyok,
176+
)
177+
if err != nil {
178+
return nil, err
179+
}
180+
181+
var (
182+
path = ""
183+
mode = os.FileMode(pymode.(py.Int))
184+
)
185+
switch v := pypath.(type) {
186+
case py.String:
187+
path = string(v)
188+
case py.Bytes:
189+
path = string(v)
190+
}
191+
192+
if pyok.(py.Bool) == py.False {
193+
// check if leaf exists.
194+
_, err := os.Stat(path)
195+
// FIXME(sbinet): handle other errors.
196+
if err == nil {
197+
return nil, py.ExceptionNewf(py.FileExistsError, "File exists: '%s'", path)
198+
}
199+
}
200+
201+
err = os.MkdirAll(path, mode)
202+
if err != nil {
203+
return nil, err
204+
}
205+
206+
return py.None, nil
207+
}
208+
209+
const mkdir_doc = `Create a directory.
210+
211+
If dir_fd is not None, it should be a file descriptor open to a directory,
212+
and path should be relative; path will then be relative to that directory.
213+
dir_fd may not be implemented on your platform.
214+
If it is unavailable, using it will raise a NotImplementedError.
215+
216+
The mode argument is ignored on Windows.`
217+
218+
func mkdir(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
219+
var (
220+
pypath py.Object
221+
pymode py.Object = py.Int(511)
222+
pydirfd py.Object = py.None
223+
)
224+
err := py.ParseTupleAndKeywords(
225+
args, kwargs,
226+
"s#|ii:mkdir", []string{"path", "mode", "dir_fd"},
227+
&pypath, &pymode, &pydirfd,
228+
)
229+
if err != nil {
230+
return nil, err
231+
}
232+
233+
var (
234+
path = ""
235+
mode = os.FileMode(pymode.(py.Int))
236+
)
237+
switch v := pypath.(type) {
238+
case py.String:
239+
path = string(v)
240+
case py.Bytes:
241+
path = string(v)
242+
}
243+
244+
if pydirfd != py.None {
245+
// FIXME(sbinet)
246+
return nil, py.ExceptionNewf(py.NotImplementedError, "mkdir(dir_fd=XXX) not implemented")
247+
}
248+
249+
err = os.Mkdir(path, mode)
250+
if err != nil {
251+
return nil, err
252+
}
253+
254+
return py.None, nil
255+
}
256+
153257
// putenv sets the value of an environment variable named by the key.
154258
func putenv(self py.Object, args py.Tuple) (py.Object, error) {
155259
if len(args) != 2 {
@@ -199,6 +303,114 @@ func _exit(self py.Object, args py.Tuple) (py.Object, error) { // can never retu
199303
return nil, nil
200304
}
201305

306+
const remove_doc = `Remove a file (same as unlink()).
307+
308+
If dir_fd is not None, it should be a file descriptor open to a directory,
309+
and path should be relative; path will then be relative to that directory.
310+
dir_fd may not be implemented on your platform.
311+
If it is unavailable, using it will raise a NotImplementedError.`
312+
313+
func remove(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
314+
var (
315+
pypath py.Object
316+
pydir py.Object = py.None
317+
)
318+
err := py.ParseTupleAndKeywords(args, kwargs, "s#|i:remove", []string{"path", "dir_fd"}, &pypath, &pydir)
319+
if err != nil {
320+
return nil, err
321+
}
322+
323+
if pydir != py.None {
324+
// FIXME(sbinet) ?
325+
return nil, py.ExceptionNewf(py.NotImplementedError, "remove(dir_fd=XXX) not implemented")
326+
}
327+
328+
var name string
329+
switch v := pypath.(type) {
330+
case py.String:
331+
name = string(v)
332+
case py.Bytes:
333+
name = string(v)
334+
}
335+
336+
err = os.Remove(name)
337+
if err != nil {
338+
return nil, err
339+
}
340+
341+
return py.None, nil
342+
}
343+
344+
const removedirs_doc = `removedirs(name)
345+
346+
Super-rmdir; remove a leaf directory and all empty intermediate
347+
ones. Works like rmdir except that, if the leaf directory is
348+
successfully removed, directories corresponding to rightmost path
349+
segments will be pruned away until either the whole path is
350+
consumed or an error occurs. Errors during this latter phase are
351+
ignored -- they generally mean that a directory was not empty.`
352+
353+
func removedirs(self py.Object, args py.Tuple) (py.Object, error) {
354+
var pypath py.Object
355+
err := py.ParseTuple(args, "s#:rmdir", &pypath)
356+
if err != nil {
357+
return nil, err
358+
}
359+
360+
var name string
361+
switch v := pypath.(type) {
362+
case py.String:
363+
name = string(v)
364+
case py.Bytes:
365+
name = string(v)
366+
}
367+
368+
err = os.RemoveAll(name)
369+
if err != nil {
370+
return nil, err
371+
}
372+
373+
return py.None, nil
374+
}
375+
376+
const rmdir_doc = `Remove a directory.
377+
378+
If dir_fd is not None, it should be a file descriptor open to a directory,
379+
and path should be relative; path will then be relative to that directory.
380+
dir_fd may not be implemented on your platform.
381+
If it is unavailable, using it will raise a NotImplementedError.`
382+
383+
func rmdir(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
384+
var (
385+
pypath py.Object
386+
pydir py.Object = py.None
387+
)
388+
err := py.ParseTupleAndKeywords(args, kwargs, "s#|i:rmdir", []string{"path", "dir_fd"}, &pypath, &pydir)
389+
if err != nil {
390+
return nil, err
391+
}
392+
393+
if pydir != py.None {
394+
// FIXME(sbinet) ?
395+
return nil, py.ExceptionNewf(py.NotImplementedError, "rmdir(dir_fd=XXX) not implemented")
396+
}
397+
398+
var name string
399+
switch v := pypath.(type) {
400+
case py.String:
401+
name = string(v)
402+
case py.Bytes:
403+
name = string(v)
404+
}
405+
406+
err = os.Remove(name)
407+
if err != nil {
408+
return nil, err
409+
}
410+
411+
return py.None, nil
412+
}
413+
202414
// os.system(command string) this function runs a shell command and directs the output to standard output.
203415
func system(self py.Object, args py.Tuple) (py.Object, error) {
204416
if len(args) != 1 {

stdlib/os/testdata/test.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,53 @@
118118
else:
119119
print("os."+k+": [OK]")
120120

121+
## mkdir,rmdir,remove,removedirs
122+
import tempfile
123+
try:
124+
top = tempfile.mkdtemp(prefix="gpython-os-test-")
125+
dir1 = top + os.sep + "dir1"
126+
dir2 = top + os.sep + "dir2"
127+
dir11 = top + os.sep + "dir1" + os.sep + "dir11"
128+
fname = dir2 + os.sep + "foo.txt"
129+
os.mkdir(dir1)
130+
os.rmdir(dir1)
131+
os.mkdir(dir1)
132+
os.mkdir(dir2)
133+
os.mkdir(dir11)
134+
os.removedirs(dir1)
135+
try:
136+
os.mkdir(dir11)
137+
print("creating nested dirs with os.mkdir should have failed")
138+
except SystemError as e:
139+
print("caught: SystemError - no such file or directory [OK]")
140+
except Exception as e:
141+
print("caught: %s" % e)
142+
143+
os.makedirs(dir11)
144+
try:
145+
os.makedirs(dir11)
146+
print("creating already existing dirs should have failed")
147+
except FileExistsError as e:
148+
print("caught: FileExistsError [OK]")
149+
except Exception as e:
150+
print("INVALID error caught: %s" % e)
151+
os.makedirs(dir11, exist_ok=True)
152+
153+
with open(fname, "w+") as f:
154+
pass
155+
try:
156+
os.rmdir(dir2)
157+
print("removing a non-empty directory should have failed")
158+
except SystemError as e:
159+
print("caught: SystemError - directory not empty [OK]")
160+
except Exception as e:
161+
print("INVALID error caught: %s" % e)
162+
os.remove(fname)
163+
os.rmdir(dir2)
164+
except Exception as e:
165+
print("could not create/remove directories: %s" % e)
166+
finally:
167+
os.removedirs(top)
168+
print("os.{mkdir,rmdir,remove,removedirs} worked as expected")
169+
121170
print("OK")

stdlib/os/testdata/test_golden.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@ os.pathsep: [OK]
2424
os.linesep: [OK]
2525
os.devnull: [OK]
2626
os.altsep: [OK]
27+
caught: SystemError - no such file or directory [OK]
28+
caught: FileExistsError [OK]
29+
caught: SystemError - directory not empty [OK]
30+
os.{mkdir,rmdir,remove,removedirs} worked as expected
2731
OK

0 commit comments

Comments
 (0)