Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,18 @@ jobs:

- name: Run tests (unittest)
run: |
python -m unittest discover -v
pip install coverage
make coverage

- name: Coveralls upload
uses: coverallsapp/github-action@v2
if: "${{ success() }}"
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
flag-name: "python-${{ matrix.python }}"
parallel: true
format: cobertura
files: coverage.xml

test-legacy:
name: Tests (Python 2.7 via container)
Expand Down Expand Up @@ -77,3 +88,10 @@ jobs:
- name: Aggregate result
run: |
echo "Modern matrix + legacy (2.7) complete. Check job logs for details."

- name: Close parallel Coveralls build
uses: coverallsapp/github-action@v2
if: "${{ success() }}"
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
parallel-finished: true
12 changes: 0 additions & 12 deletions .travis.yml

This file was deleted.

60 changes: 60 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
and we adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed

- Implement true short-circuit for 'and'/'or' returning deciding operand
- Make chained comparisons evaluate pairwise with short-circuit
- Restrict function calls to direct names only (reject attribute/lambda)

### Fixed

- Add `visit_Constant` for Python 3.8+ unified literals support

## [0.0.5] - 2018-01-29

### Added

- Support simple (augmented) assignment operations like `variable = expression`
and `variable += expression` via the `assignment` parameter and property,
with a `modified_variables` property
- Variable scope can be replaced
- Add interactive interpreter script entry point

### Changed

- Interpreter keeps context of variable scope
- Install script as `expression` instead of `expr` to avoid conflict
- Show only the expression line in the traceback

## [0.0.4] - 2017-10-05

### Added

- Track which variables are used in expression with a `used_variables` property

## [0.0.3] - 2017-10-05

### Fixed

- Correct Python 3.6+ starred arguments detection
- Disallow overriding predefined names `True`, `False` and `None`
- Always use float division for `/`

## [0.0.1] - 2017-10-05

### Added

- Initial version of expression parser

