simpleeval 0.9.12__tar.gz → 1.0.0__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.
@@ -3,6 +3,7 @@ build
3
3
  dist
4
4
  MANIFEST
5
5
  .idea
6
+ .vscode
6
7
  venv
7
8
  .eggs/
8
9
  .coverage
@@ -1,4 +1,4 @@
1
- simpleeval - Copyright (c) 2013-2017 Daniel Fairhead
1
+ simpleeval - Copyright (c) 2013-2024 Daniel Fairhead
2
2
 
3
3
  (MIT Licence)
4
4
 
@@ -1,5 +1,5 @@
1
1
  test:
2
- python test_simpleeval.py
2
+ python -Werror test_simpleeval.py
3
3
 
4
4
  autotest:
5
5
  find . -name \*.py -not -path .\/.v\* | entr make test
@@ -22,9 +22,10 @@ coverage:
22
22
  coverage run test_simpleeval.py
23
23
 
24
24
  lint:
25
- black --check --diff simpleeval.py test_simpleeval.py
26
- isort --check-only --diff simpleeval.py test_simpleeval.py
25
+ ruff check simpleeval.py test_simpleeval.py
26
+ ruff format --check simpleeval.py test_simpleeval.py
27
+ mypy simpleeval.py test_simpleeval.py
27
28
 
28
29
  format:
29
- black simpleeval.py test_simpleeval.py
30
- isort simpleeval.py test_simpleeval.py
30
+ ruff check --fix-only simpleeval.py test_simpleeval.py
31
+ ruff format simpleeval.py test_simpleeval.py
@@ -1,37 +1,49 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simpleeval
3
- Version: 0.9.12
3
+ Version: 1.0.0
4
4
  Summary: A simple, safe single expression evaluator library.
5
5
  Home-page: https://github.com/danthedeckie/simpleeval
6
6
  Author: Daniel Fairhead
7
7
  Author-email: danthedeckie@gmail.com
8
- License: UNKNOWN
8
+ License: MIT
9
9
  Keywords: eval,simple,expression,parse,ast
10
- Platform: UNKNOWN
11
- Classifier: Development Status :: 4 - Beta
10
+ Classifier: Development Status :: 5 - Production/Stable
12
11
  Classifier: Intended Audience :: Developers
13
12
  Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
14
  Classifier: Programming Language :: Python
15
+ Requires-Python: >=3.9
16
16
  Description-Content-Type: text/x-rst
17
17
  License-File: LICENCE
18
18
 
19
19
  simpleeval (Simple Eval)
20
20
  ========================
21
21
 
22
- .. image:: https://github.com/danthedeckie/simpleeval/actions/workflows/ci.yml/badge.svg?branch=gh-actions-build
22
+ .. |build-status| image:: https://github.com/danthedeckie/simpleeval/actions/workflows/ci.yml/badge.svg?branch=gh-actions-build
23
23
  :target: https://github.com/danthedeckie/simpleeval/actions/
24
24
  :alt: Build Status
25
25
 
26
- .. image:: https://codecov.io/gh/danthedeckie/simpleeval/branch/master/graph/badge.svg?token=isRnN1yrca
26
+ .. |code-coverage| image:: https://codecov.io/gh/danthedeckie/simpleeval/branch/master/graph/badge.svg?token=isRnN1yrca
27
27
  :target: https://codecov.io/gh/danthedeckie/simpleeval
28
- :alt: Code Coverage
28
+ :alt: Code Coverage Status
29
29
 
30
- .. image:: https://badge.fury.io/py/simpleeval.svg
30
+ .. |pypi-version| image:: https://badge.fury.io/py/simpleeval.svg
31
31
  :target: https://badge.fury.io/py/simpleeval
32
32
  :alt: PyPI Version
33
33
 
