github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/lib/os/__init__.py (about) 1 # Copyright 2016 Google Inc. All Rights Reserved. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 """Miscellaneous operating system interfaces.""" 16 17 # pylint: disable=g-multiple-import 18 from '__go__/io/ioutil' import ReadDir 19 from '__go__/os' import (Chdir, Chmod, Environ, Getpid as getpid, Getuid as getuid, 20 Geteuid as geteuid, Getgid as getgid, Getegid as getegid, Getwd, Pipe, 21 ProcAttr, Remove, StartProcess, Stat, Stdout, Stdin, 22 Stderr, Mkdir) 23 from '__go__/path/filepath' import Separator 24 from '__go__/grumpy' import (NewFileFromFD, StartThread, ToNative) 25 from '__go__/reflect' import MakeSlice 26 from '__go__/runtime' import GOOS 27 from '__go__/syscall' import (Close, SYS_FCNTL, Syscall, F_GETFD, Wait4, 28 WaitStatus, WNOHANG) 29 from '__go__/sync' import WaitGroup 30 from '__go__/time' import Second 31 import _syscall 32 from os import path 33 import stat as stat_module 34 import sys 35 36 37 sep = chr(Separator) 38 error = OSError # pylint: disable=invalid-name 39 curdir = '.' 40 name = 'posix' 41 42 43 environ = {} 44 for var in Environ(): 45 k, v = var.split('=', 1) 46 environ[k] = v 47 48 49 def mkdir(path, mode=0o777): 50 err = Mkdir(path, mode) 51 if err: 52 raise OSError(err.Error()) 53 54 55 def chdir(path): 56 err = Chdir(path) 57 if err: 58 raise OSError(err.Error()) 59 60 61 def fchdir(fd): 62 raise NotImplementedError("Code contributions to implement 'os.fchdir(fd)' are welcome") 63 64 65 def ctermid(): 66 raise NotImplementedError("Code contributions to implement 'os.ctermid()' are welcome") 67 68 69 def chmod(filepath, mode): 70 # TODO: Support mode flags other than perms. 71 err = Chmod(filepath, stat(filepath).st_mode & ~0o777 | mode & 0o777) 72 if err: 73 raise OSError(err.Error()) 74 75 76 def close(fd): 77 err = Close(fd) 78 if err: 79 raise OSError(err.Error()) 80 81 82 def fdopen(fd, mode='r'): # pylint: disable=unused-argument 83 # Ensure this is a valid file descriptor to match CPython behavior. 84 _, _, err = Syscall(SYS_FCNTL, fd, F_GETFD, 0) 85 if err: 86 raise OSError(err.Error()) 87 return NewFileFromFD(fd, None) 88 89 90 def listdir(p): 91 files, err = ReadDir(p) 92 if err: 93 raise OSError(err.Error()) 94 return [x.Name() for x in files] 95 96 97 def getcwd(): 98 dir, err = Getwd() 99 if err: 100 raise OSError(err.Error()) 101 return dir 102 103 104 class _Popen(object): 105 106 def __init__(self, command, mode): 107 self.mode = mode 108 self.result = None 109 self.r, self.w, err = Pipe() 110 if err: 111 raise OSError(err.Error()) 112 attr = ProcAttr.new() 113 # Create a slice using a reflect.Type returned by ToNative. 114 # TODO: There should be a cleaner way to create slices in Python. 115 files_type = ToNative(__frame__(), attr.Files).Type() 116 files = MakeSlice(files_type, 3, 3).Interface() 117 if self.mode == 'r': 118 fd = self.r.Fd() 119 files[0], files[1], files[2] = Stdin, self.w, Stderr 120 elif self.mode == 'w': 121 fd = self.w.Fd() 122 files[0], files[1], files[2] = self.r, Stdout, Stderr 123 else: 124 raise ValueError('invalid popen mode: %r', self.mode) 125 attr.Files = files 126 # TODO: There should be a cleaner way to create slices in Python. 127 args_type = ToNative(__frame__(), StartProcess).Type().In(1) 128 args = MakeSlice(args_type, 3, 3).Interface() 129 130 # 'shell' is different per environment 131 # See: https://github.com/python/cpython/blob/master/Lib/subprocess.py#L1375-L1378 132 if hasattr(sys, 'getandroidapilevel'): 133 unix_shell = '/system/bin/sh' 134 else: 135 unix_shell = '/bin/sh' 136 shell = environ.get('SHELL', unix_shell) 137 args[0] = shell 138 args[1] = '-c' 139 args[2] = command 140 self.proc, err = StartProcess(shell, args, attr) 141 if err: 142 raise OSError(err.Error()) 143 self.wg = WaitGroup.new() 144 self.wg.Add(1) 145 StartThread(self._thread_func) 146 self.file = NewFileFromFD(fd, self.close) 147 148 def _thread_func(self): 149 self.result = self.proc.Wait() 150 if self.mode == 'r': 151 self.w.Close() 152 self.wg.Done() 153 154 def close(self, _): 155 if self.mode == 'w': 156 self.w.Close() 157 self.wg.Wait() 158 state, err = self.result 159 if err: 160 raise OSError(err.Error()) 161 return state.Sys() 162 163 164 def popen(command, mode='r'): 165 return _Popen(command, mode).file 166 167 168 def remove(filepath): 169 if stat_module.S_ISDIR(stat(filepath).st_mode): 170 raise OSError('Operation not permitted: ' + filepath) 171 err = Remove(filepath) 172 if err: 173 raise OSError(err.Error()) 174 175 176 def rmdir(filepath): 177 if not stat_module.S_ISDIR(stat(filepath).st_mode): 178 raise OSError('Operation not permitted: ' + filepath) 179 err = Remove(filepath) 180 if err: 181 raise OSError(err.Error()) 182 183 184 class StatResult(object): 185 186 def __init__(self, info): 187 self._info = info 188 189 def st_mode(self): 190 # TODO: This is an incomplete mode flag. It should include S_IFDIR, etc. 191 return self._info.Mode() 192 # TODO: Make this a decorator once they're implemented. 193 st_mode = property(st_mode) 194 195 def st_mtime(self): 196 return float(self._info.ModTime().UnixNano()) / Second 197 # TODO: Make this a decorator once they're implemented. 198 st_mtime = property(st_mtime) 199 200 def st_size(self): 201 return self._info.Size() 202 # TODO: Make this a decorator once they're implemented. 203 st_size = property(st_size) 204 205 206 def stat(filepath): 207 info, err = Stat(filepath) 208 if err: 209 raise OSError(err.Error()) 210 return StatResult(info) 211 212 213 unlink = remove 214 215 216 def waitpid(pid, options): 217 status = WaitStatus.new() 218 _syscall.invoke(Wait4, pid, status, options, None) 219 return pid, _encode_wait_result(status) 220 221 222 def _encode_wait_result(status): 223 return status.Signal() | (status.ExitStatus() << 8)