0

I'm writing small application for my Django project. The idea is to create some kind of widgets - small view functions that can be added in templates via inclusion tags to display some aside information (e.g. blog records, quick-links lists, menus etc). I want to create something similar to default templatetag registering mechanism for this purpose. Generally I need the following components:

  • custom decorator to register these view functions
  • some function that will search for specific modules in all installed apps ('widgets.py' for now) at startup (possibly called from settings.py)

What I wrote by now looks like this:

# -*- coding: utf-8 -*-


REGISTERED_WIDGETS = []

class Library(object):
    """
    Utility class for registering defined widgets
    """
    def widget(self, view=None, name=None, form_class=None):
        if name is None:
            name = view.__name__
        def decorator(view):
            print 'registering', view
            REGISTERED_WIDGETS.append((view, name, form_class))
            def wrapper(request, *args, **kwargs):
                return view(request, *args, **kwargs)
            return wrapper
        if view is not None:
            return decorator(view)
        else:
            return decorator

def search_widgets():
    """
    Search for 'widgets.py' modules inside installed applications and import them,
    hence initializing its registration
    """
    from django.conf import settings

    for app in settings.INSTALLED_APPS:
        try:
            module = __import__(app + '.widgets')
            print module
        except ImportError:
            pass
        else:
            print 'Imported widgets from ', app

if __name__ == '__main__':
    search_widgets()
    print REGISTERED_WIDGETS

To test it I added this stub widget in widgets.py inside one of my applications:

# -*- coding: utf-8 -*-

from experiments.widgets.base import Library

register = Library()

@register.widget(name='dummy')
def dummy_widget(request):
    pass

I stuck now one silly issue - when run this module I suppose to see REGISTERED_WIDGETS variable filled with found widgets, but it's empty. Actually I see this output:

>>> %run widgets/base.py
<module 'django' from '/usr/lib/python2.7/dist-packages/django/__init__.pyc'>
Imported widgets from  django.contrib.admin
registering <function dummy_widget at 0x364f320>
<module 'experiments' from '/home/east825/Development/pycharm-experiments/experiments/../experiments/__init__.pyc'>
Imported widgets from  experiments.layout
[]

It seems that I forgot something very important about python shared global variables behavior. Any advices?

1 Answer 1

1

It could be possible that your module base.py gets imported twice?

Add a print at the beginning of base.py with a "print 'base.py executed'" or something.

edit: or even better, print id(REGISTERED_WIDGETS)

Sign up to request clarification or add additional context in comments.

7 Comments

Yes, you're right - I added print id(REGISTERED_WIDGETS) and saw that this list created twice: first time in main script, second - on first import through import call. It's rather strange, I always suppose that once module was loaded into sys.modules it not imported again. But I still don't know how to workaround this, and it actually shouldn't be the reason - on second import list is not created again and tuple added this time should stay in it, isn't it? I also tried to make this list Library class' field, but it didn't help anyway. Any ideas?
if you see the print twice it means that two different REGISTERED_WIDGETS are defined. The first one is the one you print, and the second one is the one which is not empty. Checking python docs: docs.python.org/library/functions.html#__import__ it seems that the problem is with your import, you should add the parameters globals and locals like in the example. I think that the reason is imported twice it's because it cannot see that the module is already imported (because you didn't specify the global variables). Try it and let me know :)
no it didn't help. After playing for sometime with imports and shared global variables I came to one idea - I can't see this changes inside module there this variables defined cause it have it's own private copies of them defined then you run it as main script. I rewrote my app to store registered widgets as field of Library object and then in search_widgets I append this field value to REGISTERED_WIDGETS (actually almost as it done in Django itself). This works as expected. Thank you for pushing me toward right solution.
I don't understand this sentence: "I can't see this changes inside module there this variables defined cause it have it's own private copies of them defined then you run it as main script" :) but anyway, I'm glad it helped you :)
I mean that if you have script where some global mutable value defined and you have other module that import this one and somehow change this variable (directly calling some method on this variable, using some special function or class - it doesn't matter). And then in your first module you are trying to run something like if __name__ == '__main__': import module2; module2.change_global(); print GLOBAL - you won't see any changes, cause actually who different instances of these variable exist - one in sys.modules['__main__'] and other in sys.modules['module1'].
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.