summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--python-pure-eval.spec691
-rw-r--r--sources1
3 files changed, 693 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..ab3410e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/pure_eval-0.2.2.tar.gz
diff --git a/python-pure-eval.spec b/python-pure-eval.spec
new file mode 100644
index 0000000..8e70954
--- /dev/null
+++ b/python-pure-eval.spec
@@ -0,0 +1,691 @@
+%global _empty_manifest_terminate_build 0
+Name: python-pure-eval
+Version: 0.2.2
+Release: 1
+Summary: Safely evaluate AST nodes without side effects
+License: MIT
+URL: http://github.com/alexmojaki/pure_eval
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/97/5a/0bc937c25d3ce4e0a74335222aee05455d6afa2888032185f8ab50cdf6fd/pure_eval-0.2.2.tar.gz
+BuildArch: noarch
+
+Requires: python3-pytest
+
+%description
+# `pure_eval`
+
+[![Build Status](https://travis-ci.org/alexmojaki/pure_eval.svg?branch=master)](https://travis-ci.org/alexmojaki/pure_eval) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/pure_eval/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/pure_eval?branch=master) [![Supports Python versions 3.5+](https://img.shields.io/pypi/pyversions/pure_eval.svg)](https://pypi.python.org/pypi/pure_eval)
+
+This is a Python package that lets you safely evaluate certain AST nodes without triggering arbitrary code that may have unwanted side effects.
+
+It can be installed from PyPI:
+
+ pip install pure_eval
+
+To demonstrate usage, suppose we have an object defined as follows:
+
+```python
+class Rectangle:
+ def __init__(self, width, height):
+ self.width = width
+ self.height = height
+
+ @property
+ def area(self):
+ print("Calculating area...")
+ return self.width * self.height
+
+
+rect = Rectangle(3, 5)
+```
+
+Given the `rect` object, we want to evaluate whatever expressions we can in this source code:
+
+```python
+source = "(rect.width, rect.height, rect.area)"
+```
+
+This library works with the AST, so let's parse the source code and peek inside:
+
+```python
+import ast
+
+tree = ast.parse(source)
+the_tuple = tree.body[0].value
+for node in the_tuple.elts:
+ print(ast.dump(node))
+```
+
+Output:
+
+```python
+Attribute(value=Name(id='rect', ctx=Load()), attr='width', ctx=Load())
+Attribute(value=Name(id='rect', ctx=Load()), attr='height', ctx=Load())
+Attribute(value=Name(id='rect', ctx=Load()), attr='area', ctx=Load())
+```
+
+Now to actually use the library. First construct an Evaluator:
+
+```python
+from pure_eval import Evaluator
+
+evaluator = Evaluator({"rect": rect})
+```
+
+The argument to `Evaluator` should be a mapping from variable names to their values. Or if you have access to the stack frame where `rect` is defined, you can instead use:
+
+```python
+evaluator = Evaluator.from_frame(frame)
+```
+
+Now to evaluate some nodes, using `evaluator[node]`:
+
+```python
+print("rect.width:", evaluator[the_tuple.elts[0]])
+print("rect:", evaluator[the_tuple.elts[0].value])
+```
+
+Output:
+
+```
+rect.width: 3
+rect: <__main__.Rectangle object at 0x105b0dd30>
+```
+
+OK, but you could have done the same thing with `eval`. The useful part is that it will refuse to evaluate the property `rect.area` because that would trigger unknown code. If we try, it'll raise a `CannotEval` exception.
+
+```python
+from pure_eval import CannotEval
+
+try:
+ print("rect.area:", evaluator[the_tuple.elts[2]]) # fails
+except CannotEval as e:
+ print(e) # prints CannotEval
+```
+
+To find all the expressions that can be evaluated in a tree:
+
+```python
+for node, value in evaluator.find_expressions(tree):
+ print(ast.dump(node), value)
+```
+
+Output:
+
+```python
+Attribute(value=Name(id='rect', ctx=Load()), attr='width', ctx=Load()) 3
+Attribute(value=Name(id='rect', ctx=Load()), attr='height', ctx=Load()) 5
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+```
+
+Note that this includes `rect` three times, once for each appearance in the source code. Since all these nodes are equivalent, we can group them together:
+
+```python
+from pure_eval import group_expressions
+
+for nodes, values in group_expressions(evaluator.find_expressions(tree)):
+ print(len(nodes), "nodes with value:", values)
+```
+
+Output:
+
+```
+1 nodes with value: 3
+1 nodes with value: 5
+3 nodes with value: <__main__.Rectangle object at 0x10d374d30>
+```
+
+If we want to list all the expressions in a tree, we may want to filter out certain expressions whose values are obvious. For example, suppose we have a function `foo`:
+
+```python
+def foo():
+ pass
+```
+
+If we refer to `foo` by its name as usual, then that's not interesting:
+
+```python
+from pure_eval import is_expression_interesting
+
+node = ast.parse('foo').body[0].value
+print(ast.dump(node))
+print(is_expression_interesting(node, foo))
+```
+
+Output:
+
+```python
+Name(id='foo', ctx=Load())
+False
+```
+
+But if we refer to it by a different name, then it's interesting:
+
+```python
+node = ast.parse('bar').body[0].value
+print(ast.dump(node))
+print(is_expression_interesting(node, foo))
+```
+
+Output:
+
+```python
+Name(id='bar', ctx=Load())
+True
+```
+
+In general `is_expression_interesting` returns False for the following values:
+- Literals (e.g. `123`, `'abc'`, `[1, 2, 3]`, `{'a': (), 'b': ([1, 2], [3])}`)
+- Variables or attributes whose name is equal to the value's `__name__`, such as `foo` above or `self.foo` if it was a method.
+- Builtins (e.g. `len`) referred to by their usual name.
+
+To make things easier, you can combine finding expressions, grouping them, and filtering out the obvious ones with:
+
+```python
+evaluator.interesting_expressions_grouped(root)
+```
+
+To get the source code of an AST node, I recommend [asttokens](https://github.com/gristlabs/asttokens).
+
+Here's a complete example that brings it all together:
+
+```python
+from asttokens import ASTTokens
+from pure_eval import Evaluator
+
+source = """
+x = 1
+d = {x: 2}
+y = d[x]
+"""
+
+names = {}
+exec(source, names)
+atok = ASTTokens(source, parse=True)
+for nodes, value in Evaluator(names).interesting_expressions_grouped(atok.tree):
+ print(atok.get_text(nodes[0]), "=", value)
+```
+
+Output:
+
+```python
+x = 1
+d = {1: 2}
+y = 2
+d[x] = 2
+```
+
+
+
+
+%package -n python3-pure-eval
+Summary: Safely evaluate AST nodes without side effects
+Provides: python-pure-eval
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-pure-eval
+# `pure_eval`
+
+[![Build Status](https://travis-ci.org/alexmojaki/pure_eval.svg?branch=master)](https://travis-ci.org/alexmojaki/pure_eval) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/pure_eval/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/pure_eval?branch=master) [![Supports Python versions 3.5+](https://img.shields.io/pypi/pyversions/pure_eval.svg)](https://pypi.python.org/pypi/pure_eval)
+
+This is a Python package that lets you safely evaluate certain AST nodes without triggering arbitrary code that may have unwanted side effects.
+
+It can be installed from PyPI:
+
+ pip install pure_eval
+
+To demonstrate usage, suppose we have an object defined as follows:
+
+```python
+class Rectangle:
+ def __init__(self, width, height):
+ self.width = width
+ self.height = height
+
+ @property
+ def area(self):
+ print("Calculating area...")
+ return self.width * self.height
+
+
+rect = Rectangle(3, 5)
+```
+
+Given the `rect` object, we want to evaluate whatever expressions we can in this source code:
+
+```python
+source = "(rect.width, rect.height, rect.area)"
+```
+
+This library works with the AST, so let's parse the source code and peek inside:
+
+```python
+import ast
+
+tree = ast.parse(source)
+the_tuple = tree.body[0].value
+for node in the_tuple.elts:
+ print(ast.dump(node))
+```
+
+Output:
+
+```python
+Attribute(value=Name(id='rect', ctx=Load()), attr='width', ctx=Load())
+Attribute(value=Name(id='rect', ctx=Load()), attr='height', ctx=Load())
+Attribute(value=Name(id='rect', ctx=Load()), attr='area', ctx=Load())
+```
+
+Now to actually use the library. First construct an Evaluator:
+
+```python
+from pure_eval import Evaluator
+
+evaluator = Evaluator({"rect": rect})
+```
+
+The argument to `Evaluator` should be a mapping from variable names to their values. Or if you have access to the stack frame where `rect` is defined, you can instead use:
+
+```python
+evaluator = Evaluator.from_frame(frame)
+```
+
+Now to evaluate some nodes, using `evaluator[node]`:
+
+```python
+print("rect.width:", evaluator[the_tuple.elts[0]])
+print("rect:", evaluator[the_tuple.elts[0].value])
+```
+
+Output:
+
+```
+rect.width: 3
+rect: <__main__.Rectangle object at 0x105b0dd30>
+```
+
+OK, but you could have done the same thing with `eval`. The useful part is that it will refuse to evaluate the property `rect.area` because that would trigger unknown code. If we try, it'll raise a `CannotEval` exception.
+
+```python
+from pure_eval import CannotEval
+
+try:
+ print("rect.area:", evaluator[the_tuple.elts[2]]) # fails
+except CannotEval as e:
+ print(e) # prints CannotEval
+```
+
+To find all the expressions that can be evaluated in a tree:
+
+```python
+for node, value in evaluator.find_expressions(tree):
+ print(ast.dump(node), value)
+```
+
+Output:
+
+```python
+Attribute(value=Name(id='rect', ctx=Load()), attr='width', ctx=Load()) 3
+Attribute(value=Name(id='rect', ctx=Load()), attr='height', ctx=Load()) 5
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+```
+
+Note that this includes `rect` three times, once for each appearance in the source code. Since all these nodes are equivalent, we can group them together:
+
+```python
+from pure_eval import group_expressions
+
+for nodes, values in group_expressions(evaluator.find_expressions(tree)):
+ print(len(nodes), "nodes with value:", values)
+```
+
+Output:
+
+```
+1 nodes with value: 3
+1 nodes with value: 5
+3 nodes with value: <__main__.Rectangle object at 0x10d374d30>
+```
+
+If we want to list all the expressions in a tree, we may want to filter out certain expressions whose values are obvious. For example, suppose we have a function `foo`:
+
+```python
+def foo():
+ pass
+```
+
+If we refer to `foo` by its name as usual, then that's not interesting:
+
+```python
+from pure_eval import is_expression_interesting
+
+node = ast.parse('foo').body[0].value
+print(ast.dump(node))
+print(is_expression_interesting(node, foo))
+```
+
+Output:
+
+```python
+Name(id='foo', ctx=Load())
+False
+```
+
+But if we refer to it by a different name, then it's interesting:
+
+```python
+node = ast.parse('bar').body[0].value
+print(ast.dump(node))
+print(is_expression_interesting(node, foo))
+```
+
+Output:
+
+```python
+Name(id='bar', ctx=Load())
+True
+```
+
+In general `is_expression_interesting` returns False for the following values:
+- Literals (e.g. `123`, `'abc'`, `[1, 2, 3]`, `{'a': (), 'b': ([1, 2], [3])}`)
+- Variables or attributes whose name is equal to the value's `__name__`, such as `foo` above or `self.foo` if it was a method.
+- Builtins (e.g. `len`) referred to by their usual name.
+
+To make things easier, you can combine finding expressions, grouping them, and filtering out the obvious ones with:
+
+```python
+evaluator.interesting_expressions_grouped(root)
+```
+
+To get the source code of an AST node, I recommend [asttokens](https://github.com/gristlabs/asttokens).
+
+Here's a complete example that brings it all together:
+
+```python
+from asttokens import ASTTokens
+from pure_eval import Evaluator
+
+source = """
+x = 1
+d = {x: 2}
+y = d[x]
+"""
+
+names = {}
+exec(source, names)
+atok = ASTTokens(source, parse=True)
+for nodes, value in Evaluator(names).interesting_expressions_grouped(atok.tree):
+ print(atok.get_text(nodes[0]), "=", value)
+```
+
+Output:
+
+```python
+x = 1
+d = {1: 2}
+y = 2
+d[x] = 2
+```
+
+
+
+
+%package help
+Summary: Development documents and examples for pure-eval
+Provides: python3-pure-eval-doc
+%description help
+# `pure_eval`
+
+[![Build Status](https://travis-ci.org/alexmojaki/pure_eval.svg?branch=master)](https://travis-ci.org/alexmojaki/pure_eval) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/pure_eval/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/pure_eval?branch=master) [![Supports Python versions 3.5+](https://img.shields.io/pypi/pyversions/pure_eval.svg)](https://pypi.python.org/pypi/pure_eval)
+
+This is a Python package that lets you safely evaluate certain AST nodes without triggering arbitrary code that may have unwanted side effects.
+
+It can be installed from PyPI:
+
+ pip install pure_eval
+
+To demonstrate usage, suppose we have an object defined as follows:
+
+```python
+class Rectangle:
+ def __init__(self, width, height):
+ self.width = width
+ self.height = height
+
+ @property
+ def area(self):
+ print("Calculating area...")
+ return self.width * self.height
+
+
+rect = Rectangle(3, 5)
+```
+
+Given the `rect` object, we want to evaluate whatever expressions we can in this source code:
+
+```python
+source = "(rect.width, rect.height, rect.area)"
+```
+
+This library works with the AST, so let's parse the source code and peek inside:
+
+```python
+import ast
+
+tree = ast.parse(source)
+the_tuple = tree.body[0].value
+for node in the_tuple.elts:
+ print(ast.dump(node))
+```
+
+Output:
+
+```python
+Attribute(value=Name(id='rect', ctx=Load()), attr='width', ctx=Load())
+Attribute(value=Name(id='rect', ctx=Load()), attr='height', ctx=Load())
+Attribute(value=Name(id='rect', ctx=Load()), attr='area', ctx=Load())
+```
+
+Now to actually use the library. First construct an Evaluator:
+
+```python
+from pure_eval import Evaluator
+
+evaluator = Evaluator({"rect": rect})
+```
+
+The argument to `Evaluator` should be a mapping from variable names to their values. Or if you have access to the stack frame where `rect` is defined, you can instead use:
+
+```python
+evaluator = Evaluator.from_frame(frame)
+```
+
+Now to evaluate some nodes, using `evaluator[node]`:
+
+```python
+print("rect.width:", evaluator[the_tuple.elts[0]])
+print("rect:", evaluator[the_tuple.elts[0].value])
+```
+
+Output:
+
+```
+rect.width: 3
+rect: <__main__.Rectangle object at 0x105b0dd30>
+```
+
+OK, but you could have done the same thing with `eval`. The useful part is that it will refuse to evaluate the property `rect.area` because that would trigger unknown code. If we try, it'll raise a `CannotEval` exception.
+
+```python
+from pure_eval import CannotEval
+
+try:
+ print("rect.area:", evaluator[the_tuple.elts[2]]) # fails
+except CannotEval as e:
+ print(e) # prints CannotEval
+```
+
+To find all the expressions that can be evaluated in a tree:
+
+```python
+for node, value in evaluator.find_expressions(tree):
+ print(ast.dump(node), value)
+```
+
+Output:
+
+```python
+Attribute(value=Name(id='rect', ctx=Load()), attr='width', ctx=Load()) 3
+Attribute(value=Name(id='rect', ctx=Load()), attr='height', ctx=Load()) 5
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+Name(id='rect', ctx=Load()) <__main__.Rectangle object at 0x105568d30>
+```
+
+Note that this includes `rect` three times, once for each appearance in the source code. Since all these nodes are equivalent, we can group them together:
+
+```python
+from pure_eval import group_expressions
+
+for nodes, values in group_expressions(evaluator.find_expressions(tree)):
+ print(len(nodes), "nodes with value:", values)
+```
+
+Output:
+
+```
+1 nodes with value: 3
+1 nodes with value: 5
+3 nodes with value: <__main__.Rectangle object at 0x10d374d30>
+```
+
+If we want to list all the expressions in a tree, we may want to filter out certain expressions whose values are obvious. For example, suppose we have a function `foo`:
+
+```python
+def foo():
+ pass
+```
+
+If we refer to `foo` by its name as usual, then that's not interesting:
+
+```python
+from pure_eval import is_expression_interesting
+
+node = ast.parse('foo').body[0].value
+print(ast.dump(node))
+print(is_expression_interesting(node, foo))
+```
+
+Output:
+
+```python
+Name(id='foo', ctx=Load())
+False
+```
+
+But if we refer to it by a different name, then it's interesting:
+
+```python
+node = ast.parse('bar').body[0].value
+print(ast.dump(node))
+print(is_expression_interesting(node, foo))
+```
+
+Output:
+
+```python
+Name(id='bar', ctx=Load())
+True
+```
+
+In general `is_expression_interesting` returns False for the following values:
+- Literals (e.g. `123`, `'abc'`, `[1, 2, 3]`, `{'a': (), 'b': ([1, 2], [3])}`)
+- Variables or attributes whose name is equal to the value's `__name__`, such as `foo` above or `self.foo` if it was a method.
+- Builtins (e.g. `len`) referred to by their usual name.
+
+To make things easier, you can combine finding expressions, grouping them, and filtering out the obvious ones with:
+
+```python
+evaluator.interesting_expressions_grouped(root)
+```
+
+To get the source code of an AST node, I recommend [asttokens](https://github.com/gristlabs/asttokens).
+
+Here's a complete example that brings it all together:
+
+```python
+from asttokens import ASTTokens
+from pure_eval import Evaluator
+
+source = """
+x = 1
+d = {x: 2}
+y = d[x]
+"""
+
+names = {}
+exec(source, names)
+atok = ASTTokens(source, parse=True)
+for nodes, value in Evaluator(names).interesting_expressions_grouped(atok.tree):
+ print(atok.get_text(nodes[0]), "=", value)
+```
+
+Output:
+
+```python
+x = 1
+d = {1: 2}
+y = 2
+d[x] = 2
+```
+
+
+
+
+%prep
+%autosetup -n pure-eval-0.2.2
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-pure-eval -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Tue Apr 11 2023 Python_Bot <Python_Bot@openeuler.org> - 0.2.2-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..c9f2980
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+212fd27ca2c58d9effddec69748d738a pure_eval-0.2.2.tar.gz