bare-script 4.0.3__tar.gz → 4.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {bare_script-4.0.3/src/bare_script.egg-info → bare_script-4.1.1}/PKG-INFO +9 -1
  2. {bare_script-4.0.3 → bare_script-4.1.1}/README.md +8 -0
  3. {bare_script-4.0.3 → bare_script-4.1.1}/setup.cfg +1 -1
  4. bare_script-4.1.1/setup.py +46 -0
  5. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/__init__.py +11 -3
  6. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/bare.py +9 -1
  7. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/library.py +8 -5
  8. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/runtime.py +118 -81
  9. bare_script-4.1.1/src/bare_script/runtime_c.c +1900 -0
  10. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/value.py +56 -37
  11. {bare_script-4.0.3 → bare_script-4.1.1/src/bare_script.egg-info}/PKG-INFO +9 -1
  12. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/SOURCES.txt +2 -0
  13. {bare_script-4.0.3 → bare_script-4.1.1}/LICENSE +0 -0
  14. {bare_script-4.0.3 → bare_script-4.1.1}/pyproject.toml +0 -0
  15. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/__main__.py +0 -0
  16. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/__init__.py +0 -0
  17. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/args.bare +0 -0
  18. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/baredoc.bare +0 -0
  19. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/baredocCLI.bare +0 -0
  20. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/data.bare +0 -0
  21. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/dataLineChart.bare +0 -0
  22. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/dataTable.bare +0 -0
  23. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/dataUtil.bare +0 -0
  24. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/diff.bare +0 -0
  25. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/draw.bare +0 -0
  26. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/elementModel.bare +0 -0
  27. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/forms.bare +0 -0
  28. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdown.bare +0 -0
  29. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdownElements.bare +0 -0
  30. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdownHighlight.bare +0 -0
  31. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdownParser.bare +0 -0
  32. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/markdownUp.bare +0 -0
  33. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/pager.bare +0 -0
  34. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/qrcode.bare +0 -0
  35. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/schemaDoc.bare +0 -0
  36. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/unittest.bare +0 -0
  37. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/include/unittestMock.bare +0 -0
  38. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/model.py +0 -0
  39. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/options.py +0 -0
  40. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script/parser.py +0 -0
  41. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/dependency_links.txt +0 -0
  42. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/entry_points.txt +0 -0
  43. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/requires.txt +0 -0
  44. {bare_script-4.0.3 → bare_script-4.1.1}/src/bare_script.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bare-script
3
- Version: 4.0.3
3
+ Version: 4.1.1
4
4
  Summary: bare-script
5
5
  Home-page: https://github.com/craigahobbs/bare-script
6
6
  Author: Craig A. Hobbs
@@ -191,6 +191,14 @@ markdownPrint('Hello, Markdown!')
191
191
  ~~~
192
192
 
193
193
 
194
+ ## C Runtime
195
+
196
+ The package ships with an optional CPython C extension (`runtime_c`) that mirrors the pure-Python
197
+ runtime for faster script execution. When the compiled extension is available, it is used
198
+ automatically; otherwise the pure-Python runtime is used as a fallback. Set the environment
199
+ variable `BARESCRIPT_RUNTIME_PY=1` to force the pure-Python runtime.
200
+
201
+
194
202
  ## Development
195
203
 
196
204
  This package is developed using [python-build](https://github.com/craigahobbs/python-build#readme).
@@ -166,6 +166,14 @@ markdownPrint('Hello, Markdown!')
166
166
  ~~~
167
167
 
168
168
 
169
+ ## C Runtime
170
+
171
+ The package ships with an optional CPython C extension (`runtime_c`) that mirrors the pure-Python
172
+ runtime for faster script execution. When the compiled extension is available, it is used
173
+ automatically; otherwise the pure-Python runtime is used as a fallback. Set the environment
174
+ variable `BARESCRIPT_RUNTIME_PY=1` to force the pure-Python runtime.
175
+
176
+
169
177
  ## Development
170
178
 
171
179
  This package is developed using [python-build](https://github.com/craigahobbs/python-build#readme).
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = bare-script
3
- version = 4.0.3
3
+ version = 4.1.1
4
4
  url = https://github.com/craigahobbs/bare-script
5
5
  author = Craig A. Hobbs
6
6
  author_email = craigahobbs@gmail.com
@@ -0,0 +1,46 @@
1
+ # Licensed under the MIT License
2
+ # https://github.com/craigahobbs/bare-script-py/blob/main/LICENSE
3
+
4
+ """
5
+ bare-script setup
6
+ """
7
+
8
+ from setuptools import setup, Extension
9
+ from setuptools.command.build_ext import build_ext
10
+
11
+
12
+ class OptionalBuildExt(build_ext):
13
+ """
14
+ Build C extensions optionally - if compilation fails, the package still installs
15
+ and falls back to the pure-Python implementation.
16
+ """
17
+
18
+ def run(self):
19
+ try:
20
+ super().run()
21
+ except Exception: # pylint: disable=broad-except
22
+ self._unavailable()
23
+
24
+ def build_extension(self, ext):
25
+ try:
26
+ super().build_extension(ext)
27
+ except Exception: # pylint: disable=broad-except
28
+ self._unavailable()
29
+
30
+ @staticmethod
31
+ def _unavailable():
32
+ print('*' * 70)
33
+ print('WARNING: C extension could not be compiled.')
34
+ print(' Falling back to pure-Python implementation.')
35
+ print('*' * 70)
36
+
37
+
38
+ setup(
39
+ ext_modules=[
40
+ Extension(
41
+ 'bare_script.runtime_c',
42
+ sources=['src/bare_script/runtime_c.c']
43
+ )
44
+ ],
45
+ cmdclass={'build_ext': OptionalBuildExt}
46
+ )
@@ -5,6 +5,8 @@
5
5
  bare-script package
6
6
  """
7
7
 
8
+ import os as _os
9
+
8
10
  from .model import \
9
11
  lint_script, \
10
12
  validate_expression, \
@@ -25,6 +27,12 @@ from .parser import \
25
27
  parse_script
26
28
 
27
29
  from .runtime import \
28
- BareScriptRuntimeError, \
29
- evaluate_expression, \
30
- execute_script
30
+ BareScriptRuntimeError
31
+
32
+ if not _os.environ.get('BARESCRIPT_RUNTIME_PY'): # pragma: no cover
33
+ try:
34
+ from .runtime_c import evaluate_expression, execute_script
35
+ except ImportError:
36
+ from .runtime import evaluate_expression, execute_script
37
+ else:
38
+ from .runtime import evaluate_expression, execute_script
@@ -7,14 +7,22 @@ bare-script command-line interface (CLI)
7
7
 
8
8
  import argparse
9
9
  from functools import partial
10
+ import os as _os
10
11
  import sys
11
12
  import time
12
13
 
13
14
  from .model import lint_script
14
15
  from .options import FETCH_SYSTEM_PREFIX, fetch_read_write, fetch_system, log_stdout, url_file_relative
15
16
  from .parser import parse_expression, parse_script
16
- from .runtime import SYSTEM_GLOBAL_INCLUDES_NAME, evaluate_expression, execute_script
17
+ from .runtime import SYSTEM_GLOBAL_INCLUDES_NAME
17
18
  from .value import value_boolean
19
+ if not _os.environ.get('BARESCRIPT_RUNTIME_PY'): # pragma: no cover
20
+ try:
21
+ from .runtime_c import evaluate_expression, execute_script
22
+ except ImportError:
23
+ from .runtime import evaluate_expression, execute_script
24
+ else:
25
+ from .runtime import evaluate_expression, execute_script
18
26
 
19
27
 
20
28
  def main(argv=None):
@@ -11,6 +11,7 @@ import functools
11
11
  import importlib
12
12
  import json
13
13
  import math
14
+ import os as _os
14
15
  import random
15
16
  import re
16
17
  import urllib
@@ -27,16 +28,18 @@ from .value import R_NUMBER_CLEANUP, ValueArgsError, value_args_model, value_arg
27
28
  # Helper to dynamically import evaluate_expression to avoid the circular dependency
28
29
  def _import_evaluate_expression():
29
30
  if not _EVALUATE_EXPRESSION:
30
- _EVALUATE_EXPRESSION.append(importlib.import_module('bare_script.runtime').evaluate_expression)
31
+ if not _os.environ.get('BARESCRIPT_RUNTIME_PY'): # pragma: no cover
32
+ try:
33
+ _EVALUATE_EXPRESSION.append(importlib.import_module('bare_script.runtime_c').evaluate_expression)
34
+ except (ImportError, AttributeError):
35
+ _EVALUATE_EXPRESSION.append(importlib.import_module('bare_script.runtime').evaluate_expression)
36
+ else:
37
+ _EVALUATE_EXPRESSION.append(importlib.import_module('bare_script.runtime').evaluate_expression)
31
38
  return _EVALUATE_EXPRESSION[0]
32
39
 
33
40
  _EVALUATE_EXPRESSION = []
34
41
 
35
42
 
36
- # The default maximum statements for executeScript
37
- DEFAULT_MAX_STATEMENTS = 1e9
38
-
39
-
40
43
  #
41
44
  # Array functions
42
45
  #
@@ -8,13 +8,17 @@ The BareScript runtime
8
8
  import datetime
9
9
  import functools
10
10
 
11
- from .library import DEFAULT_MAX_STATEMENTS, EXPRESSION_FUNCTIONS, SCRIPT_FUNCTIONS
11
+ from .library import EXPRESSION_FUNCTIONS, SCRIPT_FUNCTIONS
12
12
  from .model import lint_script
13
13
  from .options import url_file_relative
14
14
  from .parser import parse_script
15
15
  from .value import ValueArgsError, value_boolean, value_compare, value_normalize_datetime, value_round_number, value_string
16
16
 
17
17
 
18
+ # The default maximum statements for executeScript
19
+ DEFAULT_MAX_STATEMENTS = 1e9
20
+
21
+
18
22
  # Coverage configuration object global variable name
19
23
  SYSTEM_GLOBAL_COVERAGE_NAME = '__barescriptCoverage'
20
24
 
@@ -54,6 +58,13 @@ def execute_script(script, options=None):
54
58
 
55
59
  def _execute_script_helper(script, statements, options, locals_):
56
60
  globals_ = options['globals']
61
+ max_statements = options.get('maxStatements', DEFAULT_MAX_STATEMENTS)
62
+ options.setdefault('statementCount', 0)
63
+
64
+ # Coverage configuration is invariant across this helper invocation
65
+ coverage_global = globals_.get(SYSTEM_GLOBAL_COVERAGE_NAME)
66
+ has_coverage = coverage_global is not None and isinstance(coverage_global, dict) and \
67
+ coverage_global.get('enabled') and not script.get('system')
57
68
 
58
69
  # Iterate each script statement
59
70
  label_indexes = None
@@ -61,25 +72,22 @@ def _execute_script_helper(script, statements, options, locals_):
61
72
  ix_statement = 0
62
73
  while ix_statement < statements_length:
63
74
  statement = statements[ix_statement]
64
- statement_key = next(iter(statement.keys()))
65
75
 
66
76
  # Increment the statement counter
67
- options['statementCount'] = options.get('statementCount', 0) + 1
68
- max_statements = options.get('maxStatements', DEFAULT_MAX_STATEMENTS)
77
+ options['statementCount'] += 1
69
78
  if max_statements > 0 and options['statementCount'] > max_statements:
70
79
  raise BareScriptRuntimeError(script, statement, f'Exceeded maximum script statements ({max_statements})')
71
80
 
72
81
  # Record the statement coverage
73
- coverage_global = globals_.get(SYSTEM_GLOBAL_COVERAGE_NAME)
74
- has_coverage = coverage_global is not None and isinstance(coverage_global, dict) and \
75
- coverage_global.get('enabled') and not script.get('system')
76
82
  if has_coverage:
83
+ statement_key = next(iter(statement.keys()))
77
84
  _record_statement_coverage(script, statement, statement_key, coverage_global)
78
85
 
79
86
  # Expression?
80
- if statement_key == 'expr':
81
- expr_value = evaluate_expression(statement['expr']['expr'], options, locals_, False, script, statement)
82
- expr_name = statement['expr'].get('name')
87
+ if 'expr' in statement:
88
+ stmt_expr = statement['expr']
89
+ expr_value = evaluate_expression(stmt_expr['expr'], options, locals_, False, script, statement)
90
+ expr_name = stmt_expr.get('name')
83
91
  if expr_name is not None:
84
92
  if locals_ is not None:
85
93
  locals_[expr_name] = expr_value
@@ -87,24 +95,25 @@ def _execute_script_helper(script, statements, options, locals_):
87
95
  globals_[expr_name] = expr_value
88
96
 
89
97
  # Jump?
90
- elif statement_key == 'jump':
98
+ elif 'jump' in statement:
99
+ stmt_jump = statement['jump']
91
100
  # Evaluate the expression (if any)
92
- if 'expr' not in statement['jump'] or \
93
- value_boolean(evaluate_expression(statement['jump']['expr'], options, locals_, False, script, statement)):
101
+ if 'expr' not in stmt_jump or \
102
+ value_boolean(evaluate_expression(stmt_jump['expr'], options, locals_, False, script, statement)):
94
103
  # Find the label
95
- if label_indexes is not None and statement['jump']['label'] in label_indexes:
96
- ix_statement = label_indexes[statement['jump']['label']]
104
+ jump_label = stmt_jump['label']
105
+ if label_indexes is not None and jump_label in label_indexes:
106
+ ix_statement = label_indexes[jump_label]
97
107
  else:
98
- jump_label = statement['jump']['label']
99
108
  ix_label = next(
100
109
  (ix_stmt for ix_stmt, stmt in enumerate(statements) if 'label' in stmt and stmt['label']['name'] == jump_label),
101
110
  -1
102
111
  )
103
112
  if ix_label == -1:
104
- raise BareScriptRuntimeError(script, statement, f"Unknown jump label \"{statement['jump']['label']}\"")
113
+ raise BareScriptRuntimeError(script, statement, f"Unknown jump label \"{jump_label}\"")
105
114
  if label_indexes is None:
106
115
  label_indexes = {}
107
- label_indexes[statement['jump']['label']] = ix_label
116
+ label_indexes[jump_label] = ix_label
108
117
  ix_statement = ix_label
109
118
 
110
119
  # Record the label statement coverage
@@ -114,17 +123,19 @@ def _execute_script_helper(script, statements, options, locals_):
114
123
  _record_statement_coverage(script, label_statement, label_statement_key, coverage_global)
115
124
 
116
125
  # Return?
117
- elif statement_key == 'return':
118
- if 'expr' in statement['return']:
119
- return evaluate_expression(statement['return']['expr'], options, locals_, False, script, statement)
126
+ elif 'return' in statement:
127
+ stmt_return = statement['return']
128
+ if 'expr' in stmt_return:
129
+ return evaluate_expression(stmt_return['expr'], options, locals_, False, script, statement)
120
130
  return None
121
131
 
122
132
  # Function?
123
- elif statement_key == 'function':
124
- globals_[statement['function']['name']] = functools.partial(_script_function, script, statement['function'])
133
+ elif 'function' in statement:
134
+ stmt_function = statement['function']
135
+ globals_[stmt_function['name']] = functools.partial(_script_function, script, stmt_function)
125
136
 
126
137
  # Include?
127
- elif statement_key == 'include':
138
+ elif 'include' in statement:
128
139
  system_prefix = options.get('systemPrefix')
129
140
  fetch_fn = options.get('fetchFn')
130
141
  log_fn = options.get('logFn')
@@ -239,50 +250,56 @@ def evaluate_expression(expr, options=None, locals_=None, builtins=True, script=
239
250
  :raises BareScriptRuntimeError: A script runtime error occurred
240
251
  """
241
252
 
242
- expr_key, = expr.keys()
243
253
  globals_ = options.get('globals') if options is not None else None
244
254
 
245
255
  # Number
246
- if expr_key == 'number':
256
+ if 'number' in expr:
247
257
  return expr['number']
248
258
 
249
259
  # String
250
- if expr_key == 'string':
260
+ if 'string' in expr:
251
261
  return expr['string']
252
262
 
253
263
  # Variable
254
- if expr_key == 'variable':
264
+ if 'variable' in expr:
265
+ variable = expr['variable']
266
+
255
267
  # Keywords
256
- if expr['variable'] == 'null':
268
+ if variable == 'null':
257
269
  return None
258
- if expr['variable'] == 'false':
270
+ if variable == 'false':
259
271
  return False
260
- if expr['variable'] == 'true':
272
+ if variable == 'true':
261
273
  return True
262
274
 
263
275
  # Get the local or global variable value or None if undefined
264
- if locals_ is not None and expr['variable'] in locals_:
265
- return locals_[expr['variable']]
276
+ if locals_ is not None and variable in locals_:
277
+ return locals_[variable]
266
278
  else:
267
- return globals_.get(expr['variable']) if globals_ is not None else None
279
+ return globals_.get(variable) if globals_ is not None else None
268
280
 
269
281
  # Function
270
- if expr_key == 'function':
282
+ if 'function' in expr:
283
+ func = expr['function']
284
+
271
285
  # "if" built-in function?
272
- func_name = expr['function']['name']
286
+ func_name = func['name']
273
287
  if func_name == 'if':
274
- args_expr = expr['function'].get('args', ())
288
+ args_expr = func.get('args', ())
275
289
  args_expr_length = len(args_expr)
276
290
  value_expr = args_expr[0] if args_expr_length >= 1 else None
277
291
  true_expr = args_expr[1] if args_expr_length >= 2 else None
278
292
  false_expr = args_expr[2] if args_expr_length >= 3 else None
279
- value = evaluate_expression(value_expr, options, locals_, builtins, script, statement) if value_expr else False
293
+ value = evaluate_expression(value_expr, options, locals_, builtins, script, statement) \
294
+ if value_expr is not None else False
280
295
  result_expr = true_expr if value_boolean(value) else false_expr
281
- return evaluate_expression(result_expr, options, locals_, builtins, script, statement) if result_expr else None
296
+ return evaluate_expression(result_expr, options, locals_, builtins, script, statement) \
297
+ if result_expr is not None else None
282
298
 
283
299
  # Compute the function arguments
284
- func_args = [evaluate_expression(arg, options, locals_, builtins, script, statement) for arg in expr['function']['args']] \
285
- if 'args' in expr['function'] else None
300
+ args_expr = func.get('args')
301
+ func_args = [evaluate_expression(arg, options, locals_, builtins, script, statement) for arg in args_expr] \
302
+ if args_expr is not None else None
286
303
 
287
304
  # Global/local function?
288
305
  if locals_ is not None and func_name in locals_:
@@ -300,7 +317,10 @@ def evaluate_expression(expr, options=None, locals_=None, builtins=True, script=
300
317
  except Exception as error:
301
318
  # Log and return null
302
319
  if options is not None and 'logFn' in options and options.get('debug'):
303
- options['logFn'](f'BareScript: Function "{func_name}" failed with error: {error}')
320
+ error_message = BareScriptRuntimeError(
321
+ script, statement, f'BareScript: Function "{func_name}" failed with error: {error}'
322
+ )
323
+ options['logFn'](str(error_message))
304
324
  if isinstance(error, ValueArgsError):
305
325
  return error.return_value
306
326
  return None
@@ -308,54 +328,57 @@ def evaluate_expression(expr, options=None, locals_=None, builtins=True, script=
308
328
  raise BareScriptRuntimeError(script, statement, f'Undefined function "{func_name}"')
309
329
 
310
330
  # Binary expression
311
- if expr_key == 'binary':
312
- bin_op = expr['binary']['op']
313
- left_value = evaluate_expression(expr['binary']['left'], options, locals_, builtins, script, statement)
331
+ if 'binary' in expr:
332
+ binary = expr['binary']
333
+ bin_op = binary['op']
334
+ left_value = evaluate_expression(binary['left'], options, locals_, builtins, script, statement)
314
335
 
315
336
  # Short-circuiting "and" binary operator
316
337
  if bin_op == '&&':
317
338
  if not value_boolean(left_value):
318
339
  return left_value
319
- return evaluate_expression(expr['binary']['right'], options, locals_, builtins, script, statement)
340
+ return evaluate_expression(binary['right'], options, locals_, builtins, script, statement)
320
341
 
321
342
  # Short-circuiting "or" binary operator
322
343
  elif bin_op == '||':
323
344
  if value_boolean(left_value):
324
345
  return left_value
325
- return evaluate_expression(expr['binary']['right'], options, locals_, builtins, script, statement)
346
+ return evaluate_expression(binary['right'], options, locals_, builtins, script, statement)
326
347
 
327
348
  # Non-short-circuiting binary operators
328
- right_value = evaluate_expression(expr['binary']['right'], options, locals_, builtins, script, statement)
349
+ right_value = evaluate_expression(binary['right'], options, locals_, builtins, script, statement)
350
+ left_type = type(left_value)
351
+ right_type = type(right_value)
329
352
  if bin_op == '+':
330
353
  # number + number
331
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and
332
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool)):
354
+ if ((left_type is int or left_type is float) and
355
+ (right_type is int or right_type is float)):
333
356
  return left_value + right_value
334
357
 
335
358
  # string + string
336
- elif isinstance(left_value, str) and isinstance(right_value, str):
359
+ elif left_type is str and right_type is str:
337
360
  return left_value + right_value
338
361
 
339
362
  # string + <any>
340
- elif isinstance(left_value, str):
363
+ elif left_type is str:
341
364
  return left_value + value_string(right_value)
342
- elif isinstance(right_value, str):
365
+ elif right_type is str:
343
366
  return value_string(left_value) + right_value
344
367
 
345
368
  # datetime + number
346
369
  elif (isinstance(left_value, datetime.date) and
347
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool)):
370
+ (right_type is int or right_type is float)):
348
371
  left_dt = value_normalize_datetime(left_value)
