aboutsummaryrefslogtreecommitdiffstats
path: root/awlsim/common/compat.py
blob: 64f8ccf5ed5313667a5a4dcd9dabcfe6e361fef9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# -*- coding: utf-8 -*-
#
# AWL simulator - Python interpreter compatibility
#
# Copyright 2012-2020 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

from __future__ import division, absolute_import, print_function, unicode_literals

import contextlib
import fractions
import functools
import math
import os
import os.path # micropython needs explicit import of os.path
import re
import select
import socket
import sys
import time


__all__ = [
	"sys",
	"os",
	"contextlib",
	"osIsWindows",
	"osIsPosix",
	"osIsLinux",
	"standaloneServerExe",
	"isPyPy",
	"isJython",
	"isIronPython",
	"isCython",
	"isMicroPython",
	"isWinStandalone",
	"isPy3Compat",
	"isPy2Compat",
	"py23",
	"pythonInterpreter",
	"input",
	"range",
	"reduce",
	"queue",
	"BlockingIOError",
	"IOError",
	"ConnectionError",
	"StringIO",
	"isalnum",
	"isdecimal",
	"compat_gcd",
	"dictItems",
	"dictKeys",
	"dictValues",
	"bit_length",
	"excErrno",
]


# Convenient operating system identifiers
if os.name == "java": #@nocov
	import java.lang.System
	__osName = java.lang.System.getProperty("os.name").lower()
	osIsWindows = __osName.startswith("windows")
	osIsPosix = not osIsWindows
else: #@nocov
	osIsWindows = os.name == "nt" or os.name == "ce"
	osIsPosix = os.name == "posix"
osIsLinux = osIsPosix and "linux" in sys.platform.lower()

# Executable name of the standalone server.
standaloneServerExe = "awlsim-server-module.exe"

# isPyPy is True, if the interpreter is PyPy.
isPyPy = "PyPy" in sys.version

# isJython is True, if the interpreter is Jython.
isJython = sys.platform.lower().startswith("java")

# isIronPython is True, if the interpreter is IronPython
isIronPython = "IronPython" in sys.version

# isCython is True, if the interpreter is Cython
isCython = False #@nocy
#isCython = True #@cy

# isMicroPython is True, if the interpreter is MicroPython
isMicroPython = hasattr(sys, "implementation") and\
		sys.implementation.name.lower() == "micropython"

# isWinStandalone is True, if this is a Windows standalone package (py2exe/cx_Freeze)
isWinStandalone = osIsWindows and\
		  (sys.executable.endswith("awlsim-gui.exe") or\
		   sys.executable.endswith("awlsim-client.exe") or\
		   sys.executable.endswith("awlsim-server.exe") or\
		   sys.executable.endswith(standaloneServerExe) or\
		   sys.executable.endswith("awlsim-symtab.exe") or\
		   sys.executable.endswith("awlsim-test.exe"))

# isPy3Compat is True, if the interpreter is Python 3 compatible.
isPy3Compat = sys.version_info[0] == 3

# isPy2Compat is True, if the interpreter is Python 2 compatible.
isPy2Compat = sys.version_info[0] == 2

# Python 2/3 helper selection
def py23(py2, py3): #@nocov
	if isPy3Compat:
		return py3
	if isPy2Compat:
		return py2
	raise Exception("Failed to detect Python version") #@nocov

# Python interpreter name, as string.
if isCython:
	pythonInterpreter = "Cython"			#@nocov
elif isPyPy:
	pythonInterpreter = "PyPy"			#@nocov
elif isJython:
	pythonInterpreter = "Jython"			#@nocov
elif isIronPython:
	pythonInterpreter = "IronPython"		#@nocov
elif isMicroPython:
	pythonInterpreter = "MicroPython"		#@nocov
elif isWinStandalone:
	pythonInterpreter = "CPython (frozen)"		#@nocov
else:
	pythonInterpreter = "CPython"			#@nocov

# input() compatibility.
# Force Python3 behavior
if isPy2Compat: #@nocov
	input = raw_input
else: #@nocov
	input = input

# range() compatibility.
# Force Python3 behavior
if isPy2Compat: #@nocov
	range = xrange
else: #@nocov
	range = range

# reduce() compatibility.
# Force Python2 behavior
if isPy3Compat: #@nocov
	from functools import reduce
else: #@nocov
	reduce = reduce

# queue compatibility
# Force Python3 behavior
if isPy2Compat: #@nocov
	import Queue as queue
else: #@nocov
	import queue

# BlockingIOError dummy
try: #@nocov
	BlockingIOError
except NameError: #@nocov
	class BlockingIOError(BaseException): pass
BlockingIOError = BlockingIOError

# IOError dummy
try: #@nocov
	IOError
except NameError: #@nocov
	IOError = OSError
IOError = IOError

# ConnectionError dummy
try: #@nocov
	ConnectionError
