diff options
| author | David Lord <davidism@gmail.com> | 2021-03-30 16:38:04 -0700 |
|---|---|---|
| committer | David Lord <davidism@gmail.com> | 2021-03-31 10:05:55 -0700 |
| commit | 28299419ee73a09720116f38a77929452be37775 (patch) | |
| tree | 7b4b0af9c2087641acc7dcf95cfe1bf2967a1501 | |
| parent | 40a312e80f4f1b25f201293c3f1a840a1b88191d (diff) | |
| download | jinja2-docs-globals.tar.gz | |
more detailed docs about globalsdocs-globals
| -rw-r--r-- | CHANGES.rst | 5 | ||||
| -rw-r--r-- | docs/api.rst | 52 | ||||
| -rw-r--r-- | src/jinja2/environment.py | 112 |
3 files changed, 116 insertions, 53 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 7c0284f..26c928b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -36,8 +36,9 @@ Unreleased being accessed in custom context functions. :issue:`768` - Fix a bug that caused scoped blocks from accessing special loop variables. :issue:`1088` -- Fix a bug that prevented cached templates from registering new globals. - :issue:`295` +- Update the template globals when calling + ``Environment.get_template(globals=...)`` even if the template was + already loaded. :issue:`295` Version 2.11.3 diff --git a/docs/api.rst b/docs/api.rst index 47ecb9f..9ae47ef 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -114,10 +114,10 @@ useful if you want to dig deeper into Jinja or :ref:`develop extensions .. attribute:: globals - A dict of global variables. These variables are always available - in a template. As long as no template was loaded it's safe - to modify this dict. For more details see :ref:`global-namespace`. - For valid object names have a look at :ref:`identifier-naming`. + A dict of variables that are available in every template loaded + by the environment. As long as no template was loaded it's safe + to modify this. For more details see :ref:`global-namespace`. + For valid object names see :ref:`identifier-naming`. .. attribute:: policies @@ -180,9 +180,20 @@ useful if you want to dig deeper into Jinja or :ref:`develop extensions .. attribute:: globals - The dict with the globals of that template. It's unsafe to modify - this dict as it may be shared with other templates or the environment - that loaded the template. + A dict of variables that are available every time the template + is rendered, without needing to pass them during render. This + should not be modified, as depending on how the template was + loaded it may be shared with the environment and other + templates. + + Defaults to :attr:`Environment.globals` unless extra values are + passed to :meth:`Environment.get_template`. + + Globals are only intended for data that is common to every + render of the template. Specific data should be passed to + :meth:`render`. + + See :ref:`global-namespace`. .. attribute:: name @@ -814,12 +825,27 @@ A template designer can then use the test like this: The Global Namespace -------------------- -Variables stored in the :attr:`Environment.globals` dict are special as they -are available for imported templates too, even if they are imported without -context. This is the place where you can put variables and functions -that should be available all the time. Additionally :attr:`Template.globals` -exist that are variables available to a specific template that are available -to all :meth:`~Template.render` calls. +The global namespace stores variables and functions that should be +available without needing to pass them to :meth:`Template.render`. They +are also available to templates that are imported or included without +context. Most applications should only use :attr:`Environment.globals`. + +:attr:`Environment.globals` are intended for data that is common to all +templates loaded by that environment. :attr:`Template.globals` are +intended for data that is common to all renders of that template, and +default to :attr:`Environment.globals` unless they're given in +:meth:`Environment.get_template`, etc. Data that is specific to a +render should be passed as context to :meth:`Template.render`. + +Only one set of globals is used during any specific rendering. If +templates A and B both have template globals, and B extends A, then +only B's globals are used for both when using ``b.render()``. + +Environment globals should not be changed after loading any templates, +and template globals should not be changed at any time after loading the +template. Changing globals after loading a template will result in +unexpected behavior as they may be shared between the environment and +other templates. .. _low-level-api: diff --git a/src/jinja2/environment.py b/src/jinja2/environment.py index e7c42cd..065f32d 100644 --- a/src/jinja2/environment.py +++ b/src/jinja2/environment.py @@ -5,6 +5,7 @@ import os import sys import typing as t import weakref +from collections import ChainMap from functools import partial from functools import reduce @@ -811,16 +812,11 @@ class Environment: if template is not None and ( not self.auto_reload or template.is_up_to_date ): - # update globals if changed - new_globals = globals.items() - template.globals.items() - if new_globals: - # it is possible for the template and environment to share - # a globals object, in which case, a new copy should be - # made to avoid affecting other templates - if template.globals is self.globals: - template.globals = dict(template.globals, **globals) - else: - template.globals.update(dict(new_globals)) + # template.globals is a ChainMap, modifying it will only + # affect the template, not the environment globals. + if globals: + template.globals.update(globals) + return template template = self.loader.load(self, name, globals) if self.cache is not None: @@ -829,20 +825,26 @@ class Environment: @internalcode def get_template(self, name, parent=None, globals=None): - """Load a template from the loader. If a loader is configured this - method asks the loader for the template and returns a :class:`Template`. - If the `parent` parameter is not `None`, :meth:`join_path` is called - to get the real template name before loading. - - The `globals` parameter can be used to provide template wide globals. - These variables are available in the context at render time. - - If the template does not exist a :exc:`TemplateNotFound` exception is - raised. + """Load a template by name with :attr:`loader` and return a + :class:`Template`. If the template does not exist a + :exc:`TemplateNotFound` exception is raised. + + :param name: Name of the template to load. + :param parent: The name of the parent template importing this + template. :meth:`join_path` can be used to implement name + transformations with this. + :param globals: Extend the environment :attr:`globals` with + these extra variables available for all renders of this + template. If the template has already been loaded and + cached, its globals are updated with any new items. + + .. versionchanged:: 3.0 + If a template is loaded from cache, ``globals`` will update + the template's globals instead of ignoring the new values. .. versionchanged:: 2.4 - If `name` is a :class:`Template` object it is returned from the - function unchanged. + If ``name`` is a :class:`Template` object it is returned + unchanged. """ if isinstance(name, Template): return name @@ -852,18 +854,31 @@ class Environment: @internalcode def select_template(self, names, parent=None, globals=None): - """Works like :meth:`get_template` but tries a number of templates - before it fails. If it cannot find any of the templates, it will - raise a :exc:`TemplatesNotFound` exception. + """Like :meth:`get_template`, but tries loading multiple names. + If none of the names can be loaded a :exc:`TemplatesNotFound` + exception is raised. + + :param names: List of template names to try loading in order. + :param parent: The name of the parent template importing this + template. :meth:`join_path` can be used to implement name + transformations with this. + :param globals: Extend the environment :attr:`globals` with + these extra variables available for all renders of this + template. If the template has already been loaded and + cached, its globals are updated with any new items. + + .. versionchanged:: 3.0 + If a template is loaded from cache, ``globals`` will update + the template's globals instead of ignoring the new values. .. versionchanged:: 2.11 - If names is :class:`Undefined`, an :exc:`UndefinedError` is - raised instead. If no templates were found and names + If ``names`` is :class:`Undefined`, an :exc:`UndefinedError` + is raised instead. If no templates were found and ``names`` contains :class:`Undefined`, the message is more helpful. .. versionchanged:: 2.4 - If `names` contains a :class:`Template` object it is returned - from the function unchanged. + If ``names`` contains a :class:`Template` object it is + returned unchanged. .. versionadded:: 2.3 """ @@ -888,9 +903,8 @@ class Environment: @internalcode def get_or_select_template(self, template_name_or_list, parent=None, globals=None): - """Does a typecheck and dispatches to :meth:`select_template` - if an iterable of template names is given, otherwise to - :meth:`get_template`. + """Use :meth:`select_template` if an iterable of template names + is given, or :meth:`get_template` if one name is given. .. versionadded:: 2.3 """ @@ -901,18 +915,40 @@ class Environment: return self.select_template(template_name_or_list, parent, globals) def from_string(self, source, globals=None, template_class=None): - """Load a template from a string. This parses the source given and - returns a :class:`Template` object. + """Load a template from a source string without using + :attr:`loader`. + + :param source: Jinja source to compile into a template. + :param globals: Extend the environment :attr:`globals` with + these extra variables available for all renders of this + template. If the template has already been loaded and + cached, its globals are updated with any new items. + :param template_class: Return an instance of this + :class:`Template` class. """ globals = self.make_globals(globals) cls = template_class or self.template_class return cls.from_code(self, self.compile(source), globals, None) def make_globals(self, d): - """Return a dict for the globals.""" - if not d: - return self.globals - return dict(self.globals, **d) + """Make the globals map for a template. Any given template + globals overlay the environment :attr:`globals`. + + Returns a :class:`collections.ChainMap`. This allows any changes + to a template's globals to only affect that template, while + changes to the environment's globals are still reflected. + However, avoid modifying any globals after a template is loaded. + + :param d: Dict of template-specific globals. + + .. versionchanged:: 3.0 + Use :class:`collections.ChainMap` to always prevent mutating + environment globals. + """ + if d is None: + d = {} + + return ChainMap(d, self.globals) class Template: |
