#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 django-updates+unsubscr...@googlegroups.com.
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