]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
drop Python 3.6 1534/head
authorDavid Lord <davidism@gmail.com>
Mon, 8 Nov 2021 16:07:29 +0000 (08:07 -0800)
committerDavid Lord <davidism@gmail.com>
Mon, 8 Nov 2021 16:07:29 +0000 (08:07 -0800)
.github/workflows/tests.yaml
.pre-commit-config.yaml
docs/faq.rst
docs/intro.rst
setup.cfg
src/jinja2/compiler.py
src/jinja2/debug.py
src/jinja2/environment.py
tests/test_async.py
tests/test_features.py [deleted file]
tox.ini

index 4e48dd9cd0e84a7b05f86f3dde9da1849689393e..e6ef91fbd8e004fc3c41355a4bf990b5a2a437fe 100644 (file)
@@ -31,7 +31,6 @@ jobs:
           - {name: '3.9', python: '3.9', os: ubuntu-latest, tox: py39}
           - {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38}
           - {name: '3.7', python: '3.7', os: ubuntu-latest, tox: py37}
-          - {name: '3.6', python: '3.6', os: ubuntu-latest, tox: py36}
           - {name: 'PyPy', python: 'pypy-3.7', os: ubuntu-latest, tox: pypy37}
           - {name: Typing, python: '3.10', os: ubuntu-latest, tox: typing}
     steps:
index e4b8d0f979330e7f64ddb33d93835e4aaf35b498..356b49c8ce18f52b7f6d95b65b57be9da731e781 100644 (file)
@@ -5,7 +5,7 @@ repos:
     rev: v2.29.0
     hooks:
       - id: pyupgrade
-        args: ["--py36-plus"]
+        args: ["--py37-plus"]
   - repo: https://github.com/asottile/reorder_python_imports
     rev: v2.6.0
     hooks:
index dd782175074e42851601fe598bc919b8ce5989fc..8584d77500b38352ff61d2de88135f982f0670b2 100644 (file)
@@ -125,26 +125,6 @@ instead that one can assign to a variable by using set::
 
     {% set comments = get_latest_comments() %}
 
-My tracebacks look weird. What's happening?
--------------------------------------------
-
-Jinja can rewrite tracebacks so they show the template lines numbers and
-source rather than the underlying compiled code, but this requires
-special Python support. CPython <3.7 requires ``ctypes``, and PyPy
-requires transparent proxy support.
-
-If you are using Google App Engine, ``ctypes`` is not available. You can
-make it available in development, but not in production.
-
-.. code-block:: python
-
-    import os
-    if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'):
-        from google.appengine.tools.devappserver2.python import sandbox
-        sandbox._WHITE_LIST_C_MODULES += ['_ctypes', 'gestalt']
-
-Credit for this snippet goes to `Thomas Johansson
-<https://stackoverflow.com/questions/3086091/debug-jinja2-in-google-app-engine/3694434#3694434>`_
 
 My Macros are overridden by something
 -------------------------------------
index 9eeaa05401889019e2592c856a5b23222533e5d5..fd6f84ff5beb77324c5d4a7fa66049b73b6003de 100644 (file)
@@ -30,7 +30,7 @@ Installation
 ------------
 
 We recommend using the latest version of Python. Jinja supports Python
-3.6 and newer. We also recommend using a `virtual environment`_ in order
+3.7 and newer. We also recommend using a `virtual environment`_ in order
 to isolate your project dependencies from other projects and the system.
 
 .. _virtual environment: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments
index f952317e184e4bd1a7e2a1c3eaa934d5cad6a5aa..803e8c6cfbcff9475ba5f1018cde8366a6cbe6c8 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -33,7 +33,7 @@ classifiers =
 packages = find:
 package_dir = = src
 include_package_data = true
-python_requires = >= 3.6
+python_requires = >= 3.7
 # Dependencies are in setup.py for GitHub's dependency graph.
 
 [options.packages.find]
@@ -86,7 +86,7 @@ per-file-ignores =
 
 [mypy]
 files = src/jinja2
-python_version = 3.6
+python_version = 3.7
 disallow_subclassing_any = True
 disallow_untyped_calls = True
 disallow_untyped_defs = True
index 52fd5b83e20964930dde5c8d71e149031a48d5bb..cc25c3edaab257ac3251fb76b7cc73643c539fa3 100644 (file)
@@ -835,7 +835,6 @@ class CodeGenerator(NodeVisitor):
         else:
             exported_names = sorted(exported)
 
-        self.writeline("from __future__ import generator_stop")  # Python < 3.7
         self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
 
         # if we want a deferred initialization we cannot move the