349
372
  return left_dt + datetime.timedelta(milliseconds=right_value)
350
- elif (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and
373
+ elif ((left_type is int or left_type is float) and
351
374
  isinstance(right_value, datetime.date)):
352
375
  right_dt = value_normalize_datetime(right_value)
353
376
  return right_dt + datetime.timedelta(milliseconds=left_value)
354
377
 
355
378
  elif bin_op == '-':
356
379
  # number - number
357
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and
358
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool)):
380
+ if ((left_type is int or left_type is float) and
381
+ (right_type is int or right_type is float)):
359
382
  return left_value - right_value
360
383
 
361
384
  # datetime - datetime
@@ -366,90 +389,104 @@ def evaluate_expression(expr, options=None, locals_=None, builtins=True, script=
366
389
 
367
390
  elif bin_op == '*':
368
391
  # number * number
369
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and
370
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool)):
392
+ if ((left_type is int or left_type is float) and
393
+ (right_type is int or right_type is float)):
371
394
  return left_value * right_value
372
395
 
373
396
  elif bin_op == '/':
374
397
  # number / number
375
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and
376
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool)):
398
+ if ((left_type is int or left_type is float) and
399
+ (right_type is int or right_type is float)):
377
400
  return left_value / right_value
378
401
 
379
402
  elif bin_op == '==':
