OILS / core / pyutil.py View on Github | oils.pub

222 lines, 105 significant
1"""pyutil.py: Code that's only needed in Python."""
2from __future__ import print_function
3
4import sys
5import zipimport # NOT the zipfile module.
6
7from mycpp import mylib
8from pgen2 import grammar
9from pylib import os_path
10
11import posix_ as posix
12
13from typing import List, Union
14
15# Copied from 'string' module
16_PUNCT = """!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
17
18
19def nan():
20 # type: () -> float
21
22 # note: Python 3 has math.nan
23 return float('nan')
24
25
26def infinity():
27 # type: () -> float
28
29 # note: Python 3 has math.inf
30 return float('inf')
31
32
33def IsValidCharEscape(ch):
34 # type: (str) -> bool
35 """Is this a valid character escape when unquoted?"""
36 # These punctuation chars never needs to be escaped. (Note that _ is a
37 # keyword sometimes.)
38 if ch == '/' or ch == '.' or ch == '-':
39 return False
40
41 if ch == ' ': # foo\ bar is idiomatic
42 return True
43
44 # Borderline: ^ and %. But ^ is used for history?
45 # ! is an operator. And used for history.
46
47 # What about ^(...) or %(...) or /(...) .(1+2), etc.?
48
49 return ch in _PUNCT # like ispunct() in C
50
51
52def ChArrayToString(ch_array):
53 # type: (List[int]) -> str
54 """We avoid allocating 1 byte string objects in the C++ implementation.
55
56 'ch' is short for an integer that represents a character.
57 """
58 return ''.join(chr(ch) for ch in ch_array)
59
60
61def BackslashEscape(s, meta_chars):
62 # type: (str, str) -> str
63 """Escaped certain characters with backslashes.
64
65 Used for shell syntax (i.e. quoting completed filenames), globs, and
66 EREs.
67 """
68 escaped = []
69 for c in s:
70 if c in meta_chars:
71 escaped.append('\\')
72 escaped.append(c)
73 return ''.join(escaped)
74
75
76def strerror(e):
77 # type: (Union[IOError, OSError]) -> str
78 return posix.strerror(e.errno)
79
80
81def LoadYshGrammar(loader):
82 # type: (_ResourceLoader) -> grammar.Grammar
83 g = grammar.Grammar()
84 contents = loader.Get('_devbuild/gen/grammar.marshal')
85 g.loads(contents)
86 return g
87
88
89class _ResourceLoader(object):
90
91 def Get(self, rel_path):
92 # type: (str) -> str
93 raise NotImplementedError()
94
95
96class _FileResourceLoader(_ResourceLoader):
97 """Open resources relative to argv[0]."""
98
99 def __init__(self, root_dir):
100 # type: (str) -> None
101 self.root_dir = root_dir
102
103 def Get(self, rel_path):
104 # type: (str) -> str
105 with open(os_path.join(self.root_dir, rel_path)) as f:
106 contents = f.read()
107 return contents
108
109
110class _ZipResourceLoader(_ResourceLoader):
111 """Open resources INSIDE argv[0] as a zip file."""
112
113 def __init__(self, argv0):
114 # type: (str) -> None
115 self.z = zipimport.zipimporter(argv0)
116
117 def Get(self, rel_path):
118 # type: (str) -> str
119 return self.z.get_data(rel_path)
120
121
122def IsAppBundle():
123 # type: () -> bool
124 """Are we running inside the patched version of CPython?
125
126 As opposed to a "stock" Python interpreter.
127 """
128 # Ovm_Main in main.c sets this.
129 return posix.environ.get('_OVM_IS_BUNDLE') == '1'
130
131
132_loader = None # type: _ResourceLoader
133
134
135def GetResourceLoader():
136 # type: () -> _ResourceLoader
137 global _loader
138 if _loader:
139 return _loader
140
141 if IsAppBundle():
142 ovm_path = posix.environ.get('_OVM_PATH')
143 _loader = _ZipResourceLoader(ovm_path)
144
145 # Now clear them so we don't pollute the environment. In Python, this
146 # calls unsetenv().
147 del posix.environ['_OVM_IS_BUNDLE']
148 del posix.environ['_OVM_PATH']
149
150 elif posix.environ.get('_OVM_RESOURCE_ROOT'): # Unit tests set this
151 root_dir = posix.environ.get('_OVM_RESOURCE_ROOT')
152 _loader = _FileResourceLoader(root_dir)
153
154 else:
155 # Find resources relative to the binary, e.g.
156 # ~/git/oilshell/oil/bin/oil.py. But it also assumes that all unit tests
157 # that use resources are are one directory deep, e.g. core/util_test.py.
158 bin_dir = os_path.dirname(os_path.abspath(sys.argv[0]))
159 root_dir = os_path.join(bin_dir, '..') # ~/git/oilshell/oil
160 _loader = _FileResourceLoader(root_dir)
161
162 return _loader
163
164
165def GetVersion(loader):
166 # type: (_ResourceLoader) -> str
167 contents = loader.Get('oils-version.txt')
168 version_str, _ = mylib.split_once(contents, '\n')
169 return version_str
170
171
172def PrintVersionDetails(loader):
173 # type: (_ResourceLoader) -> None
174 """Show version and platform information."""
175 try:
176 contents = loader.Get('release-date.txt')
177 release_date, _ = mylib.split_once(contents, '\n')
178 except IOError:
179 release_date = '-' # in dev tree
180
181 try:
182 contents = loader.Get('pyc-version.txt')
183 pyc_version, _ = mylib.split_once(contents, '\n')
184 except IOError:
185 pyc_version = '-' # in dev tree
186
187 # node is like 'hostname'
188 # release is the kernel version
189 system, unused_node, unused_release, platform_version, machine = posix.uname(
190 )
191
192 # The platform.py module has a big regex that parses sys.version, but we
193 # don't want to depend on regular expressions. So we will do our own parsing
194 # here.
195 version_line, py_compiler = sys.version.splitlines()
196
197 # Pick off the first part of '2.7.12 (default, ...)'
198 py_version = version_line.split()[0]
199
200 assert py_compiler.startswith('['), py_compiler
201 assert py_compiler.endswith(']'), py_compiler
202 py_compiler = py_compiler[1:-1]
203
204 # We removed sys.executable from sysmodule.c.
205 py_impl = 'CPython' if hasattr(sys, 'executable') else 'OVM'
206
207 print('Release Date: %s' % release_date)
208 print('Arch: %s' % machine)
209 print('OS: %s' % system)
210 print('Platform: %s' % platform_version)
211 print('Compiler: %s' % py_compiler)
212 print('Interpreter: %s' % py_impl)
213 print('Interpreter version: %s' % py_version)
214 print('Bytecode: %s' % pyc_version)
215
216
217# This was useful for debugging.
218def ShowFdState():
219 # type: () -> None
220 import subprocess
221 import posix_ as posix
222 subprocess.call(['ls', '-l', '/proc/%d/fd' % posix.getpid()])