[Unreleased]: https://github.com/lhelwerd/expression-parser/compare/v0.0.5...HEAD
[0.0.5]: https://github.com/lhelwerd/expression-parser/releases/tag/v0.0.5
[0.0.4]: https://github.com/lhelwerd/expression-parser/releases/tag/v0.0.4
[0.0.3]: https://github.com/lhelwerd/expression-parser/releases/tag/v0.0.3
[0.0.1]: https://github.com/lhelwerd/expression-parser/releases/tag/v0.0.1
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
SOURCES=*.py expression/*.py tests/*.py
COVERAGE=coverage
TEST=-m unittest discover -s tests -p '*.py'

Expand All @@ -18,7 +19,11 @@ get_version:

.PHONY: pylint
pylint:
pylint *.py expression/*.py tests/*.py
pylint $(SOURCES)

.PHONY: ruff
ruff:
ruff check $(SOURCES)

.PHONY: tag
tag: get_version
Expand All @@ -45,6 +50,7 @@ test:
coverage:
$(COVERAGE) run $(TEST)
$(COVERAGE) report -m
$(COVERAGE) xml -i -o coverage.xml

.PHONY: clean
clean:
Expand Down
41 changes: 22 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
# Python sandboxed expression parser

[![PyPI](https://img.shields.io/pypi/v/expression-parser.svg)](https://pypi.python.org/pypi/expression-parser)
[![Build
Status](https://travis-ci.org/lhelwerd/expression-parser.svg?branch=master)](https://travis-ci.org/lhelwerd/expression-parser)
[![Coverage
Status](https://coveralls.io/repos/github/lhelwerd/expression-parser/badge.svg?branch=master)](https://coveralls.io/github/lhelwerd/expression-parser?branch=master)
[![PyPI Versions](https://img.shields.io/pypi/pyversions/expression-parser.svg)](https://pypi.python.org/pypi/expression-parser/#files)
[![CI Status](https://github.com/lhelwerd/expression-parser/actions/workflows/ci.yml/badge.svg)](https://github.com/lhelwerd/expression-parser/actions/workflows/ci.yml)
[![Coverage Status](https://coveralls.io/repos/github/lhelwerd/expression-parser/badge.svg?branch=master)](https://coveralls.io/github/lhelwerd/expression-parser?branch=master)

This parser can calculate the results of a single simple expression,
This parser calculates the results of a single simple expression,
disallowing any complicated functions or control structures, with support for
custom variable and function environment contexts.

## Features

- Support for all boolean, binary, unary, and comparative operators as in
Python itself.
Python itself, including short-circuiting and chained comparison logic.
- Support for inline `if..else` expressions.
- Support for assignments and augmented assignments like `+=`, only if enabled
explicitly.
- All other control structures and multiple expressions are disallowed.
- Isolation from execution context using a restricted scope.
- Separate scope for variables and functions to avoid abusing one or the other.
- Function calls may use specific keyword arguments.
- Function calls must use direct names (function attributes and lambda
functions are rejected) and must not provide starred arguments.
- Errors from parsing or evaluating the expression are raised as `SyntaxError`
with appropriate context parameters to make error display easier (works with
default traceback output).
- A successful parse yields the result of the evaluated expression. Successful
assignments of variables are stored in a property `modified_variables`.
A separate property `used_variables` provides a set of variable names used in
the evaluation of the expression excluding the assignment targets.
- Supports both Python 2.7 and 3.6 AST syntax trees.
- Supports Python 2.7 and 3.6-3.14 AST syntax trees.
- Python 3+ conventions are used whenever possible: Specifically, the division
operator `/` always returns floats instead of integers, and `True`, `False`
and `None` are reserved named constants and cannot be overridden through the
Expand All @@ -47,14 +47,14 @@ Not supported (often by design):

## Requirements

The expression parser has been tested to work on Python 2.7 and 3.6. This
The expression parser has been tested to work on Python 2.7 and 3.8+. This
package has no other dependencies and works with only core Python modules.

## Installation

Install the latest version from PyPI using:

```
```bash
pip install expression-parser
```

Expand Down Expand Up @@ -92,8 +92,11 @@ arguments, such as `parser = expression.Expression_Parser()`, or by passing the
scope along to it in dictionaries of values and functions, respectively:

```python
parser = expression.Expression_Parser(variables=variables, functions=functions,
assignment=bool(assignment))
parser = expression.Expression_Parser(
variables=variables,
functions=functions,
assignment=bool(assignment)
)
```

You can also set a new dictionary for `variables`, and enable or disable
Expand All @@ -103,22 +106,22 @@ via the properties of the created object.
Now you can use this parser to evaluate any valid expression:

```python
print(parser.parse('1+2'))
>> print(parser.parse('1+2'))
3
print(parser.parse('pi > 3'))
>> print(parser.parse('pi > 3'))
True
print(parser.parse('int(log(e))'))
>> print(parser.parse('int(log(e))'))
1
```

## Development

- [Travis](https://travis-ci.org/lhelwerd/expression-parser) is used to run
unit tests and report on coverage.
- [GitHub Actions](https://github.com/lhelwerd/expression-parser/actions) is
used to run unit tests and report on coverage.
- [Coveralls](https://coveralls.io/github/lhelwerd/expression-parser) receives
coverage reports and tracks them.
- You can perform local lint checks, tests and coverage during development
using `make pylint`, `make test` and `make coverage`, respectively.
- You can perform local lint checks, tests or coverage during development using
`make ruff`, `make pylint`, `make test` or `make coverage`, respectively.
- We publish releases to [PyPI](https://pypi.python.org/pypi/expression-parser)
using `make release` which performs lint and unit test checks.

Expand Down
5 changes: 0 additions & 5 deletions expression/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,6 @@ def visit_Starred(self, node):
Visit a starred function keyword argument node.
"""

# pylint: disable=no-self-use

raise SyntaxError(
"Star arguments are not supported", ("", node.lineno, node.col_offset, "")
)
Expand All @@ -429,7 +427,6 @@ def visit_Num(self, node):
Visit a literal number node.
"""

# pylint: disable=no-self-use
return node.n

def visit_Name(self, node):
Expand All @@ -453,10 +450,8 @@ def visit_NameConstant(self, node):
Visit a named constant singleton node (Python 3).
"""

# pylint: disable=no-self-use
return node.value

def visit_Constant(self, node): # Python 3.8+ unified literal node
"""Visit a constant literal node (numbers, strings, booleans, None, Ellipsis)."""
# pylint: disable=no-self-use
return node.value
8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Software Development :: Interpreters",
],
keywords=["expression", "parser", "sandbox"],
Expand Down