403
+ if (left_type is int or left_type is float) and (right_type is int or right_type is float):
404
+ return left_value == right_value
380
405
  return value_compare(left_value, right_value) == 0
381
406
 
382
407
  elif bin_op == '!=':
408
+ if (left_type is int or left_type is float) and (right_type is int or right_type is float):
409
+ return left_value != right_value
383
410
  return value_compare(left_value, right_value) != 0
384
411
 
385
412
  elif bin_op == '<=':
413
+ if (left_type is int or left_type is float) and (right_type is int or right_type is float):
414
+ return left_value <= right_value
386
415
  return value_compare(left_value, right_value) <= 0
387
416
 
388
417
  elif bin_op == '<':
418
+ if (left_type is int or left_type is float) and (right_type is int or right_type is float):
419
+ return left_value < right_value
389
420
  return value_compare(left_value, right_value) < 0
390
421
 
391
422
  elif bin_op == '>=':
423
+ if (left_type is int or left_type is float) and (right_type is int or right_type is float):
424
+ return left_value >= right_value
392
425
  return value_compare(left_value, right_value) >= 0
393
426
 
394
427
  elif bin_op == '>':
428
+ if (left_type is int or left_type is float) and (right_type is int or right_type is float):
429
+ return left_value > right_value
395
430
  return value_compare(left_value, right_value) > 0