34
- A quick single file library for easily adding evaluatable expressions into
34
+ .. |python-versions| image:: https://img.shields.io/badge/python-3.9_%7C_3.10_%7C_3.11_%7C_3.12_%7C_3.13_%7C_PyPy3.9_%7C_PyPy3.10-blue
35
+ :alt: Static Badge
36
+
37
+ .. |pypi-monthly-downloads| image:: https://img.shields.io/pypi/dm/SimpleEval
38
+ :alt: PyPI - Downloads
39
+
40
+ .. |formatting-with-ruff| image:: https://img.shields.io/badge/-ruff-black?logo=lightning&logoColor=%2300ff00&link=https%3A%2F%2Fdocs.astral.sh%2Fruff%2F
41
+ :alt: Static Badge
42
+
43
+ |build-status| |code-coverage| |pypi-version| |python-versions| |pypi-monthly-downloads| |formatting-with-ruff|
44
+
45
+
46
+ A single file library for easily adding evaluatable expressions into
35
47
  python projects. Say you want to allow a user to set an alarm volume, which
36
48
  could depend on the time of day, alarm level, how many previous alarms had gone
37
49
  off, and if there is music playing at the time.
@@ -39,8 +51,9 @@ off, and if there is music playing at the time.
39
51
  Or if you want to allow simple formulae in a web application, but don't want to
40
52
  give full eval() access, or don't want to run in javascript on the client side.
41
53
 
42
- It's deliberately very simple, pull it in from PyPI (pip or easy_install), or
43
- even just a single file you can dump into a project.
54
+ It's deliberately trying to stay simple to use and not have millions of features,
55
+ pull it in from PyPI (pip or easy_install), or even just a single file you can dump
56
+ into a project.
44
57
 
45
58
  Internally, it's using the amazing python ``ast`` module to parse the
46
59
  expression, which allows very fine control of what is and isn't allowed. It
@@ -55,7 +68,7 @@ Operators_ section below)
55
68
 
56
69
  You should be aware of this when deploying in a public setting.
57
70
 
58
- The defaults are pretty locked down and basic, and it's very easy to add
71
+ The defaults are pretty locked down and basic, and it's easy to add
59
72
  whatever extra specific functionality you need (your own functions,
60
73
  variable/name lookup, etc).
61
74
 
@@ -149,23 +162,36 @@ the defaults:
149
162
  | | ``"spam" in "my breakfast"`` |
150
163
  | | -> ``False`` |
151
164
  +--------+------------------------------------+
165
+ | ``^`` | "bitwise exclusive OR" (xor) |
166
+ | | ``62 ^ 20`` -> ``42`` |
167
+ +--------+------------------------------------+
168
+ | ``|`` | "bitwise OR" |
169
+ | | ``8 | 34`` -> ``42`` |
170
+ +--------+------------------------------------+
171
+ | ``&`` | "bitwise AND" |
172
+ | | ``100 & 63`` -> ``36`` |
173
+ +--------+------------------------------------+
174
+ | ``~`` | "bitwise invert" |
175
+ | | ``~ -43`` -> ``42`` |
176
+ +--------+------------------------------------+
152
177
 
153
178
 
154
- The ``^`` operator is notably missing - not because it's hard, but because it
155
- is often mistaken for a exponent operator, not the bitwise operation that it is
156
- in python. It's trivial to add back in again if you wish (using the class
157
- based evaluator explained below):
179
+ The ``^`` operator is often mistaken for a exponent operator, not the bitwise
180
+ operation that it is in python, so if you want ``3 ^ 2`` to equal ``9``, you can
181
+ replace the operator like this:
158
182
 
159
- .. code-block:: python
183
+ .. code-block:: pycon
160
184
 
161
185
  >>> import ast
162
- >>> import operator
186
+ >>> from simpleeval import safe_power
163
187
 
164
188
  >>> s = SimpleEval()
165
- >>> s.operators[ast.BitXor] = operator.xor
189
+ >>> s.operators[ast.BitXor] = safe_power
166
190
 
167
- >>> s.eval("2 ^ 10")
168
- 8
191
+ >>> s.eval("3 ^ 2")
192
+ 9
193
+
194
+ for example.
169
195
 
170
196
  Limited Power
171
197
  ~~~~~~~~~~~~~
@@ -173,13 +199,13 @@ Limited Power
173
199
  Also note, the ``**`` operator has been locked down by default to have a
174
200
  maximum input value of ``4000000``, which makes it somewhat harder to make
