On Jun 1, 3:07 am, Russell Keith-Magee <russ...@keith-magee.com>
wrote:
>
> My concern here is YAGNI. Can you suggest a specific use case where
> this will actually be required?
>

I agree with the YAGNI principle generally, but to my mind it's
something to bear in mind if one were implementing a new feature with
a fair amount of additional code. In this case, we're talking about a
choice between (using pseudo-names for the functions)

    # in Django code after importing settings
    configure_logging_from_dict(settings.LOGGING) # your proposal

versus

    # in Django code after importing settings
    settings_callback() # my proposal

# in settings.py
def settings_callback():
    configure_logging_from_dict(LOGGING) # the normal case

which has the advantage that I can replace the "normal case" with any
specific stuff that I want. For example, pick between different
configurations depending on some other value in settings.py:

# in settings.py
def settings_callback():
    if DEBUG:
        logging_config = DEV_LOGGING
    else:
        logging_config = PROD_LOGGING
    configure_logging_from_dict(logging_config)

I don't think this use case would be uncommon. Now I'm not saying you
couldn't do this any other way, but the above seems a reasonably
natural way of doing it.

> No - the startup files would be imported immediately after
> configuration has completed, but before any models have been loaded.
> This is probably closest to PRE_MODEL_CALLBACKS in your setup. This
> means you could connect to the class_prepared signal in startup.py.
>

Okay. I thought you were talking about POST_MODEL_CALLBACK because you
mentioned importing models in startup.py.

> There is a potential race condition here. A model import that occurs
> in startup.py will cause the model loading infrastructure for that
> model to be invoked. This means it would be possible to load a model
> before you get a chance to install the class_prepared signal handler.
> However, the same race condition exists in your own proposal. If you
> did a model import in a pre-model-callback, that would cause those
> models to be registered, so a pre-model callback can't actually
> guarantee that the models *haven't* been loaded.

But that can be easily covered by documentation. The point of the
having the two callback points is that you can add hooks before and
after model loading. It's easy enough to prominently warn in the
documentation that PRE_MODEL_CALLBACK functions should not expect
models to be available, and that "if you want to do that, put it in a
POST_MODEL_CALLBACK". The very prefixes PRE_MODEL_ and POST_MODEL_
give some indication about the availability of models. (By the way,
those names xxx_CALLBACKS are just placeholders, and if better names
could be found, then that would be great.)

With your proposal, the race condition is harder to avoid because
there's only one callback point. If I want, in a callback, to import a
model, I can't place it in a POST_MODEL_CALLBACK because there isn't
one.

> This is just one of the unfortunate eccentricities of Django's model
> loading and registration process. The only solution I can offer here
> is to say that the order in which startup.py's will be invoked will be
> predictable, so if you need to guaranteed that a class-prepared
> handler will be added, you can add an app to INSTALLED_APPS whose only
> purpose is to register a handler for class_prepared signals, or jiggle
> the order of INSTALLED_APPS to avoid this sort of side effect.

Well, isn't my proposal an alternative solution? What's wrong with
having PRE_MODEL_ and POST_MODEL callback points? Is it an aesthetic
objection? The example I gave initialises logging in the bootstrap
callback, hooks class_prepared in the pre_model callback and imports a
model in the post_model callback. From my perspective, it seems to
cover pretty much what most use cases would need (if that's not so,
please chime in with the not-covered use cases).

> The issue of adding other signals is orthogonal to the logging
> request, AFAICT. You could certainly make an argument for a
> "apps_loaded" signal that is emitted after all the INSTALLED_APPS have
> been loaded. However, I don't see that this is a specific requirement
> for logging, and as long as we provide an entry point early in the

Sure, but I believe that people have brought up the startup hook issue
before, not always in the context of logging. To quote Simon Willison
[1]:

"The problem is that Django's startup execution order is poorly
defined - stuff relating to settings is lazily evaluated the first
time it's needed, and there's no real documented place to put code
that needs to be executed once (the fact so many registration things
end up being called from urls.py is a good indicator that this is a
problem)."

The way I've set things up, init_logging is called only once, though
settings.py is imported twice.

Logging is just one instance of something you want to do early in the
startup process. The mechanism I've provided is more general than the
minimum needed for logging, and the only compelling reason I can see
for disallowing it is that you think it somehow allows people to shoot
themselves in the foot, or "it's just not the Django Way". The default
settings.py created by django-admin startproject could have the
init_logging callback defined as in my example, so that users don't
need to do anything more than set up settings.LOGGING according to
their needs. IOW, for many uses, the users have no extra work to do
between our two proposals. So, easy things are easy. But having the
more general mechanism also makes harder things possible at almost no
extra cost, and in my view that's a good thing.

> startup process, I don't see that this is something that requires a
> callback system independent of Django's existing signals framework.
> Again, if you can provide a specific use case to prove otherwise, I'm
> willing to be convinced.

It's hardly a "callback system independent of Django's existing
signals framework", by which I mean it's hardly worthy of calling it a
"system" as it's a lot simpler. Basically, just something like

    if hasattr(settings, 'PRE_MODEL_CALLBACKS'):
        callbacks = settings.PRE_MODEL_CALLBACKS
        for callback in callbacks:
            callback()

which is pretty darn simple, to my mind and hardly a competing
framework to the signals stuff. If you think the names would cause
confusion (e.g. BOOTSTRAP, CALLBACK, then those can easily be changed.

If your objections are on aesthetic/taste grounds, I completely
understand - that's a personal preference and there's no point in
trying to sway anyone from their personal style preferences. But in
terms of meeting the functional requirement (both logging
configuration and the more general "do stuff on start-up once and once
only") and bang for buck, what I've proposed is simpler, involves less
code  than a separate infrastructure of startup.py files, and provides
for more advanced usages (e.g. importing models in a callback without
worrying about race conditions) without making the simple cases
harder.

Regards,

Vinay Sajip

[1] http://groups.google.com/group/django-developers/msg/19f1ba7e4614cff6

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

Reply via email to