sopel.tools.calculation#

Tools to help safely do calculations from user input

New in version 5.3.

Note

Most of this is internal machinery. eval_equation() is the “public” part, used by Sopel’s built-in calc plugin.

class sopel.tools.calculation.EquationEvaluator#

Specific subclass of ExpressionEvaluator for simple math

This presets the allowed operators to safeguard against user input that could try to do things that will adversely affect the running bot, while still letting users pass arbitrary mathematical expressions using the available (mostly arithmetic) operators.

class sopel.tools.calculation.ExpressionEvaluator(
bin_ops: dict[type[ast.operator], Callable] | None = None,
unary_ops: dict[type[ast.unaryop], Callable] | None = None,
)#

A generic class for evaluating limited forms of Python expressions.

Parameters:
  • bin_ops (dict) – optional; see notes

  • unary_ops (dict) – optional; see notes

Instances can pass binary_ops and unary_ops arguments with dicts of the form {ast.Node, function}. When the ast.Node used as key is found, it will be evaluated using the given function.

exception Error#

Internal exception type for ExpressionEvaluators.

sopel.tools.calculation.eval_equation = <sopel.tools.calculation.EquationEvaluator object>#

Evaluates a Python equation expression and returns the result.

Parameters:
  • equation (str) – the equation to evaluate

  • timeout (int or float) – optional timeout value

Supports addition (+), subtraction (-), multiplication (*), division (/), power (**) and modulo (%).

sopel.tools.calculation.guarded_mul(left: float, right: float) float#

Multiply two values, guarding against overly large inputs.

Parameters:
  • left – the left operand

  • right – the right operand

Raises:

ValueError – if the inputs are too large to handle safely

sopel.tools.calculation.guarded_pow(num: float, exp: float) float#

Raise a number to a power, guarding against overly large inputs.

Parameters:
  • num – base

  • exp – exponent

Raises:

ValueError – if the inputs are too large to handle safely

sopel.tools.calculation.pow_complexity(num: int, exp: int) float#

Estimate the worst case time pow() takes to calculate.

Parameters:
  • num – base

  • exp – exponent

This function is based on experimental data from the time it takes to calculate num**exp in 32-bit CPython 2.7.6 on an Intel Core i7-2670QM laptop running Windows.

It tries to implement this surface: x=exp, y=num

1e5

2e5

3e5

4e5

5e5

6e5

7e5

8e5

9e5

e1

0.03

0.09

0.16

0.25

0.35

0.46

0.60

0.73

0.88

e2

0.08

0.24

0.46

0.73

1.03

1.40

1.80

2.21

2.63

e3

0.15

0.46

0.87

1.39

1.99

2.63

3.35

4.18

5.15

e4

0.24

0.73

1.39

2.20

3.11

4.18

5.39

6.59

7.88

e5

0.34

1.03

2.00

3.12

4.48

5.97

7.56

9.37

11.34

e6

0.46

1.39

2.62

4.16

5.97

7.86

10.09

12.56

15.39

e7

0.60

1.79

3.34

5.39

7.60

10.16

13.00

16.23

19.44

e8

0.73

2.20

4.18

6.60

9.37

12.60

16.26

19.83

23.70

e9

0.87

2.62

5.15

7.93

11.34

15.44

19.40

23.66

28.58

For powers of 2 it tries to implement this surface:

1e7

2e7

3e7

4e7

5e7

6e7

7e7

8e7

9e7

1

0.00

0.00

0.00

0.00

0.00

0.00

0.00

0.00

0.00

2

0.21

0.44

0.71

0.92

1.20

1.49

1.66

1.95

2.23

4

0.43

0.91

1.49

1.96

2.50

3.13

3.54

4.10

4.77

8

0.70

1.50

2.24

3.16

3.83

4.66

5.58

6.56

7.67

The function numbers were selected by starting with the theoretical complexity of exp * log2(num)**2 and fiddling with the exponents until it more or less matched with the table.

Because this function is based on a limited set of data it might not give accurate results outside these boundaries. The results derived from large num and exp were quite accurate for small num and very large exp though, except when num was a power of 2.