175
201
  expressions which go on for ever. You can change this limit by changing the
176
- ``simpleeval.POWER_MAX`` module level value to whatever is an appropriate value
202
+ ``simpleeval.MAX_POWER`` module level value to whatever is an appropriate value
177
203
  for you (and the hardware that you're running on) or if you want to completely
178
204
  remove all limitations, you can set the ``s.operators[ast.Pow] = operator.pow``
179
205
  or make your own function.
180
206
 
181
207
  On my computer, ``9**9**5`` evaluates almost instantly, but ``9**9**6`` takes
182
- over 30 seconds. Since ``9**7`` is ``4782969``, and so over the ``POWER_MAX``
208
+ over 30 seconds. Since ``9**7`` is ``4782969``, and so over the ``MAX_POWER``
183
209
  limit, it throws a ``NumberTooHigh`` exception for you. (Otherwise it would go
184
210
  on for hours, or until the computer runs out of memory)
185
211
 
@@ -197,7 +223,7 @@ If Expressions
197
223
 
198
224
  You can use python style ``if x then y else z`` type expressions:
199
225
 
200
- .. code-block:: python
226
+ .. code-block:: pycon
201
227
 
202
228
  >>> simple_eval("'equal' if x == y else 'not equal'",
203
229
  names={"x": 1, "y": 2})
@@ -205,7 +231,7 @@ You can use python style ``if x then y else z`` type expressions:
205
231
 
206
232
  which, of course, can be nested:
207
233
 
208
- .. code-block:: python
234
+ .. code-block:: pycon
209
235
 
210
236
  >>> simple_eval("'a' if 1 == 2 else 'b' if 2 == 3 else 'c'")
211
237
  'c'
@@ -216,7 +242,7 @@ Functions
216
242
 
217
243
  You can define functions which you'd like the expresssions to have access to:
218
244
 
219
- .. code-block:: python
245
+ .. code-block:: pycon
220
246
 
221
247
  >>> simple_eval("double(21)", functions={"double": lambda x:x*2})
222
248
  42
@@ -224,7 +250,7 @@ You can define functions which you'd like the expresssions to have access to:
224
250
  You can define "real" functions to pass in rather than lambdas, of course too,
225
251
  and even re-name them so that expressions can be shorter
226
252
 
227
- .. code-block:: python
253
+ .. code-block:: pycon
228
254
 
229
255
  >>> def double(x):
230
256
  return x * 2
@@ -243,13 +269,13 @@ are provided in the ``DEFAULT_FUNCTIONS`` dict:
243
269
  +----------------+--------------------------------------------------+
244
270
  | ``float(x)`` | Convert ``x`` to a ``float``. |
245
271
  +----------------+--------------------------------------------------+
246
- | ``str(x)`` | Convert ``x`` to a ``str`` (``unicode`` in py2) |
272
+ | ``str(x)`` | Convert ``x`` to a ``str`` |
247
273
  +----------------+--------------------------------------------------+
248
274
 
249
275
  If you want to provide a list of functions, but want to keep these as well,
250
276
  then you can do a normal python ``.copy()`` & ``.update``:
251
277
 
252
- .. code-block:: python
278
+ .. code-block:: pycon
253
279
 
254
280
  >>> my_functions = simpleeval.DEFAULT_FUNCTIONS.copy()
255
281
  >>> my_functions.update(
@@ -264,7 +290,7 @@ Names
264
290
  Sometimes it's useful to have variables available, which in python terminology
265
291
  are called 'names'.
266
292
 
267
- .. code-block:: python
293
+ .. code-block:: pycon
268
294
 
269
295
  >>> simple_eval("a + b", names={"a": 11, "b": 100})
270
296
  111
@@ -272,7 +298,7 @@ are called 'names'.
272
298
  You can also hand the handling of names over to a function, if you prefer:
273
299
 
274
300
 
275
- .. code-block:: python
301
+ .. code-block:: pycon
276
302
 
277
303
  >>> def name_handler(node):
278
304
  return ord(node.id[0].lower(a))-96
@@ -281,9 +307,34 @@ You can also hand the handling of names over to a function, if you prefer:
281
307
  3
282
308
 
283
309
  That was a bit of a silly example, but you could use this for pulling values
284
- from a database or file, say, or doing some kind of caching system.
310
+ from a database or file, looking up spreadsheet cells, say, or doing some kind of caching system.
311
+
312
+ In general, when it attempts to find a variable by name, if it cannot find one,
313
+ then it will look in the ``functions`` for a function of that name. If you want your name handler
314
+ function to return an "I can't find that name!", then it should raise a ``simpleeval.NameNotDefined``
315
+ exception. Eg:
316
+
317
+ .. code-block:: pycon
285
318
 
286
- The two default names that are provided are ``True`` and ``False``. So if you want to provide your own names, but want ``True`` and ``False`` to keep working, either provide them yourself, or ``.copy()`` and ``.update`` the ``DEFAULT_NAMES``. (See functions example above).
319
+ >>> def name_handler(node):
320
+ ... if node.id[0] == 'a':
321
+ ... return 21
322
+ ... raise NameNotDefined(node.id[0], "Not found")
323
+ ...
324
+ ... simple_eval('a + a', names=name_handler, functions={"b": 100})
325
+
326
+ 42
327
+
328
+ >>> simple_eval('a + b', names=name_handler, functions={'b': 100})
329
+ 121
330
+
331
+ (Note: in that example, putting a number directly into the ``functions`` dict was done just to
332
+ show the fall-back to functions. Normally only put actual callables in there.)
333
+
334
+
335
+ The two default names that are provided are ``True`` and ``False``. So if you want to provide
336
+ your own names, but want ``True`` and ``False`` to keep working, either provide them yourself,
337
+ or ``.copy()`` and ``.update`` the ``DEFAULT_NAMES``. (See functions example above).
287
338
 
288
339
  Creating an Evaluator Class
289
340
  ---------------------------
@@ -293,7 +344,7 @@ evaluations, you can create a SimpleEval object, and pass it expressions each
293
344
  time (which should be a bit quicker, and certainly more convenient for some use
294
345
  cases):
295
346
 
296
- .. code-block:: python
347
+ .. code-block:: pycon
297
348
 
298
349
  >>> s = SimpleEval()
299
350
 
@@ -305,6 +356,22 @@ cases):
305
356
 
306
357
  # and so on...
307
358
 
359
+ One useful feature of using the ``SimpleEval`` object is that you can parse an expression
360
+ once, and then evaluate it mulitple times using different ``names``:
361
+
362
+ .. code-block:: python
363
+
364
+ # Set up & Cache the parse tree:
365
+ expression = "foo + bar"
366
+ parsed = s.parse(expression)
367
+
368
+ # evaluate the expression multiple times:
369
+ for names in [{"foo": 1, "bar": 10}, {"foo": 100, "bar": 42}]:
370
+ s.names = names
371
+ print(s.eval(expression, previously_parsed=parsed))
372
+
373
+ for instance. This may help with performance.
374
+
308
375
  You can assign / edit the various options of the ``SimpleEval`` object if you
309
376
  want to. Either assign them during creation (like the ``simple_eval``
310
377
  function)
@@ -356,7 +423,7 @@ comprehensions.
356
423
  Since the primary intention of this library is short expressions - an extra 'sweetener' is
357
424
  enabled by default. You can access a dict (or similar's) keys using the .attr syntax:
358
425
 
359
- .. code-block:: python
426
+ .. code-block:: pycon
360
427
 
361
428
  >>> simple_eval("foo.bar", names={"foo": {"bar": 42}})
362
429
  42
@@ -386,8 +453,7 @@ and then use ``EvalNoMethods`` instead of the ``SimpleEval`` class.
386
453
  Other...
387
454
  --------
388
455
 
389
- The library supports python 3 - but should be mostly compatible (and tested before 0.9.11)
390
- with python 2.7 as well.
456
+ The library supports Python 3.9 and higher.
391
457
 
392
458
  Object attributes that start with ``_`` or ``func_`` are disallowed by default.
393
459
  If you really need that (BE CAREFUL!), then modify the module global
@@ -418,11 +484,18 @@ Or to set the tests running on every file change:
418
484
 
419
485
  (requires ``entr``)
420
486
 
487
+ I'm trying to keep the codebase relatively clean with Black, isort, pylint & mypy.
488
+ See::
489
+
490
+ $ make format
491
+
492
+ and::
493
+
494
+ $ make lint
495
+
421
496
  BEWARE
422
497
  ------
423
498
 
424
- I've done the best I can with this library - but there's no warrenty, no guarentee, nada. A lot of
499
+ I've done the best I can with this library - but there's no warranty, no guarantee, nada. A lot of
425
500
  very clever people think the whole idea of trying to sandbox CPython is impossible. Read the code
426
501
  yourself, and use it at your own risk.
427
-
428
-
@@ -1,19 +1,31 @@
1
1
  simpleeval (Simple Eval)
2
2
  ========================
3
3
 
4
- .. image:: https://github.com/danthedeckie/simpleeval/actions/workflows/ci.yml/badge.svg?branch=gh-actions-build
4
+ .. |build-status| image:: https://github.com/danthedeckie/simpleeval/actions/workflows/ci.yml/badge.svg?branch=gh-actions-build
5
5
  :target: https://github.com/danthedeckie/simpleeval/actions/
6
6
  :alt: Build Status
7
7
 
8
- .. image:: https://codecov.io/gh/danthedeckie/simpleeval/branch/master/graph/badge.svg?token=isRnN1yrca
8
+ .. |code-coverage| image:: https://codecov.io/gh/danthedeckie/simpleeval/branch/master/graph/badge.svg?token=isRnN1yrca
9
9
  :target: https://codecov.io/gh/danthedeckie/simpleeval
10
- :alt: Code Coverage
10
+ :alt: Code Coverage Status
11
11
 
12
- .. image:: https://badge.fury.io/py/simpleeval.svg
12
+ .. |pypi-version| image:: https://badge.fury.io/py/simpleeval.svg
13
13
  :target: https://badge.fury.io/py/simpleeval
14
14
  :alt: PyPI Version
15
15
 
16
- A quick single file library for easily adding evaluatable expressions into
16
+ .. |python-versions| image:: https://img.shields.io/badge/python-3.9_%7C_3.10_%7C_3.11_%7C_3.12_%7C_3.13_%7C_PyPy3.9_%7C_PyPy3.10-blue
17
+ :alt: Static Badge
18
+
19
+ .. |pypi-monthly-downloads| image:: https://img.shields.io/pypi/dm/SimpleEval
20
+ :alt: PyPI - Downloads
21
+
22
+ .. |formatting-with-ruff| image:: https://img.shields.io/badge/-ruff-black?logo=lightning&logoColor=%2300ff00&link=https%3A%2F%2Fdocs.astral.sh%2Fruff%2F
23
+ :alt: Static Badge
24
+
25
+ |build-status| |code-coverage| |pypi-version| |python-versions| |pypi-monthly-downloads| |formatting-with-ruff|
26
+
27
+
28
+ A single file library for easily adding evaluatable expressions into
17
29
  python projects. Say you want to allow a user to set an alarm volume, which
18
30
  could depend on the time of day, alarm level, how many previous alarms had gone
19
31
  off, and if there is music playing at the time.
@@ -21,8 +33,9 @@ off, and if there is music playing at the time.
21
33
  Or if you want to allow simple formulae in a web application, but don't want to
22
34
  give full eval() access, or don't want to run in javascript on the client side.
23
35
 
24
- It's deliberately very simple, pull it in from PyPI (pip or easy_install), or
25
- even just a single file you can dump into a project.
36
+ It's deliberately trying to stay simple to use and not have millions of features,
37
+ pull it in from PyPI (pip or easy_install), or even just a single file you can dump
38
+ into a project.
26
39
 
27
40
  Internally, it's using the amazing python ``ast`` module to parse the
28
41
  expression, which allows very fine control of what is and isn't allowed. It
@@ -37,7 +50,7 @@ Operators_ section below)
37
50
 
38
51
  You should be aware of this when deploying in a public setting.
39
52
 
40
- The defaults are pretty locked down and basic, and it's very easy to add
53
+ The defaults are pretty locked down and basic, and it's easy to add
41
54
  whatever extra specific functionality you need (your own functions,
42
55
  variable/name lookup, etc).
43
56
 
@@ -131,23 +144,36 @@ the defaults:
131
144
  | | ``"spam" in "my breakfast"`` |
132
145
  | | -> ``False`` |
133
146
  +--------+------------------------------------+
147
+ | ``^`` | "bitwise exclusive OR" (xor) |
148
+ | | ``62 ^ 20`` -> ``42`` |
149
+ +--------+------------------------------------+
150
+ | ``|`` | "bitwise OR" |
151
+ | | ``8 | 34`` -> ``42`` |
152
+ +--------+------------------------------------+
153
+ | ``&`` | "bitwise AND" |
154
+ | | ``100 & 63`` -> ``36`` |
155
+ +--------+------------------------------------+
156
+ | ``~`` | "bitwise invert" |
157
+ | | ``~ -43`` -> ``42`` |
158
+ +--------+------------------------------------+
134
159
 
135
160
 
136
- The ``^`` operator is notably missing - not because it's hard, but because it
137
- is often mistaken for a exponent operator, not the bitwise operation that it is
138
- in python. It's trivial to add back in again if you wish (using the class
139
- based evaluator explained below):
161
+ The ``^`` operator is often mistaken for a exponent operator, not the bitwise
162
+ operation that it is in python, so if you want ``3 ^ 2`` to equal ``9``, you can
163
+ replace the operator like this:
140
164
 
