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)