From: David Lord Date: Mon, 8 Nov 2021 16:07:29 +0000 (-0800) Subject: drop Python 3.6 X-Git-Tag: 3.1.0~24^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5308c9588d50d49b18885a8864915d728477a433;p=thirdparty%2Fjinja.git drop Python 3.6 --- diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 4e48dd9c..e6ef91fb 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -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: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e4b8d0f9..356b49c8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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: diff --git a/docs/faq.rst b/docs/faq.rst index dd782175..8584d775 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -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 -`_ My Macros are overridden by something ------------------------------------- diff --git a/docs/intro.rst b/docs/intro.rst index 9eeaa054..fd6f84ff 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -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 diff --git a/setup.cfg b/setup.cfg index f952317e..803e8c6c 100644 --- 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 diff --git a/src/jinja2/compiler.py b/src/jinja2/compiler.py index 52fd5b83..cc25c3ed 100644 --- a/src/jinja2/compiler.py +++ b/src/jinja2/compiler.py @@ -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 diff --git a/src/jinja2/debug.py b/src/jinja2/debug.py index 02de4ee7..be33dd15 100644 --- a/src/jinja2/debug.py +++ b/src/jinja2/debug.py @@ -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 diff --git a/src/jinja2/environment.py b/src/jinja2/environment.py index 8a831db8..09fa83ac 100644 --- a/src/jinja2/environment.py +++ b/src/jinja2/environment.py @@ -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)) diff --git a/tests/test_async.py b/tests/test_async.py index 375a7bac..635727e2 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -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 == "" @@ -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 index 4f36458a..00000000 --- a/tests/test_features.py +++ /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 f14c957d..056ca0d9 100644 --- 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