On Monday, January 28, 2013 9:30:44 AM UTC-8, Christopher Medrela wrote:
>
> Hello everybody. 
>
> 1. I'm working on ticket #17093 [1]. You can see progress of work at 
> github [2]. It's about rewritting django.template so it won't use global 
> state. I wrote this post to tell you what I did so far, what issues I deal 
> with and to discuss them. I hope that we will gain valuable experience in 
> splitting Django into smaller parts. 
>

While I agree with Carl that there is some danger of this being a change 
that flirts with "is it worth it" in the specific sense - there is a 
certain value in exploring the process of making Django less monolithic, 
and in the long run I think Django HAS to go that way. So bravo for taking 
this on. I've only skimmed the code so far - but will try to make a couple 
comments.


>
> 3. The goal is to write TemplateEngine class to encapsulate global state. 
> For backward-compatibility, there will be one global instance of 
> TemplateEngine and all functions like loader.get_template should delegate 
> to the instance. Tests need to be rewritten. 
>

A global instance is probably unavoidable, but by itself doesn't result in 
less global state - it just wraps it all in one global object. Functions 
that need Engine state should be passed an engine instance explicitly 
wherever possible - not importing default_engine at module level. 


> 4. What I did so far? I created base.TemplateEngine class and 
> base.default_engine global instance. Some functions (mostly from loader 
> module: get_template, find_template, select_templates and from base module: 
> add_to_builtins, get_library, compile_string) delegates to appropriate 
> methods of default_engine. I also partially rewrote tests (only 
> tests/regressiontests/templates/tests.py) so they don't use default_engine 
> directly. 
>

Since your TemplateEngine class represents the current snapshot of features 
 you may want to make rename your current TemplateEngine to 
BaseTemplateEngine, and then call TemplateEngineWithBuiltins just 
TemplateEngine.

Any internal method that uses default_engine needs to allow for an explicit 
engine instance, or be deprecated with a suggestion to use an alternate API 
that can support an explicit engine. Anything much less is just shuffling 
chairs without actually removing the dependency on global state.


> 5. The one issue is that the engine is needed everywhere. For example, 
> consider creating base.Template instance. At the time of creation the 
> template source is being compiled. The template may load a library, so the 
> compilation cannot be done without list of all libraries defined in the 
> engine. To sum up, we cannot just type Template(source), because this 
> depends on template engine. I solved this issue by passing engine instance 
> to Template constructor. In order not to break everything, I renamed 
> Template class to _Template and created function called Template that does 
> "return _Template(default_engine, **kwargs)". 
>

could this just be handled with a kwarg of engine=default_engine?


>
> 10. As I said I'm trying to rewrite tests from tests.py file so they won't 
> use the default engine. There left some sources of dependency on default 
> engine. One of them are include tags in tests which load templates at the 
> time of importing module [3] (or the source of problems is in 
> base.Library.inclusion_tag?). Another one are template.response module [4] 
> and all other test files (i. e. callables.py, custom.py). I didn't dig into 
> these issues deeply yet since I wanted to publish my results and take some 
> feedback. 
>

The shim point for all of these IMO should be your get_default_engine 
function - and although the following suggestion requires some more 
thought/discussion - the default_engine instance might as well live on the 
settings object, where the setting itself could be the default engine 
class. Killing the global state doesn't mean killing the state - but it is 
a process of gathering it from all the scattered places, and consolidating 
it into piles, then gather those piles into one (or very few) pile and 
creating that only in the entry point(s) so that it is in fact local state. 
If there is a candidate for the one pile, it is settings. But I'm waxing 
now ;-)

The testing environment is both a great design testbed, and eventual 
benefactor of reduced global state. It will help surface all the template 
related API that is making global assumptions, and those places will need 
to be modified in a way to take an explicit engine instance, using the 
default if one is not passed.



> 11. A small issue is test 
> regressiontests.views.tests.debug.DebugViewTests.test_template_loader_postmortem.
>  
> The problem is in method 
> django.views.debug.ExceptionReporter.get_traceback_data. It uses 
> template_source_loaders global state which was removed. We can just use 
> default_engine. I wonder when a Django developer uses a custom template 
> engine instead of the global default one then they will get faulty error 
> message. Is it important or negligible? 
>
> 12. As I said there is another source of global-state -- django settings. 
> I think we can deal with that by passing all necessary settings (like 
> TEMPLATE_DEBUG, INSTALLED_APPS etc.) to TemplateEngine constructor. It 
> means that we won't be able to create the default engine at the 
> module-level since settings are not configured at the time of importing 
> template package. We will have to make instancing default engine lazy. It 
> means that there should be get_default_engine() function which creates the 
> default engine at the very first call. I wonder if we will fall into 
> circular imports hell or other problems... 
>

There are dragons lurking here for sure - I agree that all template related 
settings need to be passed and encapsulated in the engine.

This is an example of why you want to avoid the module level 
"default_engine" instance completely if at all possible

If the route was to consolidate global state onto settings (something that 
merits a whole different discussion) - then any internal reference to a 
template related setting should switch to retrieving it from a passed 
explicit engine instance, that would default to settings.default_engine

-Preston

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to