396
431
 
397
432
  elif bin_op == '%':
398
433
  # number % number
399
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and
400
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool)):
434
+ if ((left_type is int or left_type is float) and
435
+ (right_type is int or right_type is float)):
401
436
  return left_value % right_value
402
437
 
403
438
  elif bin_op == '**':
404
439
  # number ** number
405
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and
406
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool)):
440
+ if ((left_type is int or left_type is float) and
441
+ (right_type is int or right_type is float)):
407
442
  return left_value ** right_value
408
443
 
409
444
  elif bin_op == '&':
410
445
  # int & int
411
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and int(left_value) == left_value and
412
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool) and int(right_value) == right_value):
446
+ if ((left_type is int or left_type is float) and int(left_value) == left_value and
447
+ (right_type is int or right_type is float) and int(right_value) == right_value):
413
448
  return int(left_value) & int(right_value)
414
449
 
415
450
  elif bin_op == '|':
416
451
  # int & int
417
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and int(left_value) == left_value and
418
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool) and int(right_value) == right_value):
452
+ if ((left_type is int or left_type is float) and int(left_value) == left_value and
453
+ (right_type is int or right_type is float) and int(right_value) == right_value):
419
454
  return int(left_value) | int(right_value)
420
455
 
421
456
  elif bin_op == '^':
