RestrictedPython 8.3__tar.gz → 8.4__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.
- {restrictedpython-8.3 → restrictedpython-8.4}/.pre-commit-config.yaml +5 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/CHANGES.rst +13 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/PKG-INFO +17 -1
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/usage/api.rst +8 -8
- {restrictedpython-8.3 → restrictedpython-8.4}/pyproject.toml +28 -1
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/Eval.py +31 -17
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/Guards.py +12 -1
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/Limits.py +23 -4
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/PrintCollector.py +4 -4
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/Utilities.py +21 -6
- restrictedpython-8.4/src/RestrictedPython/_types.py +25 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/compile.py +104 -61
- restrictedpython-8.4/src/RestrictedPython/py.typed +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/transformer.py +175 -127
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython.egg-info/PKG-INFO +17 -1
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython.egg-info/SOURCES.txt +2 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython.egg-info/requires.txt +3 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_Guards.py +133 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_compile_restricted_function.py +66 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/.readthedocs.yaml +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/CONTRIBUTING.md +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/COPYRIGHT.txt +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/LICENSE.txt +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/MANIFEST.in +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/README.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/buildout.cfg +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/constraints.txt +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/Makefile +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/changes.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/conf.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/ast/python3_10.ast +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/ast/python3_11.ast +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/ast/python3_12.ast +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/ast/python3_13.ast +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/ast/python3_14.ast +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/ast/python3_8.ast +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/ast/python3_9.ast +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/changes_from310to311.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/changes_from311to312.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/changes_from312to313.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/changes_from313to314.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/changes_from39to310.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/contributing/index.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/idea.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/index.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/install/index.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/logo.jpg +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/make.bat +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/requirements.txt +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/roadmap/index.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/usage/basic_usage.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/usage/framework_usage.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/usage/index.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/docs/usage/policy.rst +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/setup.cfg +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/setup.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/__init__.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython/_compat.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython.egg-info/dependency_links.txt +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/src/RestrictedPython.egg-info/top_level.txt +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/__init__.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/builtins/test_limits.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/builtins/test_utilities.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/helper.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_NamedExpr.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_Utilities.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_compile.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_eval.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_imports.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_iterating_over_dict_items.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/test_print_function.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/operators/test_arithmetic_operators.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/operators/test_bit_wise_operators.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/operators/test_bool_operators.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/operators/test_comparison_operators.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/operators/test_identity_operators.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/operators/test_logical_operators.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/operators/test_unary_operators.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_assert.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_assign.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_async.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_attribute.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_augassign.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_base_types.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_breakpoint.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_call.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_classdef.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_comparators.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_conditional.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_dict_comprehension.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_eval_exec.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_fstring.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_functiondef.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_gen.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_generic.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_global_local.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_import.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_inspect.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_iterator.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_lambda.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_loop.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_name.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_slice.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_subscript.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_try.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_tstring.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_with_stmt.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tests/transformer/test_yield.py +0 -0
- {restrictedpython-8.3 → restrictedpython-8.4}/tox.ini +0 -0
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
Changes
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
+
8.4 (2026-07-10)
|
|
5
|
+
----------------
|
|
6
|
+
|
|
7
|
+
- Add type annotations to the package code.
|
|
8
|
+
For clarification, restricted Python code does not support type annotations.
|
|
9
|
+
|
|
10
|
+
- Allow ``ast.Module``, ``ast.Expression`` and ``ast.Interactive`` as body in compile_restricted_function
|
|
11
|
+
|
|
12
|
+
- Disallow ``mode="function"`` in ``compile_restricted`` (it never worked).
|
|
13
|
+
|
|
14
|
+
- Prevent access to ``string.Formatter`` and its unsafe traversal methods via
|
|
15
|
+
``safer_getattr``.
|
|
16
|
+
|
|
4
17
|
8.3 (2026-06-16)
|
|
5
18
|
----------------
|
|
6
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: RestrictedPython
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.4
|
|
4
4
|
Summary: RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment.
|
|
5
5
|
Author-email: Zope Foundation and contributors <zope-dev@zope.dev>
|
|
6
6
|
Maintainer-email: Plone Foundation and contributors <zope-dev@zope.dev>
|
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.14
|
|
22
22
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
23
|
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Typing :: Typed
|
|
24
25
|
Requires-Python: <3.16,>=3.10
|
|
25
26
|
Description-Content-Type: text/x-rst
|
|
26
27
|
License-File: LICENSE.txt
|
|
@@ -30,6 +31,8 @@ Requires-Dist: pytest-mock; extra == "test"
|
|
|
30
31
|
Provides-Extra: docs
|
|
31
32
|
Requires-Dist: Sphinx; extra == "docs"
|
|
32
33
|
Requires-Dist: furo; extra == "docs"
|
|
34
|
+
Provides-Extra: typecheck
|
|
35
|
+
Requires-Dist: mypy; extra == "typecheck"
|
|
33
36
|
Dynamic: license-file
|
|
34
37
|
|
|
35
38
|
.. image:: https://github.com/zopefoundation/RestrictedPython/actions/workflows/tests.yml/badge.svg
|
|
@@ -123,6 +126,19 @@ the documentation `Contributing page
|
|
|
123
126
|
Changes
|
|
124
127
|
=======
|
|
125
128
|
|
|
129
|
+
8.4 (2026-07-10)
|
|
130
|
+
----------------
|
|
131
|
+
|
|
132
|
+
- Add type annotations to the package code.
|
|
133
|
+
For clarification, restricted Python code does not support type annotations.
|
|
134
|
+
|
|
135
|
+
- Allow ``ast.Module``, ``ast.Expression`` and ``ast.Interactive`` as body in compile_restricted_function
|
|
136
|
+
|
|
137
|
+
- Disallow ``mode="function"`` in ``compile_restricted`` (it never worked).
|
|
138
|
+
|
|
139
|
+
- Prevent access to ``string.Formatter`` and its unsafe traversal methods via
|
|
140
|
+
``safer_getattr``.
|
|
141
|
+
|
|
126
142
|
8.3 (2026-06-16)
|
|
127
143
|
----------------
|
|
128
144
|
|
|
@@ -11,13 +11,13 @@ API overview
|
|
|
11
11
|
|
|
12
12
|
:param source: (required). the source code that should be compiled
|
|
13
13
|
:param filename: (optional). defaults to ``'<unknown>'``
|
|
14
|
-
:param mode: (optional). Use ``'exec'``, ``'eval'
|
|
14
|
+
:param mode: (optional). Use ``'exec'``, ``'eval'`` or ``'single'``. defaults to ``'exec'``
|
|
15
15
|
:param flags: (optional). defaults to ``0``
|
|
16
16
|
:param dont_inherit: (optional). defaults to ``False``
|
|
17
17
|
:param policy: (optional). defaults to ``RestrictingNodeTransformer``
|
|
18
|
-
:type source: str or
|
|
19
|
-
:type filename: str or
|
|
20
|
-
:type mode: str
|
|
18
|
+
:type source: str or bytes or bytearray or ``ast.Module`` or ``ast.Expression`` or ``ast.Interactive``
|
|
19
|
+
:type filename: str or bytes or os.PathLike[typing.Any]
|
|
20
|
+
:type mode: str
|
|
21
21
|
:type flags: int
|
|
22
22
|
:type dont_inherit: int
|
|
23
23
|
:type policy: RestrictingNodeTransformer class
|
|
@@ -67,10 +67,10 @@ API overview
|
|
|
67
67
|
:param flags: (optional). defaults to ``0``
|
|
68
68
|
:param dont_inherit: (optional). defaults to ``False``
|
|
69
69
|
:param policy: (optional). defaults to ``RestrictingNodeTransformer``
|
|
70
|
-
:type p: str
|
|
71
|
-
:type body: str or
|
|
72
|
-
:type name: str
|
|
73
|
-
:type filename: str or
|
|
70
|
+
:type p: str
|
|
71
|
+
:type body: str or bytes or bytearray or ``ast.Module`` or ``ast.Expression`` or ``ast.Interactive``
|
|
72
|
+
:type name: str
|
|
73
|
+
:type filename: str or bytes or os.PathLike[typing.Any]
|
|
74
74
|
:type globalize: None or list
|
|
75
75
|
:type flags: int
|
|
76
76
|
:type dont_inherit: int
|
|
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
|
|
|
9
9
|
|
|
10
10
|
[project]
|
|
11
11
|
name = "RestrictedPython"
|
|
12
|
-
version = "8.
|
|
12
|
+
version = "8.4"
|
|
13
13
|
description = "RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment."
|
|
14
14
|
license = "ZPL-2.1"
|
|
15
15
|
classifiers = [
|
|
@@ -24,6 +24,7 @@ classifiers = [
|
|
|
24
24
|
"Programming Language :: Python :: 3.14",
|
|
25
25
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
26
26
|
"Topic :: Security",
|
|
27
|
+
"Typing :: Typed",
|
|
27
28
|
]
|
|
28
29
|
dynamic = ["readme"]
|
|
29
30
|
requires-python = ">=3.10, <3.16"
|
|
@@ -50,6 +51,9 @@ docs = [
|
|
|
50
51
|
"Sphinx",
|
|
51
52
|
"furo",
|
|
52
53
|
]
|
|
54
|
+
typecheck = [
|
|
55
|
+
"mypy",
|
|
56
|
+
]
|
|
53
57
|
|
|
54
58
|
[project.urls]
|
|
55
59
|
Documentation = "https://restrictedpython.readthedocs.io/"
|
|
@@ -57,6 +61,7 @@ Issues = "https://github.com/zopefoundation/RestrictedPython/issues"
|
|
|
57
61
|
Source = "https://github.com/zopefoundation/RestrictedPython"
|
|
58
62
|
Changelog = "https://github.com/zopefoundation/RestrictedPython/blob/master/CHANGES.rst"
|
|
59
63
|
|
|
64
|
+
|
|
60
65
|
[tool.coverage.run]
|
|
61
66
|
branch = true
|
|
62
67
|
source = ["RestrictedPython"]
|
|
@@ -84,6 +89,28 @@ directory = "parts/htmlcov"
|
|
|
84
89
|
readme = {file = ["README.rst", "CHANGES.rst"]}
|
|
85
90
|
|
|
86
91
|
|
|
92
|
+
[tool.mypy]
|
|
93
|
+
mypy_path = "src"
|
|
94
|
+
packages = ["RestrictedPython"]
|
|
95
|
+
python_version = "3.10"
|
|
96
|
+
warn_unreachable = true
|
|
97
|
+
implicit_reexport = false
|
|
98
|
+
strict = true
|
|
99
|
+
|
|
100
|
+
[[tool.mypy.overrides]]
|
|
101
|
+
module = ["DateTime"]
|
|
102
|
+
ignore_missing_imports = true
|
|
103
|
+
|
|
104
|
+
[[tool.mypy.overrides]]
|
|
105
|
+
module = ["RestrictedPython.Guards"]
|
|
106
|
+
check_untyped_defs = false
|
|
107
|
+
disallow_untyped_defs = false
|
|
108
|
+
|
|
109
|
+
[[tool.mypy.overrides]]
|
|
110
|
+
module = ["RestrictedPython.transformer"]
|
|
111
|
+
warn_no_return = false
|
|
112
|
+
|
|
113
|
+
|
|
87
114
|
[tool.zest-releaser]
|
|
88
115
|
create-wheel = false
|
|
89
116
|
upload-pypi = false
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
"""Restricted Python Expressions."""
|
|
14
14
|
|
|
15
15
|
import ast
|
|
16
|
+
import collections
|
|
17
|
+
import types
|
|
18
|
+
import typing
|
|
16
19
|
|
|
17
|
-
from .
|
|
20
|
+
from RestrictedPython._types import cast_not_none
|
|
21
|
+
from RestrictedPython.compile import compile_restricted_eval
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
nltosp = str.maketrans('\r\n', ' ')
|
|
@@ -22,13 +26,21 @@ nltosp = str.maketrans('\r\n', ' ')
|
|
|
22
26
|
# No restrictions.
|
|
23
27
|
default_guarded_getattr = getattr
|
|
24
28
|
|
|
29
|
+
_T = typing.TypeVar('_T')
|
|
30
|
+
_TK = typing.TypeVar('_TK', contravariant=True)
|
|
31
|
+
_TV = typing.TypeVar('_TV', covariant=True)
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
|
|
34
|
+
class _GetItem(typing.Protocol[_TK, _TV]):
|
|
35
|
+
def __getitem__(self, key: _TK) -> _TV: ...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def default_guarded_getitem(ob: _GetItem[_TK, _TV], index: _TK) -> _TV:
|
|
27
39
|
# No restrictions.
|
|
28
40
|
return ob[index]
|
|
29
41
|
|
|
30
42
|
|
|
31
|
-
def default_guarded_getiter(ob):
|
|
43
|
+
def default_guarded_getiter(ob: _T) -> _T:
|
|
32
44
|
# No restrictions.
|
|
33
45
|
return ob
|
|
34
46
|
|
|
@@ -36,17 +48,18 @@ def default_guarded_getiter(ob):
|
|
|
36
48
|
class RestrictionCapableEval:
|
|
37
49
|
"""A base class for restricted code."""
|
|
38
50
|
|
|
39
|
-
globals = {'__builtins__': None}
|
|
51
|
+
globals: dict[str, typing.Any] = {'__builtins__': None}
|
|
52
|
+
|
|
40
53
|
# restricted
|
|
41
|
-
rcode = None
|
|
54
|
+
rcode: types.CodeType | None = None
|
|
42
55
|
|
|
43
56
|
# unrestricted
|
|
44
|
-
ucode = None
|
|
57
|
+
ucode: types.CodeType | None = None
|
|
45
58
|
|
|
46
59
|
# Names used by the expression
|
|
47
|
-
used = None
|
|
60
|
+
used: tuple[str, ...] | None = None
|
|
48
61
|
|
|
49
|
-
def __init__(self, expr):
|
|
62
|
+
def __init__(self, expr: str):
|
|
50
63
|
"""Create a restricted expression
|
|
51
64
|
|
|
52
65
|
where:
|
|
@@ -60,7 +73,7 @@ class RestrictionCapableEval:
|
|
|
60
73
|
# Catch syntax errors.
|
|
61
74
|
self.prepUnrestrictedCode()
|
|
62
75
|
|
|
63
|
-
def prepRestrictedCode(self):
|
|
76
|
+
def prepRestrictedCode(self) -> None:
|
|
64
77
|
if self.rcode is None:
|
|
65
78
|
result = compile_restricted_eval(self.expr, '<string>')
|
|
66
79
|
if result.errors:
|
|
@@ -68,13 +81,12 @@ class RestrictionCapableEval:
|
|
|
68
81
|
self.used = tuple(result.used_names)
|
|
69
82
|
self.rcode = result.code
|
|
70
83
|
|
|
71
|
-
def prepUnrestrictedCode(self):
|
|
84
|
+
def prepUnrestrictedCode(self) -> None:
|
|
72
85
|
if self.ucode is None:
|
|
73
|
-
exp_node =
|
|
86
|
+
exp_node = ast.parse(
|
|
74
87
|
self.expr,
|
|
75
88
|
'<string>',
|
|
76
|
-
'eval'
|
|
77
|
-
ast.PyCF_ONLY_AST)
|
|
89
|
+
'eval')
|
|
78
90
|
|
|
79
91
|
co = compile(exp_node, '<string>', 'eval')
|
|
80
92
|
|
|
@@ -90,7 +102,9 @@ class RestrictionCapableEval:
|
|
|
90
102
|
|
|
91
103
|
self.ucode = co
|
|
92
104
|
|
|
93
|
-
def eval(self,
|
|
105
|
+
def eval(self,
|
|
106
|
+
mapping: collections.abc.Mapping[str,
|
|
107
|
+
typing.Any]) -> typing.Any:
|
|
94
108
|
# This default implementation is probably not very useful. :-(
|
|
95
109
|
# This is meant to be overridden.
|
|
96
110
|
self.prepRestrictedCode()
|
|
@@ -103,11 +117,11 @@ class RestrictionCapableEval:
|
|
|
103
117
|
|
|
104
118
|
global_scope.update(self.globals)
|
|
105
119
|
|
|
106
|
-
for name in self.used:
|
|
120
|
+
for name in cast_not_none(self.used):
|
|
107
121
|
if (name not in global_scope) and (name in mapping):
|
|
108
122
|
global_scope[name] = mapping[name]
|
|
109
123
|
|
|
110
|
-
return eval(self.rcode, global_scope)
|
|
124
|
+
return eval(cast_not_none(self.rcode), global_scope)
|
|
111
125
|
|
|
112
|
-
def __call__(self, **kw):
|
|
126
|
+
def __call__(self, **kw: typing.Any) -> typing.Any:
|
|
113
127
|
return self.eval(kw)
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
# DocumentTemplate.DT_UTil contains a few.
|
|
17
17
|
|
|
18
18
|
import builtins
|
|
19
|
+
import string
|
|
19
20
|
|
|
20
21
|
from RestrictedPython.transformer import INSPECT_ATTRIBUTES
|
|
21
22
|
|
|
@@ -220,7 +221,7 @@ def _full_write_guard():
|
|
|
220
221
|
return guard
|
|
221
222
|
|
|
222
223
|
|
|
223
|
-
full_write_guard = _full_write_guard()
|
|
224
|
+
full_write_guard = _full_write_guard() # type: ignore[no-untyped-call]
|
|
224
225
|
|
|
225
226
|
|
|
226
227
|
def guarded_setattr(object, name, value):
|
|
@@ -238,6 +239,8 @@ safe_builtins['delattr'] = guarded_delattr
|
|
|
238
239
|
|
|
239
240
|
|
|
240
241
|
raise_ = object()
|
|
242
|
+
_FORMATTER_UNSAFE_METHODS = frozenset(('format', 'get_field', 'get_value',
|
|
243
|
+
'vformat'))
|
|
241
244
|
|
|
242
245
|
|
|
243
246
|
def safer_getattr(object, name, default=None, getattr=getattr):
|
|
@@ -254,6 +257,14 @@ def safer_getattr(object, name, default=None, getattr=getattr):
|
|
|
254
257
|
(isinstance(object, type) and issubclass(object, str))):
|
|
255
258
|
raise NotImplementedError(
|
|
256
259
|
'Using the format*() methods of `str` is not safe')
|
|
260
|
+
if object is string and name == 'Formatter':
|
|
261
|
+
raise NotImplementedError('string.Formatter is not safe')
|
|
262
|
+
if name in _FORMATTER_UNSAFE_METHODS and (
|
|
263
|
+
isinstance(object, string.Formatter) or
|
|
264
|
+
(isinstance(object, type) and
|
|
265
|
+
issubclass(object, string.Formatter))):
|
|
266
|
+
raise NotImplementedError(
|
|
267
|
+
'Using string.Formatter methods is not safe')
|
|
257
268
|
if name in INSPECT_ATTRIBUTES:
|
|
258
269
|
raise AttributeError(
|
|
259
270
|
f'"{name}" is a restricted name,'
|
|
@@ -10,11 +10,28 @@
|
|
|
10
10
|
# FOR A PARTICULAR PURPOSE
|
|
11
11
|
#
|
|
12
12
|
##############################################################################
|
|
13
|
+
import collections.abc
|
|
14
|
+
import typing
|
|
13
15
|
|
|
14
|
-
limited_builtins = {}
|
|
15
16
|
|
|
17
|
+
limited_builtins: dict[str, typing.Any] = {}
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
@typing.overload
|
|
21
|
+
def limited_range(iFirst: int) -> collections.abc.Sequence[int]: ...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@typing.overload
|
|
25
|
+
def limited_range(iStart: int, iEnd: int, /
|
|
26
|
+
) -> collections.abc.Sequence[int]: ...
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@typing.overload
|
|
30
|
+
def limited_range(iStart: int, iEnd: int, iStep: int, /
|
|
31
|
+
) -> collections.abc.Sequence[int]: ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def limited_range(iFirst: int, *args: int) -> collections.abc.Sequence[int]:
|
|
18
35
|
# limited range function from Martijn Pieters
|
|
19
36
|
RANGELIMIT = 1000
|
|
20
37
|
if not len(args):
|
|
@@ -41,8 +58,10 @@ def limited_range(iFirst, *args):
|
|
|
41
58
|
|
|
42
59
|
limited_builtins['range'] = limited_range
|
|
43
60
|
|
|
61
|
+
_T = typing.TypeVar('_T')
|
|
62
|
+
|
|
44
63
|
|
|
45
|
-
def limited_list(seq):
|
|
64
|
+
def limited_list(seq: collections.abc.Iterable[_T]) -> list[_T]:
|
|
46
65
|
if isinstance(seq, str):
|
|
47
66
|
raise TypeError('cannot convert string to list')
|
|
48
67
|
return list(seq)
|
|
@@ -51,7 +70,7 @@ def limited_list(seq):
|
|
|
51
70
|
limited_builtins['list'] = limited_list
|
|
52
71
|
|
|
53
72
|
|
|
54
|
-
def limited_tuple(seq):
|
|
73
|
+
def limited_tuple(seq: collections.abc.Iterable[_T]) -> tuple[_T, ...]:
|
|
55
74
|
if isinstance(seq, str):
|
|
56
75
|
raise TypeError('cannot convert string to tuple')
|
|
57
76
|
return tuple(seq)
|
|
@@ -15,17 +15,17 @@
|
|
|
15
15
|
class PrintCollector:
|
|
16
16
|
"""Collect written text, and return it when called."""
|
|
17
17
|
|
|
18
|
-
def __init__(self, _getattr_=None):
|
|
18
|
+
def __init__(self, _getattr_=None): # type: ignore[no-untyped-def]
|
|
19
19
|
self.txt = []
|
|
20
20
|
self._getattr_ = _getattr_
|
|
21
21
|
|
|
22
|
-
def write(self, text):
|
|
22
|
+
def write(self, text: str) -> None:
|
|
23
23
|
self.txt.append(text)
|
|
24
24
|
|
|
25
|
-
def __call__(self):
|
|
25
|
+
def __call__(self) -> str:
|
|
26
26
|
return ''.join(self.txt)
|
|
27
27
|
|
|
28
|
-
def _call_print(self, *objects, **kwargs):
|
|
28
|
+
def _call_print(self, *objects, **kwargs): # type: ignore[no-untyped-def]
|
|
29
29
|
if kwargs.get('file', None) is None:
|
|
30
30
|
kwargs['file'] = self
|
|
31
31
|
else:
|
|
@@ -11,21 +11,24 @@
|
|
|
11
11
|
#
|
|
12
12
|
##############################################################################
|
|
13
13
|
|
|
14
|
+
import collections.abc
|
|
14
15
|
import math
|
|
15
16
|
import random
|
|
16
17
|
import string
|
|
18
|
+
import types
|
|
19
|
+
import typing
|
|
17
20
|
|
|
18
21
|
|
|
19
|
-
utility_builtins = {}
|
|
22
|
+
utility_builtins: dict[str, typing.Any] = {}
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
class _AttributeDelegator:
|
|
23
|
-
def __init__(self, mod, *excludes):
|
|
26
|
+
def __init__(self, mod: types.ModuleType, *excludes: str):
|
|
24
27
|
"""delegate attribute lookups outside *excludes* to module *mod*."""
|
|
25
28
|
self.__mod = mod
|
|
26
29
|
self.__excludes = excludes
|
|
27
30
|
|
|
28
|
-
def __getattr__(self, attr):
|
|
31
|
+
def __getattr__(self, attr: str) -> typing.Any:
|
|
29
32
|
if attr in self.__excludes:
|
|
30
33
|
raise NotImplementedError(
|
|
31
34
|
f"{self.__mod.__name__}.{attr} is not safe")
|
|
@@ -50,7 +53,7 @@ except ImportError:
|
|
|
50
53
|
pass
|
|
51
54
|
|
|
52
55
|
|
|
53
|
-
def same_type(arg1, *args):
|
|
56
|
+
def same_type(arg1: object, *args: object) -> bool:
|
|
54
57
|
"""Compares the class or type of two or more objects."""
|
|
55
58
|
t = getattr(arg1, '__class__', type(arg1))
|
|
56
59
|
for arg in args:
|
|
@@ -61,8 +64,10 @@ def same_type(arg1, *args):
|
|
|
61
64
|
|
|
62
65
|
utility_builtins['same_type'] = same_type
|
|
63
66
|
|
|
67
|
+
_T = typing.TypeVar('_T')
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
|
|
70
|
+
def test(*args: _T) -> _T | None:
|
|
66
71
|
length = len(args)
|
|
67
72
|
for i in range(1, length, 2):
|
|
68
73
|
if args[i - 1]:
|
|
@@ -70,12 +75,22 @@ def test(*args):
|
|
|
70
75
|
|
|
71
76
|
if length % 2:
|
|
72
77
|
return args[-1]
|
|
78
|
+
return None
|
|
73
79
|
|
|
74
80
|
|
|
75
81
|
utility_builtins['test'] = test
|
|
76
82
|
|
|
83
|
+
_TK = typing.TypeVar('_TK')
|
|
84
|
+
_TV = typing.TypeVar('_TV')
|
|
85
|
+
_T_in: typing.TypeAlias = collections.abc.Iterable[_TK | tuple[_TK, _TV]]
|
|
86
|
+
_T_out: typing.TypeAlias = list[tuple[_TK, _TK | _TV]]
|
|
87
|
+
|
|
77
88
|
|
|
78
|
-
def reorder(
|
|
89
|
+
def reorder(
|
|
90
|
+
s: _T_in[_TK, _TV],
|
|
91
|
+
with_: collections.abc.Iterable[typing.Any] | None = None,
|
|
92
|
+
without: collections.abc.Iterable[typing.Any] = ()
|
|
93
|
+
) -> _T_out[_TK, _TV]:
|
|
79
94
|
# s, with_, and without are sequences treated as sets.
|
|
80
95
|
# The result is subtract(intersect(s, with_), without),
|
|
81
96
|
# unless with_ is None, in which case it is subtract(s, without).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import sys
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
_T = typing.TypeVar('_T')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def cast_not_none(var: _T | None) -> _T:
|
|
10
|
+
return typing.cast(_T, var)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# T_pos_ast are subtypes of ast.AST that have a position
|
|
14
|
+
# (have attributes: lineno, end_lineno, col_offset, and end_col_offset).
|
|
15
|
+
#
|
|
16
|
+
# ast.type_param is a new type in python 3.12 that has a position.
|
|
17
|
+
# TODO: Remove `else` when Support for Python 3.11 is dropped.
|
|
18
|
+
if sys.version_info >= (3, 12):
|
|
19
|
+
T_pos_ast: typing.TypeAlias = (
|
|
20
|
+
ast.stmt | ast.expr | ast.excepthandler | ast.arg | ast.keyword
|
|
21
|
+
| ast.alias | ast.pattern | ast.type_param)
|
|
22
|
+
else:
|
|
23
|
+
T_pos_ast: typing.TypeAlias = (
|
|
24
|
+
ast.stmt | ast.expr | ast.excepthandler | ast.arg | ast.keyword
|
|
25
|
+
| ast.alias | ast.pattern)
|