except NameError: #@nocov
	ConnectionError = OSError
ConnectionError = ConnectionError

# Import StringIO
if isIronPython and isPy2Compat: #@nocov
	# Workaround for IronPython's buggy io.StringIO
	from StringIO import StringIO
else: #@nocov
	from io import StringIO
StringIO = StringIO
from io import BytesIO

# str.isalnum() compatibility
# This defines a global function: isalnum(string) ==> bool
if hasattr(str, "isalnum"): #@nocov
	isalnum = lambda s: s.isalnum()
else: #@nocov
	isalnum = lambda s: all(c.isalpha() or c.isdigit() for c in s)

# str.isdecimal() compatibility
# This defines a global function: isdecimal(string) ==> bool
if hasattr(str, "isdecimal"): #@nocov
	isdecimal = lambda s: s.isdecimal()
else: #@nocov
	isdecimal = lambda s: all(c in "0123456789" for c in s)

# gcd() compatibility
# This defines a global function: compat_gcd(a, b) ==> int
if hasattr(math, "gcd"): #@nocov
	compat_gcd = math.gcd
elif hasattr(fractions, "gcd"): #@nocov
	compat_gcd = fractions.gcd
else: #@nocov
	def compat_gcd(a, b):
		while b:
			(a, b) = (b, a % b)
		return a

# contextlib.suppress compatibility
if not hasattr(contextlib, "suppress"): #@nocov
	class _suppress(object):
		def __init__(self, *excs):
			self._excs = excs
		def __enter__(self):
			pass
		def __exit__(self, exctype, excinst, exctb):
			return exctype is not None and issubclass(exctype, self._excs)
	contextlib.suppress = _suppress

# contextlib.nullcontext compatibility
if not hasattr(contextlib, "nullcontext"): #@nocov
	class _nullcontext(object):
		def __init__(self, enter_result=None):
			self.enter_result = enter_result
		def __enter__(self):
			return self.enter_result
		def __exit__(self, *unused):
			pass
	contextlib.nullcontext = _nullcontext

# Dict items(), keys(), values() compatibility.
# Use Python3 behavior.
dictItems = py23(lambda d: d.viewitems(),
		 lambda d: d.items())
dictKeys = py23(lambda d: d.viewkeys(),
		lambda d: d.keys())
dictValues = py23(lambda d: d.viewvalues(),
		  lambda d: d.values())

# select.select substitute
# Micropython doesn't have select.select.
if not hasattr(select, "select"): #@nocov
	select.select = None # Dummy

# Python 2 compat: log2
if not hasattr(math, "log2"): #@nocov
	math.log2 = lambda x: math.log(x, 2)

# Python 2 compat: isfinite
if not hasattr(math, "isfinite"): #@nocov
	math.isfinite = lambda x: not math.isnan(x) and not math.isinf(x)

# int.bit_length substitute
# Micropython doesn't have int.bit_length.
def bit_length(value): #@nocov
	assert isinstance(value, (int, long) if isPy2Compat else int)
	if hasattr(value, "bit_length"):
		return value.bit_length()
	return int(math.ceil(math.log2(value)))

# functools.cmp_to_key substitute
# Micropython doesn't have functools.cmp_to_key.
if not hasattr(functools, "cmp_to_key"): #@nocov
	def cmp_to_key(f):
		class Key(object):
			__hash__ = None
			def __init__(s, x): s.x = x
			def __eq__(s, o): return f(s.x, o.x) == 0
			def __lt__(s, o): return f(s.x, o.x) < 0
			def __ge__(s, o): return f(s.x, o.x) >= 0
			def __gt__(s, o): return f(s.x, o.x) > 0
			def __le__(s, o): return f(s.x, o.x) <= 0
		return Key
	functools.cmp_to_key = cmp_to_key

# functools.lru_cache substitute
# Python2 does not have functools.lru_cache.
if not hasattr(functools, "lru_cache"): #@nocov
	def _no_lru_cache(*a, **k):
		def decorator(f):
			def wrapper(*aa, **kk):
				return f(*aa, **kk)
			return wrapper
		return decorator
	functools.lru_cache = _no_lru_cache

# socket.AF_UNSPEC substitute
# Micropython doesn't have socket.AF_UNSPEC.
if not hasattr(socket, "AF_UNSPEC"): #@nocov
	socket.AF_UNSPEC = 0

# socket.timeout substitute
# Micropython doesn't have socket.timeout.
if not hasattr(socket, "timeout"): #@nocov
	socket.timeout = OSError

# OSError.errno substitute
# Micropython doesn't have OSError.errno.
def excErrno(exc):
	if hasattr(exc, "errno"):
		return exc.errno
	if isMicroPython and isinstance(exc, OSError):
		m = re.match(r"\[Errno (\d+)\]", str(exc))
		if m:
			return int(m.group(1))
	return -1
bues.ch cgit interface