422
457
  # int & int
423
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and int(left_value) == left_value and
424
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool) and int(right_value) == right_value):
458
+ if ((left_type is int or left_type is float) and int(left_value) == left_value and
459
+ (right_type is int or right_type is float) and int(right_value) == right_value):
425
460
  return int(left_value) ^ int(right_value)
426
461
 
427
462
  elif bin_op == '<<':
428
463
  # int & int
429
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and int(left_value) == left_value and
430
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool) and int(right_value) == right_value):
464
+ if ((left_type is int or left_type is float) and int(left_value) == left_value and
465
+ (right_type is int or right_type is float) and int(right_value) == right_value):
431
466
  return int(left_value) << int(right_value)
432
467
 
433
468
  else: # bin_op == '>>':
434
469
  # int & int
435
- if (isinstance(left_value, (int, float)) and not isinstance(left_value, bool) and int(left_value) == left_value and
436
- isinstance(right_value, (int, float)) and not isinstance(right_value, bool) and int(right_value) == right_value):
470
+ if ((left_type is int or left_type is float) and int(left_value) == left_value and
471
+ (right_type is int or right_type is float) and int(right_value) == right_value):
437
472
  return int(left_value) >> int(right_value)
438
473
 
439
474
  # Invalid operation values
440
475
  return None
441
476
 
442
477
  # Unary expression
443
- if expr_key == 'unary':
444
- unary_op = expr['unary']['op']
445
- value = evaluate_expression(expr['unary']['expr'], options, locals_, builtins, script, statement)
478
+ if 'unary' in expr:
479
+ unary = expr['unary']
480
+ unary_op = unary['op']
481
+ value = evaluate_expression(unary['expr'], options, locals_, builtins, script, statement)
446
482
  if unary_op == '!':
447
483
  return not value_boolean(value)
448
- elif unary_op == '-':
449
- if isinstance(value, (int, float)) and not isinstance(value, bool):
484
+ val_type = type(value)
485
+ if unary_op == '-':
486
+ if val_type is int or val_type is float:
450
487
  return -value
451
488
  else: # unary_op == '~':
452
- if isinstance(value, (int, float)) and not isinstance(value, bool) and int(value) == value:
489
+ if (val_type is int or val_type is float) and int(value) == value:
453
490
  return ~int(value)
454
491
 
455
492
  # Invalid operation value