Opened 16 years ago

Closed 16 years ago

#11190 closed (invalid)

urlresolvers.reverse: possibilities = self.reverse_dict.getlist(lookup_view) is empty but everything else seems to work

Reported by: deltapass@… Owned by: nobody
Component: Core (Other) Version: 1.0
Severity: Keywords: urlresolvers reverse possibilities self.reverse_dict.getlist() permalink
Cc: '1.0.2 final', debian version Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I've think i've found a bug with urlresolvers.reverse() (in use with permalink).

I've tried to get this code working:

    _CONTENT_VIEW = "page.views.view_by_content"
    _CONTENT_VIEW_KWARG_ID_NAME = "content_id"

    *snip*
    # Point to content with default id
    @property
    @models.permalink
    def link(self):
        """Return link to content with
        Page calculated default content id.

        """
        return (self._CONTENT_VIEW, (),
                {self._CONTENT_VIEW_KWARG_ID_NAME:
                 self.to_object.get_default_content_id()})

And some method that is similar but in another subclass from this classes superclass.

My urls.py and page/urls.py are looking like this:

urlpatterns = patterns('',
    # Example:
    # (r'^evankproj/', include('evankproj.foo.urls')),

    # Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
    # to INSTALLED_APPS to enable admin documentation:
    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    (r'^admin/(.*)', admin.site.root),
    (r"^content/", include("evankproj.page.urls")),
    *snip*

and

urlpatterns = patterns("evankproj.page.views",
    (r"^$", "index"),
    (r"^(?P<content_id>\d+)/$", "view_by_content"),
)

Here is a pdb session where i tried to track down the problem, I entered
from within my link method:

Quit the server with CONTROL-C.
> *snip*page/models.py(258)link()
-> return (self._CONTENT_VIEW, (),
(Pdb) up
> /usr/lib/pymodules/python2.5/django/db/models/__init__.py(29)inner()
-> bits = func(*args, **kwargs)
(Pdb) list
 24  	        (viewname, viewargs)
 25  	        (viewname, viewargs, viewkwargs)
 26  	    """
 27  	    from django.core.urlresolvers import reverse
 28  	    def inner(*args, **kwargs):
 29  ->	        bits = func(*args, **kwargs)
 30  	        return reverse(bits[0], None, *bits[1:3])
 31  	    return inner
[EOF]
(Pdb) list 20
 15  	ADD, CHANGE, BOTH = 1, 2, 3
 16  	
 17  	def permalink(func):
 18  	    """
 19  	    Decorator that calls urlresolvers.reverse() to return a URL using
 20  	    parameters returned by the decorated function "func".
 21  	
 22  	    "func" should be a function that returns a tuple in one of the
 23  	    following formats:
 24  	        (viewname, viewargs)
 25  	        (viewname, viewargs, viewkwargs)
(Pdb) next
> /usr/lib/pymodules/python2.5/django/db/models/__init__.py(30)inner()
-> return reverse(bits[0], None, *bits[1:3])
(Pdb) p bits
('page.views.view_by_content', (), {'content_id': 1})
(Pdb) p bits[1:3]
((), {'content_id': 1})
(Pdb) next
NoReverseMatch: NoRevers...found.",)
> /usr/lib/pymodules/python2.5/django/db/models/__init__.py(30)inner()
-> return reverse(bits[0], None, *bits[1:3])
(Pdb) down
> /usr/lib/pymodules/python2.5/django/core/urlresolvers.py(254)reverse()
-> *args, **kwargs)))
(Pdb) list
249  	    args = args or []
250  	    kwargs = kwargs or {}
251  	    if prefix is None:
252  	        prefix = get_script_prefix()
253  	    return iri_to_uri(u'%s%s' % (prefix, get_resolver(urlconf).reverse(viewname,
254  ->	            *args, **kwargs)))
255  	
256  	def clear_url_caches():
257  	    global _resolver_cache
258  	    global _callable_cache
259  	    _resolver_cache.clear()
(Pdb) p prefiyx
*** NameError: NameError("name 'prefiyx' is not defined",)
(Pdb) prefix
u'/'
(Pdb) down
> /usr/lib/pymodules/python2.5/django/core/urlresolvers.py(243)reverse()
-> "arguments '%s' not found." % (lookup_view, args, kwargs))
(Pdb) p lookup_view
<function view_by_content at 0x93ad6bc>
(Pdb) p lookup_view(1)
*** TypeError: TypeError('view_by_content() takes exactly 2 arguments (1 given)',)
(Pdb) p lookup_view("x", content_id=1)
<django.http.HttpResponse object at 0x947deac>
(Pdb) list
238  	                    unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()])
239  	                    candidate = result % unicode_kwargs
240  	                if re.search(u'^%s' % pattern, candidate, re.UNICODE):
241  	                    return candidate
242  	        raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
243  ->	                "arguments '%s' not found." % (lookup_view, args, kwargs))
244  	
245  	def resolve(path, urlconf=None):
246  	    return get_resolver(urlconf).resolve(path)
247  	
248  	def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
(Pdb) p args
()
(Pdb) kwargs
{'content_id': 1}
(Pdb) p lookup_view(*(), **kwargs)
*** TypeError: TypeError('view_by_content() takes exactly 2 non-keyword arguments (0 given)',)
(Pdb) p lookup_view("x", **kwargs)
<django.http.HttpResponse object at 0x93b1f8c>
(Pdb) list
249  	    args = args or []
250  	    kwargs = kwargs or {}
251  	    if prefix is None:
252  	        prefix = get_script_prefix()
253  	    return iri_to_uri(u'%s%s' % (prefix, get_resolver(urlconf).reverse(viewname,
254  	            *args, **kwargs)))
255  	
256  	def clear_url_caches():
257  	    global _resolver_cache
258  	    global _callable_cache
259  	    _resolver_cache.clear()
(Pdb) down
*** Newest frame
(Pdb) list
260  	    _callable_cache.clear()
261  	
262  	def set_script_prefix(prefix):
263  	    """
264  	    Sets the script prefix for the current thread.
265  	    """
266  	    if not prefix.endswith('/'):
267  	        prefix += '/'
268  	    _prefixes[currentThread()] = prefix
269  	
270  	def get_script_prefix():
(Pdb) list cur
*** Error in argument: 'cur'
(Pdb) list 24
 19  	
 20  	try:
 21  	    reversed
 22  	except NameError:
 23  	    from django.utils.itercompat import reversed     # Python 2.3 fallback
 24  	    from sets import Set as set
 25  	
 26  	_resolver_cache = {} # Maps urlconf modules to RegexURLResolver instances.
 27  	_callable_cache = {} # Maps view and url pattern names to their view functions.
 28  	
 29  	# SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