141
- .. code-block:: python
165
+ .. code-block:: pycon
142
166
 
143
167
  >>> import ast
144
- >>> import operator
168
+ >>> from simpleeval import safe_power
145
169
 
146
170
  >>> s = SimpleEval()
147
- >>> s.operators[ast.BitXor] = operator.xor
171
+ >>> s.operators[ast.BitXor] = safe_power
148
172
 
149
- >>> s.eval("2 ^ 10")
150
- 8
173
+ >>> s.eval("3 ^ 2")
174
+ 9
175
+
176
+ for example.
151
177
 
152
178
  Limited Power
153
179
  ~~~~~~~~~~~~~
@@ -155,13 +181,13 @@ Limited Power
155
181
  Also note, the ``**`` operator has been locked down by default to have a
156
182
  maximum input value of ``4000000``, which makes it somewhat harder to make
157
183
  expressions which go on for ever. You can change this limit by changing the
158
- ``simpleeval.POWER_MAX`` module level value to whatever is an appropriate value
184
+ ``simpleeval.MAX_POWER`` module level value to whatever is an appropriate value
159
185
  for you (and the hardware that you're running on) or if you want to completely
160
186
  remove all limitations, you can set the ``s.operators[ast.Pow] = operator.pow``
161
187
  or make your own function.
162
188
 
163
189
  On my computer, ``9**9**5`` evaluates almost instantly, but ``9**9**6`` takes
164
- over 30 seconds. Since ``9**7`` is ``4782969``, and so over the ``POWER_MAX``
190
+ over 30 seconds. Since ``9**7`` is ``4782969``, and so over the ``MAX_POWER``
165
191
  limit, it throws a ``NumberTooHigh`` exception for you. (Otherwise it would go
166
192
  on for hours, or until the computer runs out of memory)
167
193
 
@@ -179,7 +205,7 @@ If Expressions
179
205
 
180
206
  You can use python style ``if x then y else z`` type expressions:
181
207
 
182
- .. code-block:: python
208
+ .. code-block:: pycon
183
209
 
184
210
  >>> simple_eval("'equal' if x == y else 'not equal'",
185
211
  names={"x": 1, "y": 2})
@@ -187,7 +213,7 @@ You can use python style ``if x then y else z`` type expressions:
187
213
 
188
214
  which, of course, can be nested:
189
215
 
190
- .. code-block:: python
216
+ .. code-block:: pycon
191
217
 
192
218
  >>> simple_eval("'a' if 1 == 2 else 'b' if 2 == 3 else 'c'")
193
219
  'c'
@@ -198,7 +224,7 @@ Functions
198
224
 
199
225
  You can define functions which you'd like the expresssions to have access to:
200
226
 
201
- .. code-block:: python
227
+ .. code-block:: pycon
202
228
 
203
229
  >>> simple_eval("double(21)", functions={"double": lambda x:x*2})
204
230
  42
@@ -206,7 +232,7 @@ You can define functions which you'd like the expresssions to have access to:
206
232
  You can define "real" functions to pass in rather than lambdas, of course too,
207
233
  and even re-name them so that expressions can be shorter
208
234
 
209
- .. code-block:: python
235
+ .. code-block:: pycon
210
236
 
211
237
  >>> def double(x):
212
238
  return x * 2
@@ -225,13 +251,13 @@ are provided in the ``DEFAULT_FUNCTIONS`` dict:
225
251
  +----------------+--------------------------------------------------+
226
252
  | ``float(x)`` | Convert ``x`` to a ``float``. |
227
253
  +----------------+--------------------------------------------------+
228
- | ``str(x)`` | Convert ``x`` to a ``str`` (``unicode`` in py2) |
254
+ | ``str(x)`` | Convert ``x`` to a ``str`` |
229
255
  +----------------+--------------------------------------------------+
230
256
 
231
257
  If you want to provide a list of functions, but want to keep these as well,
232
258
  then you can do a normal python ``.copy()`` & ``.update``:
233
259
 
234
- .. code-block:: python
260
+ .. code-block:: pycon
235
261
 
236
262
  >>> my_functions = simpleeval.DEFAULT_FUNCTIONS.copy()
237
263
  >>> my_functions.update(
@@ -246,7 +272,7 @@ Names
246
272
  Sometimes it's useful to have variables available, which in python terminology
247
273
  are called 'names'.
248
274
 
249
- .. code-block:: python
275
+ .. code-block:: pycon
250
276
 
251
277
  >>> simple_eval("a + b", names={"a": 11, "b": 100})
252
278
  111
@@ -254,7 +280,7 @@ are called 'names'.
254
280
  You can also hand the handling of names over to a function, if you prefer:
255
281
 
256
282
 
257
- .. code-block:: python
283
+ .. code-block:: pycon
258
284
 
259
285
  >>> def name_handler(node):
260
286
  return ord(node.id[0].lower(a))-96
@@ -263,9 +289,34 @@ You can also hand the handling of names over to a function, if you prefer:
263
289
  3
264
290
 
265
291
  That was a bit of a silly example, but you could use this for pulling values
266
- from a database or file, say, or doing some kind of caching system.
292
+ from a database or file, looking up spreadsheet cells, say, or doing some kind of caching system.
293
+
294
+ In general, when it attempts to find a variable by name, if it cannot find one,
295
+ then it will look in the ``functions`` for a function of that name. If you want your name handler
296
+ function to return an "I can't find that name!", then it should raise a ``simpleeval.NameNotDefined``
297
+ exception. Eg:
267
298
 
268
- The two default names that are provided are ``True`` and ``False``. So if you want to provide your own names, but want ``True`` and ``False`` to keep working, either provide them yourself, or ``.copy()`` and ``.update`` the ``DEFAULT_NAMES``. (See functions example above).
299
+ .. code-block:: pycon
300
+
301
+ >>> def name_handler(node):
302
+ ... if node.id[0] == 'a':
303
+ ... return 21
304
+ ... raise NameNotDefined(node.id[0], "Not found")
305
+ ...
306
+ ... simple_eval('a + a', names=name_handler, functions={"b": 100})
307
+
308
+ 42
309
+
310
+ >>> simple_eval('a + b', names=name_handler, functions={'b': 100})
311
+ 121
312
+
313
+ (Note: in that example, putting a number directly into the ``functions`` dict was done just to
314
+ show the fall-back to functions. Normally only put actual callables in there.)
315
+
316
+
317
+ The two default names that are provided are ``True`` and ``False``. So if you want to provide
318
+ your own names, but want ``True`` and ``False`` to keep working, either provide them yourself,
319
+ or ``.copy()`` and ``.update`` the ``DEFAULT_NAMES``. (See functions example above).
269
320
 
270
321
  Creating an Evaluator Class
271
322
  ---------------------------
@@ -275,7 +326,7 @@ evaluations, you can create a SimpleEval object, and pass it expressions each
275
326
  time (which should be a bit quicker, and certainly more convenient for some use
276
327
  cases):
277
328
 
278
- .. code-block:: python
329
+ .. code-block:: pycon
279
330
 
280
331
  >>> s = SimpleEval()
281
332
 
@@ -287,6 +338,22 @@ cases):
287
338
 
288
339
  # and so on...
289
340
 
341
+ One useful feature of using the ``SimpleEval`` object is that you can parse an expression
342
+ once, and then evaluate it mulitple times using different ``names``:
343
+
344
+ .. code-block:: python
345
+
346
+ # Set up & Cache the parse tree:
347
+ expression = "foo + bar"
348
+ parsed = s.parse(expression)
349
+
350
+ # evaluate the expression multiple times:
351
+ for names in [{"foo": 1, "bar": 10}, {"foo": 100, "bar": 42}]:
352
+ s.names = names
353
+ print(s.eval(expression, previously_parsed=parsed))
354
+
355
+ for instance. This may help with performance.
356
+
290
357
  You can assign / edit the various options of the ``SimpleEval`` object if you
291
358
  want to. Either assign them during creation (like the ``simple_eval``
292
359
  function)
@@ -338,7 +405,7 @@ comprehensions.
338
405
  Since the primary intention of this library is short expressions - an extra 'sweetener' is
339
406
  enabled by default. You can access a dict (or similar's) keys using the .attr syntax:
340
407
 
341
- .. code-block:: python
408
+ .. code-block:: pycon
342
409
 
343
410
  >>> simple_eval("foo.bar", names={"foo": {"bar": 42}})
344
411
  42
@@ -368,8 +435,7 @@ and then use ``EvalNoMethods`` instead of the ``SimpleEval`` class.
368
435
  Other...
369
436
  --------
370
437
 
371
- The library supports python 3 - but should be mostly compatible (and tested before 0.9.11)
372
- with python 2.7 as well.
438
+ The library supports Python 3.9 and higher.
373
439
 
374
440
  Object attributes that start with ``_`` or ``func_`` are disallowed by default.
375
441
  If you really need that (BE CAREFUL!), then modify the module global
@@ -400,9 +466,18 @@ Or to set the tests running on every file change:
400
466
 
401
467
  (requires ``entr``)
402
468
 
469
+ I'm trying to keep the codebase relatively clean with Black, isort, pylint & mypy.
470
+ See::
471
+
472
+ $ make format
473
+
474
+ and::
475
+
476
+ $ make lint
477
+
403
478
  BEWARE
404
479
  ------
405
480
 
406
- I've done the best I can with this library - but there's no warrenty, no guarentee, nada. A lot of
481
+ I've done the best I can with this library - but there's no warranty, no guarantee, nada. A lot of
407
482
  very clever people think the whole idea of trying to sandbox CPython is impossible. Read the code
408
483
  yourself, and use it at your own risk.
@@ -1,11 +1,14 @@
1
1
  [build-system]
2
- requires = ["setuptools>=30.3.0", "wheel", "build"]
2
+ requires = ["setuptools>=30.3.0", "wheel"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [tool.black]
6
6
  line-length = 99
7
7
  target-version = ['py310']
8
8
 
9
+ [tool.ruff]
10
+ line-length = 99
11
+
9
12
  [tool.isort]
10
13
  combine_as_imports = true
11
14
  float_to_top = true
@@ -16,7 +19,9 @@ multi_line_output = 3
16
19
 
17
20
  [tool.pylint.messages_control]
18
21
  disable = [
22
+ "fixme",
19
23
  "consider-using-f-string",
24
+ "raise-missing-from",
20
25
  "invalid-name",
21
26
  "too-few-public-methods",
22
27
  "too-many-public-methods",