Skip to content

Commit b08271e

Browse files
committed
stdlib/os: first import
Signed-off-by: Sebastien Binet <[email protected]>
1 parent 433aea8 commit b08271e

File tree

5 files changed

+392
-0
lines changed

5 files changed

+392
-0
lines changed

Diff for: stdlib/os/os.go

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
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 os implements the Python os module.
6+
package os
7+
8+
import (
9+
"os"
10+
"os/exec"
11+
"runtime"
12+
"strings"
13+
14+
"github.com/go-python/gpython/py"
15+
)
16+
17+
var (
18+
osSep = py.String("/")
19+
osName = py.String("posix")
20+
osPathsep = py.String(":")
21+
osLinesep = py.String("\n")
22+
osDefpath = py.String(":/bin:/usr/bin")
23+
osDevnull = py.String("/dev/null")
24+
25+
osAltsep py.Object = py.None
26+
)
27+
28+
func initGlobals() {
29+
switch runtime.GOOS {
30+
case "android":
31+
osName = py.String("java")
32+
case "windows":
33+
osSep = py.String(`\`)
34+
osName = py.String("nt")
35+
osPathsep = py.String(";")
36+
osLinesep = py.String("\r\n")
37+
osDefpath = py.String(`C:\bin`)
38+
osDevnull = py.String("nul")
39+
osAltsep = py.String("/")
40+
}
41+
}
42+
43+
func init() {
44+
initGlobals()
45+
46+
methods := []*py.Method{
47+
py.MustNewMethod("getcwd", getCwd, 0, "Get the current working directory"),
48+
py.MustNewMethod("getcwdb", getCwdb, 0, "Get the current working directory in a byte slice"),
49+
py.MustNewMethod("chdir", chdir, 0, "Change the current working directory"),
50+
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."),
51+
py.MustNewMethod("getpid", getpid, 0, "Return the current process id."),
52+
py.MustNewMethod("putenv", putenv, 0, "Set the environment variable named key to the string value."),
53+
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"),
56+
}
57+
globals := py.StringDict{
58+
"error": py.OSError,
59+
"environ": getEnvVariables(),
60+
"sep": osSep,
61+
"name": osName,
62+
"curdir": py.String("."),
63+
"pardir": py.String(".."),
64+
"extsep": py.String("."),
65+
"altsep": osAltsep,
66+
"pathsep": osPathsep,
67+
"linesep": osLinesep,
68+
"defpath": osDefpath,
69+
"devnull": osDevnull,
70+
}
71+
72+
py.RegisterModule(&py.ModuleImpl{
73+
Info: py.ModuleInfo{
74+
Name: "os",
75+
Doc: "Miscellaneous operating system interfaces",
76+
},
77+
Methods: methods,
78+
Globals: globals,
79+
})
80+
}
81+
82+
// getEnvVariables returns the dictionary of environment variables.
83+
func getEnvVariables() py.StringDict {
84+
vs := os.Environ()
85+
dict := py.NewStringDictSized(len(vs))
86+
for _, evar := range vs {
87+
key_value := strings.SplitN(evar, "=", 2) // returns a []string containing [key,value]
88+
dict.M__setitem__(py.String(key_value[0]), py.String(key_value[1]))
89+
}
90+
91+
return dict
92+
}
93+
94+
// getCwd returns the current working directory.
95+
func getCwd(self py.Object, args py.Tuple) (py.Object, error) {
96+
dir, err := os.Getwd()
97+
if err != nil {
98+
return nil, py.ExceptionNewf(py.OSError, "Unable to get current working directory.")
99+
}
100+
return py.String(dir), nil
101+
}
102+
103+
// getCwdb returns the current working directory as a byte list.
104+
func getCwdb(self py.Object, args py.Tuple) (py.Object, error) {
105+
dir, err := os.Getwd()
106+
if err != nil {
107+
return nil, py.ExceptionNewf(py.OSError, "Unable to get current working directory.")
108+
}
109+
return py.Bytes(dir), nil
110+
}
111+
112+
// chdir changes the current working directory to the provided path.
113+
func chdir(self py.Object, args py.Tuple) (py.Object, error) {
114+
if len(args) == 0 {
115+
return nil, py.ExceptionNewf(py.TypeError, "Missing required argument 'path' (pos 1)")
116+
}
117+
dir, ok := args[0].(py.String)
118+
if !ok {
119+
return nil, py.ExceptionNewf(py.TypeError, "str expected, not "+args[0].Type().Name)
120+
}
121+
err := os.Chdir(string(dir))
122+
if err != nil {
123+
return nil, py.ExceptionNewf(py.NotADirectoryError, "Couldn't change cwd; "+err.Error())
124+
}
125+
return py.None, nil
126+
}
127+
128+
// getenv returns the value of the environment variable key.
129+
// If no such environment variable exists and a default value was provided, that value is returned.
130+
func getenv(self py.Object, args py.Tuple) (py.Object, error) {
131+
if len(args) < 1 {
132+
return nil, py.ExceptionNewf(py.TypeError, "missing one required argument: 'name:str'")
133+
}
134+
k, ok := args[0].(py.String)
135+
if !ok {
136+
return nil, py.ExceptionNewf(py.TypeError, "str expected (pos 1), not "+args[0].Type().Name)
137+
}
138+
v, ok := os.LookupEnv(string(k))
139+
if ok {
140+
return py.String(v), nil
141+
}
142+
if len(args) == 2 {
143+
return args[1], nil
144+
}
145+
return py.None, nil
146+
}
147+
148+
// getpid returns the current process id.
149+
func getpid(self py.Object, args py.Tuple) (py.Object, error) {
150+
return py.Int(os.Getpid()), nil
151+
}
152+
153+
// putenv sets the value of an environment variable named by the key.
154+
func putenv(self py.Object, args py.Tuple) (py.Object, error) {
155+
if len(args) != 2 {
156+
return nil, py.ExceptionNewf(py.TypeError, "missing required arguments: 'key:str' and 'value:str'")
157+
}
158+
k, ok := args[0].(py.String)
159+
if !ok {
160+
return nil, py.ExceptionNewf(py.TypeError, "str expected (pos 1), not "+args[0].Type().Name)
161+
}
162+
v, ok := args[1].(py.String)
163+
if !ok {
164+
return nil, py.ExceptionNewf(py.TypeError, "str expected (pos 2), not "+args[1].Type().Name)
165+
}
166+
err := os.Setenv(string(k), string(v))
167+
if err != nil {
168+
return nil, py.ExceptionNewf(py.OSError, "Unable to set enviroment variable")
169+
}
170+
return py.None, nil
171+
}
172+
173+
// Unset (delete) the environment variable named key.
174+
func unsetenv(self py.Object, args py.Tuple) (py.Object, error) {
175+
if len(args) != 1 {
176+
return nil, py.ExceptionNewf(py.TypeError, "missing one required argument: 'key:str'")
177+
}
178+
k, ok := args[0].(py.String)
179+
if !ok {
180+
return nil, py.ExceptionNewf(py.TypeError, "str expected (pos 1), not "+args[0].Type().Name)
181+
}
182+
err := os.Unsetenv(string(k))
183+
if err != nil {
184+
return nil, py.ExceptionNewf(py.OSError, "Unable to unset enviroment variable")
185+
}
186+
return py.None, nil
187+
}
188+
189+
// os._exit() immediate program termination; unlike sys.exit(), which raises a SystemExit, this function will termninate the program immediately.
190+
func _exit(self py.Object, args py.Tuple) (py.Object, error) { // can never return
191+
if len(args) == 0 {
192+
os.Exit(0)
193+
}
194+
arg, ok := args[0].(py.Int)
195+
if !ok {
196+
return nil, py.ExceptionNewf(py.TypeError, "expected int (pos 1), not "+args[0].Type().Name)
197+
}
198+
os.Exit(int(arg))
199+
return nil, nil
200+
}
201+
202+
// os.system(command string) this function runs a shell command and directs the output to standard output.
203+
func system(self py.Object, args py.Tuple) (py.Object, error) {
204+
if len(args) != 1 {
205+
return nil, py.ExceptionNewf(py.TypeError, "missing one required argument: 'command:str'")
206+
}
207+
arg, ok := args[0].(py.String)
208+
if !ok {
209+
return nil, py.ExceptionNewf(py.TypeError, "str expected (pos 1), not "+args[0].Type().Name)
210+
}
211+
212+
var command *exec.Cmd
213+
if runtime.GOOS != "windows" {
214+
command = exec.Command("/bin/sh", "-c", string(arg))
215+
} else {
216+
command = exec.Command("cmd.exe", string(arg))
217+
}
218+
outb, err := command.CombinedOutput() // - commbinedoutput to get both stderr and stdout -
219+
if err != nil {
220+
return nil, py.ExceptionNewf(py.OSError, err.Error())
221+
}
222+
ok = py.Println(self, string(outb))
223+
if !ok {
224+
return py.Int(1), nil
225+
}
226+
227+
return py.Int(0), nil
228+
}

Diff for: stdlib/os/os_test.go

+15
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 os_test
6+
7+
import (
8+
"testing"
9+
10+
"github.com/go-python/gpython/pytest"
11+
)
12+
13+
func TestOs(t *testing.T) {
14+
pytest.RunScript(t, "./testdata/test.py")
15+
}

Diff for: stdlib/os/testdata/test.py

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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+
import os
6+
7+
print("test os")
8+
print("os.error: ", os.error)
9+
print("os.getenv($GPYTHON_TEST_HOME)=", os.getenv("GPYTHON_TEST_HOME"))
10+
os.putenv("GPYTHON_TEST_HOME", "/home/go")
11+
print("os.environ($GPYTHON_TEST_HOME)=", os.environ.get("GPYTHON_TEST_HOME"))
12+
print("os.getenv($GPYTHON_TEST_HOME)=", os.getenv("GPYTHON_TEST_HOME"))
13+
os.unsetenv("GPYTHON_TEST_HOME")
14+
print("os.unsetenv($GPYTHON_TEST_HOME)=", os.getenv("GPYTHON_TEST_HOME"))
15+
16+
if not os.error is OSError:
17+
print("os.error is not OSError!")
18+
else:
19+
print("os.error is OSError [OK]")
20+
21+
## FIXME(sbinet): check returned value with a known one
22+
## (ie: when os.mkdir is implemented)
23+
if os.getcwd() == None:
24+
print("os.getcwd() == None !")
25+
else:
26+
print("os.getcwd() != None [OK]")
27+
28+
## FIXME(sbinet): check returned value with a known one
29+
## (ie: when os.mkdir is implemented)
30+
if os.getcwdb() == None:
31+
print("os.getcwdb() == None !")
32+
else:
33+
print("os.getcwdb() != None [OK]")
34+
35+
print("os.system('echo hello')...")
36+
if os.name != "nt":
37+
os.system('echo hello')
38+
else: ## FIXME(sbinet): find a way to test this nicely
39+
print("hello\n")
40+
41+
if os.getpid() > 1:
42+
print("os.getpid is greater than 1 [OK]")
43+
else:
44+
print("invalid os.getpid: ", os.getpid())
45+
46+
orig = os.getcwd()
47+
testdir = "/"
48+
if os.name == "nt":
49+
testdir = "C:\\"
50+
os.chdir(testdir)
51+
if os.getcwd() != testdir:
52+
print("invalid getcwd() after os.chdir:",os.getcwd())
53+
else:
54+
print("os.chdir(testdir) [OK]")
55+
os.chdir(orig)
56+
57+
try:
58+
os.chdir(1)
59+
print("expected an error with os.chdir(1)")
60+
except TypeError:
61+
print("os.chdir(1) failed [OK]")
62+
63+
try:
64+
os.environ.get(15)
65+
print("expected an error with os.environ.get(15)")
66+
except KeyError:
67+
print("os.environ.get(15) failed [OK]")
68+
69+
try:
70+
os.putenv()
71+
print("expected an error with os.putenv()")
72+
except TypeError:
73+
print("os.putenv() failed [OK]")
74+
75+
try:
76+
os.unsetenv()
77+
print("expected an error with os.unsetenv()")
78+
except TypeError:
79+
print("os.unsetenv() failed [OK]")
80+
81+
try:
82+
os.getenv()
83+
print("expected an error with os.getenv()")
84+
except TypeError:
85+
print("os.getenv() failed [OK]")
86+
87+
try:
88+
os.unsetenv("FOO", "BAR")
89+
print("expected an error with os.unsetenv(\"FOO\", \"BAR\")")
90+
except TypeError:
91+
print("os.unsetenv(\"FOO\", \"BAR\") failed [OK]")
92+
93+
if bytes(os.getcwd(), "utf-8") == os.getcwdb():
94+
print('bytes(os.getcwd(), "utf-8") == os.getcwdb() [OK]')
95+
else:
96+
print('expected: bytes(os.getcwd(), "utf-8") == os.getcwdb()')
97+
98+
golden = {
99+
"posix": {
100+
"sep": "/",
101+
"pathsep": ":",
102+
"linesep": "\n",
103+
"devnull": "/dev/null",
104+
"altsep": None
105+
},
106+
"nt": {
107+
"sep": "\\",
108+
"pathsep": ";",
109+
"linesep": "\r\n",
110+
"devnull": "nul",
111+
"altsep": "/"
112+
},
113+
}[os.name]
114+
115+
for k in ("sep", "pathsep", "linesep", "devnull", "altsep"):
116+
if getattr(os, k) != golden[k]:
117+
print("invalid os."+k+": got=",getattr(os,k),", want=", golden[k])
118+
else:
119+
print("os."+k+": [OK]")
120+
121+
print("OK")

Diff for: stdlib/os/testdata/test_golden.txt

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
test os
2+
os.error: <class 'OSError'>
3+
os.getenv($GPYTHON_TEST_HOME)= None
4+
os.environ($GPYTHON_TEST_HOME)= None
5+
os.getenv($GPYTHON_TEST_HOME)= /home/go
6+
os.unsetenv($GPYTHON_TEST_HOME)= None
7+
os.error is OSError [OK]
8+
os.getcwd() != None [OK]
9+
os.getcwdb() != None [OK]
10+
os.system('echo hello')...
11+
hello
12+
13+
os.getpid is greater than 1 [OK]
14+
os.chdir(testdir) [OK]
15+
os.chdir(1) failed [OK]
16+
os.environ.get(15) failed [OK]
17+
os.putenv() failed [OK]
18+
os.unsetenv() failed [OK]
19+
os.getenv() failed [OK]
20+
os.unsetenv("FOO", "BAR") failed [OK]
21+
bytes(os.getcwd(), "utf-8") == os.getcwdb() [OK]
22+
os.sep: [OK]
23+
os.pathsep: [OK]
24+
os.linesep: [OK]
25+
os.devnull: [OK]
26+
os.altsep: [OK]
27+
OK

0 commit comments

Comments
 (0)