diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c209842..fa3155e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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) @@ -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 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e407b11..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -sudo: false -dist: trusty -language: python -python: - - "2.7" - - "3.6" -install: - - pip install coverage coveralls -script: - - make coverage -after_success: - - coveralls diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..67026fa --- /dev/null +++ b/CHANGELOG.md @@ -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 diff --git a/Makefile b/Makefile index fed31c8..4ba81d5 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +SOURCES=*.py expression/*.py tests/*.py COVERAGE=coverage TEST=-m unittest discover -s tests -p '*.py' @@ -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 @@ -45,6 +50,7 @@ test: coverage: $(COVERAGE) run $(TEST) $(COVERAGE) report -m + $(COVERAGE) xml -i -o coverage.xml .PHONY: clean clean: diff --git a/README.md b/README.md index 06524ee..a5755a8 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ # 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). @@ -28,7 +28,7 @@ custom variable and function environment contexts. 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 @@ -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 ``` @@ -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 @@ -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. diff --git a/expression/parser.py b/expression/parser.py index a790e66..4e13dc2 100644 --- a/expression/parser.py +++ b/expression/parser.py @@ -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, "") ) @@ -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): @@ -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 diff --git a/setup.py b/setup.py index aa0b3a1..1c4d382 100644 --- a/setup.py +++ b/setup.py @@ -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"],