(Pdb) list 243
238  	                    unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()])
239  	                    candidate = result % unicode_kwargs
240  	                if re.search(u'^%s' % pattern, candidate, re.UNICODE):
241  	                    return candidate
242  	        raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
243  ->	                "arguments '%s' not found." % (lookup_view, args, kwargs))
244  	
245  	def resolve(path, urlconf=None):
246  	    return get_resolver(urlconf).resolve(path)
247  	
248  	def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
(Pdb) list 238
233  	                    unicode_args = [force_unicode(val) for val in args]
234  	                    candidate =  result % dict(zip(params, unicode_args))
235  	                else:
236  	                    if set(kwargs.keys()) != set(params):
237  	                        continue
238  	                    unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()])
239  	                    candidate = result % unicode_kwargs
240  	                if re.search(u'^%s' % pattern, candidate, re.UNICODE):
241  	                    return candidate
242  	        raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
243  ->	                "arguments '%s' not found." % (lookup_view, args, kwargs))
(Pdb) p params
*** NameError: NameError("name 'params' is not defined",)
(Pdb) p set(params)
*** NameError: NameError("name 'params' is not defined",)
(Pdb) p kwargs
{'content_id': 1}
(Pdb) dict(k, force_unicode(v)) for k, v in kwargs.items())
*** SyntaxError: invalid syntax (<stdin>, line 1)
(Pdb) dict((k, force_unicode(v)) for k, v in kwargs.items())
{'content_id': u'1'}
(Pdb) list 230
225  	        except (ImportError, AttributeError), e:
226  	            raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e))
227  	        possibilities = self.reverse_dict.getlist(lookup_view)
228  	        for possibility, pattern in possibilities:
229  	            for result, params in possibility:
230  	                if args:
231  	                    if len(args) != len(params):
232  	                        continue
233  	                    unicode_args = [force_unicode(val) for val in args]
234  	                    candidate =  result % dict(zip(params, unicode_args))
235  	                else:
(Pdb) p possibilities
[]
(Pdb) p self
<RegexURLResolver evankproj.urls ^/>
(Pdb) p self.reverse_dict
<MultiValueDict: {None: [([(u'images/%(path)s', ['path'])], 'images/(?P<path>.*)$'), ([(u'css/%(path)s', ['path'])], 'css/(?P<path>.*)$'), ([(u'content/%(content_id)s/', ['content_id'])], 'content/(?P<content_id>\\d+)/$'), ([(u'content/', [])], 'content/$'), ([(u'admin/%(_0)s', ['_0'])], 'admin/(.*)')], <functools.partial object at 0x93a64dc>: [([(u'content/', [])], 'content/$')], <function view_by_content at 0x93a1b8c>: [([(u'content/%(content_id)s/', ['content_id'])], 'content/(?P<content_id>\\d+)/$')], <function serve at 0x93ad924>: [([(u'images/%(path)s', ['path'])], 'images/(?P<path>.*)$'), ([(u'css/%(path)s', ['path'])], 'css/(?P<path>.*)$')], <bound method AdminSite.root of <django.contrib.admin.sites.AdminSite object at 0x92ca10c>>: [([(u'admin/%(_0)s', ['_0'])], 'admin/(.*)')]}>
(Pdb) p self.reverse_dict.getlist(lookup_view)
[]

As far as i could see, property is no problem in conjunction with permalink, as an inner func gets created and returned. Also, get_absolute_url as name is no requirement for permalink (as you may think after reading the permalink documentation). So, technically this should work.

The function view_by_content gets found, so searching for it works. possibilities is empty, which is self.reverse_dict.getlist(lookup_view), so here seems to be the bug (if it is one). The for loop never gets entered and the raise statements gets executed.

The problem seems to be completly related about this, as this is a change i just made in my mercurial (revision control) repository and nothing but this changed.

Change History (1)

comment:1 by Chris Beaven, 16 years ago

Resolution: invalid
Status: newclosed

Wow, that's one full-on ugly ticket description :P
I'm going to mark invalid as I can't see the problem which this ticket is attempting to address.

Firstly, perhaps _CONTENT_VIEW should really be "evankproj.page.views.view_by_content"? In any case, you should just be using a named url pattern.
I'd ask about this in IRC or the google group. If it does turn out you have discovered a Django bug, open a new ticket with only the relevant bits which show the problem (or if you're game, attach a test case which shows the bug)

Note: See TracTickets for help on using tickets.
Back to Top