#35046: BlankChoiceIterator causes AttributeError for some existing packages and
projects
--------------------------------------+------------------------
               Reporter:  hazho       |          Owner:  nobody
                   Type:  Bug         |         Status:  new
              Component:  Utilities   |        Version:  5.0
               Severity:  Normal      |       Keywords:
           Triage Stage:  Unreviewed  |      Has patch:  0
    Needs documentation:  0           |    Needs tests:  0
Patch needs improvement:  0           |  Easy pickings:  1
                  UI/UX:  0           |
--------------------------------------+------------------------
 The iterators should have method __len__ ...!
 while this is not the case for (BlankChoiceIterator) in the current
 version of Django (5.0), this is why the following error raised:
 AttributeError: 'BlankChoiceIterator' object has no attribute '__len__'.
 Did you mean: '__le__'?


 to solve this, simply the BlankChoiceIterator class should have the method
 __len__ returning 0 as indication for emptiness.

 this is important because the package maintainer or project author may not
 find the way to update their code accordingly, for example the projects
 depend on django-countries would have the following trace raised (note
 there is no obvious indication where in the project code is the main
 causer that calls __len__ of BlankChoiceIterator objects:


 {{{

 Traceback (most recent call last):
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\core\handlers\exception.py", line 55, in inner
     response = get_response(request)
                ^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\core\handlers\base.py", line 197, in _get_response
     response = wrapped_callback(request, *callback_args,
 **callback_kwargs)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\django_simple_payment_system\wallets\views.py", line 38, in
 index
     return render(request, template_path, context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\shortcuts.py", line 24, in render
     content = loader.render_to_string(template_name, context, request,
 using=using)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\loader.py", line 62, in render_to_string
     return template.render(context, request)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\backends\django.py", line 61, in render
     return self.template.render(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 171, in render
     return self._render(context)
            ^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 163, in _render
     return self.nodelist.render(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 1000, in render
     return SafeString("".join([node.render_annotated(context) for node in
 self]))
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 961, in render_annotated
     return self.render(context)
            ^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\loader_tags.py", line 210, in render
     return template.render(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 173, in render
     return self._render(context)
            ^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 163, in _render
     return self.nodelist.render(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 1000, in render
     return SafeString("".join([node.render_annotated(context) for node in
 self]))
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 961, in render_annotated
     return self.render(context)
            ^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\defaulttags.py", line 241, in render
     nodelist.append(node.render_annotated(context))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 961, in render_annotated
     return self.render(context)
            ^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 1065, in render
     return render_value_in_context(output, context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\template\base.py", line 1042, in render_value_in_context
     value = str(value)
             ^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\forms\utils.py", line 79, in __str__
     return self.as_widget()
            ^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\forms\boundfield.py", line 95, in as_widget
     attrs = self.build_widget_attrs(attrs, widget)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\forms\boundfield.py", line 270, in build_widget_attrs
     widget.use_required_attribute(self.initial)
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\forms\widgets.py", line 781, in use_required_attribute
     first_choice = next(iter(self.choices), None)
                              ^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django_countries\widgets.py", line 29, in get_choices
     self._choices: ChoiceList = list(self._choices)
                                 ^^^^^^^^^^^^^^^^^^^
   File "C:\env_django_simple_payment_system\Lib\site-
 packages\django\utils\functional.py", line 188, in __wrapper__
     return getattr(result, __method_name)(*args, **kw)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 AttributeError: 'BlankChoiceIterator' object has no attribute '__len__'.
 Did you mean: '__le__'?
 }}}


 the solution can be as the following:

 {{{

 # django/utils/choices.py
 class BlankChoiceIterator(BaseChoiceIterator):
     """Iterator to lazily inject a blank choice."""
     # existing code

     def __len__(self):
         return 0
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/35046>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018c7a30cea5-ebffe305-7941-418d-8ec5-a561360ee2c8-000000%40eu-central-1.amazonses.com.

Reply via email to