index 02de4ee71b36035c8c4055b00ff36ad6dd2e43d5..be33dd15cec15f2d999c9fa2132dfd0f029be9a7 100644 (file)
@@ -1,4 +1,3 @@
-import platform
 import sys
 import typing as t
 from types import CodeType
@@ -68,7 +67,8 @@ def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException:
 
     # Assign tb_next in reverse to avoid circular references.
     for tb in reversed(stack):
-        tb_next = tb_set_next(tb, tb_next)
+        tb.tb_next = tb_next
+        tb_next = tb
 
     return exc_value.with_traceback(tb_next)
 
@@ -209,71 +209,3 @@ def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any
             data[name] = value
 
     return data
-
-
-if sys.version_info >= (3, 7):
-    # tb_next is directly assignable as of Python 3.7
-    def tb_set_next(
-        tb: TracebackType, tb_next: t.Optional[TracebackType]
-    ) -> TracebackType:
-        tb.tb_next = tb_next
-        return tb
-
-
-elif platform.python_implementation() == "PyPy":
-    # PyPy might have special support, and won't work with ctypes.
-    try:
-        import tputil  # type: ignore
-    except ImportError:
-        # Without tproxy support, use the original traceback.
-        def tb_set_next(
-            tb: TracebackType, tb_next: t.Optional[TracebackType]
-        ) -> TracebackType:
-            return tb
-
-    else:
-        # With tproxy support, create a proxy around the traceback that
-        # returns the new tb_next.
-        def tb_set_next(
-            tb: TracebackType, tb_next: t.Optional[TracebackType]
-        ) -> TracebackType:
-            def controller(op):  # type: ignore
-                if op.opname == "__getattribute__" and op.args[0] == "tb_next":
-                    return tb_next
-
-                return op.delegate()
-
-            return tputil.make_proxy(controller, obj=tb)  # type: ignore
-
-
-else:
-    # Use ctypes to assign tb_next at the C level since it's read-only
-    # from Python.
-    import ctypes
-
-    class _CTraceback(ctypes.Structure):
-        _fields_ = [
-            # Extra PyObject slots when compiled with Py_TRACE_REFS.
-            ("PyObject_HEAD", ctypes.c_byte * object().__sizeof__()),
-            # Only care about tb_next as an object, not a traceback.
-            ("tb_next", ctypes.py_object),
-        ]
-
-    def tb_set_next(
-        tb: TracebackType, tb_next: t.Optional[TracebackType]
-    ) -> TracebackType:
-        c_tb = _CTraceback.from_address(id(tb))
-
-        # Clear out the old tb_next.
-        if tb.tb_next is not None:
-            c_tb_next = ctypes.py_object(tb.tb_next)
-            c_tb.tb_next = ctypes.py_object()
-            ctypes.pythonapi.Py_DecRef(c_tb_next)
-
-        # Assign the new tb_next.
-        if tb_next is not None:
-            c_tb_next = ctypes.py_object(tb_next)
-            ctypes.pythonapi.Py_IncRef(c_tb_next)
-            c_tb.tb_next = c_tb_next
-
-        return tb
index 8a831db82fcde16b8a12698ee26205225c288b2a..09fa83ac5753a48d365643d669e1fc550aea1ba6 100644 (file)
@@ -2,7 +2,6 @@
 options.
 """
 import os
-import sys
 import typing
 import typing as t
 import weakref
@@ -1281,14 +1280,11 @@ class Template:
 
             close = False
 
-            if sys.version_info < (3, 7):
-                loop = asyncio.get_event_loop()
-            else:
-                try:
-                    loop = asyncio.get_running_loop()
-                except RuntimeError:
-                    loop = asyncio.new_event_loop()
-                    close = True
+            try:
+                loop = asyncio.get_running_loop()
+            except RuntimeError:
+                loop = asyncio.new_event_loop()
+                close = True
 
             try:
                 return loop.run_until_complete(self.render_async(*args, **kwargs))
@@ -1344,13 +1340,7 @@ class Template:
             async def to_list() -> t.List[str]:
                 return [x async for x in self.generate_async(*args, **kwargs)]
 
-            if sys.version_info < (3, 7):
-                loop = asyncio.get_event_loop()
-                out = loop.run_until_complete(to_list())
-            else:
-                out = asyncio.run(to_list())
-
-            yield from out
+            yield from asyncio.run(to_list())
             return
 
         ctx = self.new_context(dict(*args, **kwargs))
index 375a7bac33d0ed55ed31daafc0b28880ed93e9a8..635727e273c64221ce8657afc480a0677d3d4fda 100644 (file)
@@ -1,5 +1,4 @@
 import asyncio
-import sys
 
 import pytest
 
@@ -14,19 +13,6 @@ from jinja2.exceptions import UndefinedError
 from jinja2.nativetypes import NativeEnvironment
 
 
-if sys.version_info < (3, 7):
-
-    def run(coro):
-        loop = asyncio.get_event_loop()
-        return loop.run_until_complete(coro)
-
-
-else:
-
-    def run(coro):
-        return asyncio.run(coro)
-
-
 def test_basic_async():
     t = Template(
         "{% for item in [1, 2, 3] %}[{{ item }}]{% endfor %}", enable_async=True
@@ -35,7 +21,7 @@ def test_basic_async():
     async def func():
         return await t.render_async()
 
-    rv = run(func())
+    rv = asyncio.run(func())
     assert rv == "[1][2][3]"
 
 
@@ -51,7 +37,7 @@ def test_await_on_calls():
     async def func():
         return await t.render_async(async_func=async_func, normal_func=normal_func)
 
-    rv = run(func())
+    rv = asyncio.run(func())
     assert rv == "65"
 
 
@@ -65,7 +51,6 @@ def test_await_on_calls_normal_render():
         return 23
 
     rv = t.render(async_func=async_func, normal_func=normal_func)
-
     assert rv == "65"
 
 
@@ -81,7 +66,7 @@ def test_await_and_macros():
     async def func():
         return await t.render_async(async_func=async_func)
 
-    rv = run(func())
+    rv = asyncio.run(func())
     assert rv == "[42][42]"
 
 
@@ -95,7 +80,7 @@ def test_async_blocks():
     async def func():
         return await t.render_async()
 
-    rv = run(func())
+    rv = asyncio.run(func())
     assert rv == "<Test><Test>"
 
 
@@ -172,19 +157,18 @@ class TestAsyncImports:
         test_env_async.from_string('{% from "foo" import bar, with with context %}')
 
     def test_exports(self, test_env_async):
-        m = run(
-            test_env_async.from_string(
-                """
+        coro = test_env_async.from_string(
+            """
             {% macro toplevel() %}...{% endmacro %}
             {% macro __private() %}...{% endmacro %}
             {% set variable = 42 %}
             {% for item in [1] %}
                 {% macro notthere() %}{% endmacro %}
             {% endfor %}
-        """
-            )._get_default_module_async()
-        )
-        assert run(m.toplevel()) == "..."
+            """
+        )._get_default_module_async()
+        m = asyncio.run(coro)
+        assert asyncio.run(m.toplevel()) == "..."
         assert not hasattr(m, "__missing")
         assert m.variable == 42
         assert not hasattr(m, "notthere")
@@ -621,7 +605,7 @@ def test_namespace_awaitable(test_env_async):
         actual = await t.render_async()
         assert actual == "Bar"
 
-    run(_test())
+    asyncio.run(_test())
 
 
 def test_chainable_undefined_aiter():
@@ -634,7 +618,7 @@ def test_chainable_undefined_aiter():
         rv = await t.render_async(a={})
         assert rv == ""
 
-    run(_test())
+    asyncio.run(_test())
 
 
 @pytest.fixture
@@ -648,7 +632,7 @@ def test_native_async(async_native_env):
         rv = await t.render_async(x=23)
         assert rv == 23
 
-    run(_test())
+    asyncio.run(_test())
 
 
 def test_native_list_async(async_native_env):
@@ -657,4 +641,4 @@ def test_native_list_async(async_native_env):
         rv = await t.render_async(x=list(range(3)))
         assert rv == [0, 1, 2]
 
-    run(_test())
+    asyncio.run(_test())
diff --git a/tests/test_features.py b/tests/test_features.py
deleted file mode 100644 (file)
index 4f36458..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-import pytest
-
-from jinja2 import Template
-
-
-# Python < 3.7
-def test_generator_stop():
-    class X:
-        def __getattr__(self, name):
-            raise StopIteration()
-
-    t = Template("a{{ bad.bar() }}b")
-    with pytest.raises(RuntimeError):
-        t.render(bad=X())
diff --git a/tox.ini b/tox.ini
index f14c957dcde87036de703ff00651c73f7e97141e..056ca0d9479a9097a342b412e57f80407c7c0715 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 envlist =
-    py{311,310,39,38,37,36,py37}
+    py3{11,10,9,8,7},pypy3{8,7}
     style
     typing
     docs