Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RecursionError when using child component #477

Closed
gschurck opened this issue Nov 14, 2022 · 8 comments · May be fixed by #736
Closed

RecursionError when using child component #477

gschurck opened this issue Nov 14, 2022 · 8 comments · May be fixed by #736

Comments

@gschurck
Copy link

There is a Recursion Error with my website.

RecursionError: maximum recursion depth exceeded while calling a Python object

I use a view with a loop that call multiple children
category.html

{% extends 'base.html' %}

{% block content %}
    {% load bulma_tags %}
    {% load unicorn %}
    {% load static %}

    <div class="container" unicorn:view>
        <h1 class="is-1 is-size-1-desktop is-size-3-mobile has-text-centered mb-6">Catégorie</h1>
        <div class="level" id="ttm-loader">
            <div class="level-item">
                <img src="{% static 'monkaS.png' %}" class="loader">
            </div>
        </div>
        <div id="loading-images">
            {% for category in categories %}
                <h2 class="is-2 is-size-3-desktop is-size-4-mobile is-underlined has-text-centered my-5">{{ category.name }}</h2>
                <div class="columns is-multiline is-mobile is-variable is-1-touch">
                    {% for meme in category.get_memes %}
                        <div class="column is-one-quarter-desktop is-half-touch">
                            {% unicorn 'meme_card' parent=view meme=meme key=meme.id id=meme.id %} {# parent=view #}
                        </div>
                    {% endfor %}
                </div>
            {% endfor %}
        </div>
        {% unicorn 'meme_card_modal' %}
    </div>
    {% include 'loader.html' %}


{% endblock %}

Children :
meme_card.html

{% load unicorn %}
<div class="card">
    <a class="card-image js-modal-trigger" data-target="modal-js-example" unicorn:click="set_meme_modal({{ meme.id }})">
        <figure class="image">
            {#                <img src="{{ meme.image.url }}" class="is-hidden-touch" alt="Meme image" loading="lazy">#}
            {% if meme.image_compressed %}
                <img src="{{ meme.image_compressed.url }}" alt="Meme image" loading="lazy">
            {% else %}
                <img src="{{ meme.image.url }}" alt="Meme image" loading="lazy">
            {% endif %}
        </figure>
    </a>
    <div class="card-content">
        <div class="media">
            <div class="media-content">
                <p class="title is-size-4-desktop is-size-5-mobile">{{ meme.title }}</p>
                {% if memes_result %}
                    <p class="is-6">Catégories :</p>
                    <div class="tags">
                        {% for category in meme.categories.all %}
                            <span class="tag">{{ category.name }}</span>
                        {% endfor %}
                    </div>
                {% endif %}
                <p class="subtitle is-6">Uploadé par <b>{{ meme.user }}</b></p>
            </div>
        </div>
    </div>
    {% unicorn 'meme_card_footer' meme=meme %}
</div>

With an onclick that calls a function on the component, to update a value from its parent.

meme_card.py

from django_unicorn.components import UnicornView

from trouve_ton_meme.models import Meme


class MemeCardView(UnicornView):
    meme = Meme.objects.none()

    def set_meme_modal(self, meme_id):
        print(self.parent)
        meme = Meme.objects.get(id=meme_id)
        self.parent.modal_meme = meme

The function is executed and I have access to the parent, but it crashes after that with a RecursionError from Django unicorn.
I don't really understand where is the recursion here.
Is there any solution for this ?

@adamghill
Copy link
Owner

Thanks for sending so much of your code. Does print(self.parent) trigger the recursion or is it self.parent.modal_meme = meme? Can you show me the view code that has modal_meme as property/attribute?

@gschurck
Copy link
Author

Nope, even by setting the function as an empty one with only pass triggers the error.

Sure

CategoryView

from django_unicorn.components import UnicornView

from trouve_ton_meme.models import Category, Meme


class CategoryView(UnicornView):
    categories = Category.objects.none()
    modal_meme = Meme.objects.none()

    def mount(self):
        self.categories = [Category.objects.get(id=self.request.GET.get('id'))]

Full trace of the error

<trouve_ton_meme.components.category.CategoryView object at 0x7f42cb2f9300>    # <--- parent view is printed here
Internal Server Error: /unicorn/message/meme_card
Traceback (most recent call last):
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "<decorator-gen-25>", line 2, in message
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django_unicorn/decorators.py", line 21, in timed
    result = func(*args, **kwargs)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django_unicorn/views/__init__.py", line 44, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django/utils/decorators.py", line 133, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django/views/decorators/http.py", line 43, in inner
    return func(request, *args, **kwargs)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django_unicorn/views/__init__.py", line 528, in message
    json_result = _handle_component_request(request, component_request)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django_unicorn/views/__init__.py", line 374, in _handle_component_request
    return _process_component_request(request, component_request)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django_unicorn/views/__init__.py", line 210, in _process_component_request
    cache.set(component.component_cache_key, get_cacheable_component(component))
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django_unicorn/utils.py", line 88, in get_cacheable_component
    component.parent = get_cacheable_component(component.parent)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django_unicorn/utils.py", line 92, in get_cacheable_component
    child = get_cacheable_component(child)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/django_unicorn/utils.py", line 95, in get_cacheable_component
    pickle.dumps(component)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/bs4/element.py", line 956, in __getnewargs__
    return (str(self),)
RecursionError: maximum recursion depth exceeded while calling a Python object
[16/Nov/2022 18:10:26] "POST /unicorn/message/meme_card HTTP/1.1" 500 20366

@gschurck
Copy link
Author

@adamghill Do you have any solution for this ?

@adamghill
Copy link
Owner

@gschurck Unfortunately, I haven't been able to replicate this on my own.

The last line

File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.10/lib/python3.10/site-packages/bs4/element.py", line 956, in __getnewargs__
    return (str(self),)

is in beautifulsoup, but not sure what might be going on in there. If you can add a small example to https://github.com/adamghill/django-unicorn/tree/main/example that replicates this that would be helpful.

@pa-hqt
Copy link

pa-hqt commented Aug 28, 2023

Hello adamghill,
first of all: I like the idea of django-unicorn very much and honor your work on that project! ✨

I think I might have a similiar issue, but cannot provide a full example currently.
This error happend, when I tried to update the child view by an click event (not on initial load) like:

<a unicorn:click.prevent="select_item()" href="#">Click</a>
Internal Server Error: /unicorn/message/mdb-index-info-details-items
Traceback (most recent call last):
  File "C:\Python\venv\lib\site-packages\django\core\handlers\exception.py", line 56, in inner
    response = get_response(request)
  File "C:\Python\venv\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "<decorator-gen-25>", line 2, in message
  File "C:\Python\venv\lib\site-packages\django_unicorn\decorators.py", line 21, in timed
    result = func(*args, **kwargs)
  File "C:\Python\venv\lib\site-packages\django_unicorn\views\__init__.py", line 44, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Python\venv\lib\site-packages\django\utils\decorators.py", line 134, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "C:\Python\venv\lib\site-packages\django\views\decorators\http.py", line 43, in inner
    return func(request, *args, **kwargs)
  File "C:\Python\venv\lib\site-packages\django_unicorn\views\__init__.py", line 526, in message
    json_result = _handle_component_request(request, component_request)
  File "C:\Python\venv\lib\site-packages\django_unicorn\views\__init__.py", line 372, in _handle_component_request
    return _process_component_request(request, component_request)
  File "C:\Python\venv\lib\site-packages\django_unicorn\views\__init__.py", line 208, in _process_component_request
    cache_full_tree(component)
  File "C:\Python\venv\lib\site-packages\django_unicorn\utils.py", line 93, in cache_full_tree
    with CacheableComponent(root) as caching:
  File "C:\Python\venv\lib\site-packages\django_unicorn\utils.py", line 189, in __enter__
    pickle.dumps(component)
  File "C:\Python\venv\lib\site-packages\bs4\element.py", line 975, in __getnewargs__
    return (str(self),)
RecursionError: maximum recursion depth exceeded while getting the str of an object
<!-- mdb-index-info-details-parent.html -->
{% load unicorn %}

<div class="row">
    <div class="col-lg-4">
        {% unicorn 'mdb-index-info-details-items' %}
    </div>
    <div class="col-lg-8">
        {% unicorn 'mdb-index-info-details-chart' %}
    </div>
</div>

MdbIndexInfoDetailsItemsView and MdbIndexInfoDetailsChartView are childs of MdbIndexInfoDetailsParentView

I noticed that the error disappeared when I removed one of the childs from parent template.
But when both are used in the parent template and I try some updating action, the RecursionError is raised.

@adamghill
Copy link
Owner

I think this should be fixed by 0.58.0 of Unicorn which drastically improves child components so I'm going to close it. If it is still a problem feel free to re-open this and create a failing test or small example in the example app of this repo to replicate, though. Thank you!

@philippe-docourt
Copy link

philippe-docourt commented Jan 8, 2024

I ran into a similar problem. After some digging I was able to set a breakpoint into the faulty callstack:

  1. getattr, element.py:1608
  2. loads, pickle.py:32
  3. decode, default.py:452
  4. get, default.py:265
  5. _get, cache.py:99
  6. _decorator, cache.py:29
  7. get, cache.py:92
  8. restore_from_cache, cacher.py:116
  9. create, unicorn_view.py:833
  10. [...]

Turned out that django_redis, uses beautifulsoup4 for serialization, and bs4 relies on some hardcoded delimiter name "Tag":
if len(tag) > 3 and tag.endswith('Tag'):

My problem was that my component view contained a variable for a model named Tag, which was serialized as is, without any escaping. This made the whole parsing fail :-O

@adamghill
Copy link
Owner

@philippe-docourt Thanks for debugging this and figuring out the callstack that triggered it. Any idea of how to stop it from happening?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants