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

TypeError __str__ returned non-string (type PosixPath) /debug_toolbar/templates/debug_toolbar/panels/staticfiles.html #2002

Closed
dougwmorrow opened this issue Sep 6, 2024 · 11 comments · Fixed by #2021

Comments

@dougwmorrow
Copy link

dougwmorrow commented Sep 6, 2024

django-debug-toolbar: 4.4.6

I'm just providing a solution to an event I came across. Line 29 for /debug_toolbar/templates/debug_toolbar/panels/staticfiles.html needs to have {{ staticfile }} updated to {{ staticfile|stringformat:"s" }}

Below is the event that happened.

When running uvicorn project.asgi:application --host 127.0.0.1 --port 8001 and then loading 127.0.0.1:8001 in a new web browser, the TypeError str returned non-string (type PosixPath). Specifically focusing on {{ staticfile }}. I updated {{ staticfile }} to {{ staticfile|stringformat:"s" }} and that resolved the TypeError.

Thanks!

@tim-schilling
Copy link
Member

Can you share more information about your project? This isn't something we've come across elsewhere. We'd want to know what your static file was and what your installed apps and middlewares are?

@dougwmorrow
Copy link
Author

dougwmorrow commented Sep 11, 2024

Sure. After switching to ASGI and installing uvicorn, I noticed the event.

Legend:

  1. Apps/Middlewares
  2. Static File Previous
  3. Static File Updated

Look for the line {{ staticfile|stringformat:"s" }} is shown on.

  1. Apps/Middlewares:
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "django_select2",
    "django_htmx",
    "crispy_forms",
    "crispy_tailwind",
    "storages",
    "widget_tweaks",
    "django_user_agents",
    "debug_toolbar",
    'django.contrib.gis',
    'leaflet',
    'channels',
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "django_htmx.middleware.HtmxMiddleware",
    "django_user_agents.middleware.UserAgentMiddleware",
    "project.middleware.DeviceDetailsMiddleware",
    "debug_toolbar.middleware.DebugToolbarMiddleware",
    'whitenoise.middleware.WhiteNoiseMiddleware',
]
  1. Static File Previous:
{% load i18n %}

<h4>{% blocktrans count staticfiles_dirs|length as dirs_count %}Static file path{% plural %}Static file paths{% endblocktrans %}</h4>
{% if staticfiles_dirs %}
  <ol>
    {% for prefix, staticfiles_dir in staticfiles_dirs %}
      <li>{{ staticfiles_dir }}{% if prefix %} {% blocktrans %}(prefix {{ prefix }}){% endblocktrans %}{% endif %}</li>
    {% endfor %}
  </ol>
{% else %}
  <p>{% trans "None" %}</p>
{% endif %}

<h4>{% blocktrans count staticfiles_apps|length as apps_count %}Static file app{% plural %}Static file apps{% endblocktrans %}</h4>
{% if staticfiles_apps %}
  <ol>
    {% for static_app in staticfiles_apps %}
      <li>{{ static_app }}</li>
    {% endfor %}
  </ol>
{% else %}
  <p>{% trans "None" %}</p>
{% endif %}

<h4>{% blocktrans count staticfiles|length as staticfiles_count %}Static file{% plural %}Static files{% endblocktrans %}</h4>
{% if staticfiles %}
  <dl>
    {% for staticfile in staticfiles %}
      <dt><strong><a class="toggleTemplate" href="{{ staticfile.url }}">{{ staticfile }}</a></strong></dt>
      <dd><samp>{{ staticfile.real_path }}</samp></dd>
    {% endfor %}
  </dl>
{% else %}
  <p>{% trans "None" %}</p>
{% endif %}


{% for finder, payload in staticfiles_finders.items %}
  <h4>{{ finder }} ({% blocktrans count payload|length as payload_count %}{{ payload_count }} file{% plural %}{{ payload_count }} files{% endblocktrans %})</h4>
  <table>
    <thead>
      <tr>
        <th>{% trans 'Path' %}</th>
        <th>{% trans 'Location' %}</th>
      </tr>
    </thead>
    <tbody>
      {% for path, real_path in payload %}
        <tr>
          <td>{{ path }}</td>
          <td>{{ real_path }}</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>
{% endfor %}
  1. Static File Updated:
{% load i18n %}

<h4>{% blocktrans count staticfiles_dirs|length as dirs_count %}Static file path{% plural %}Static file paths{% endblocktrans %}</h4>
{% if staticfiles_dirs %}
  <ol>
    {% for prefix, staticfiles_dir in staticfiles_dirs %}
      <li>{{ staticfiles_dir }}{% if prefix %} {% blocktrans %}(prefix {{ prefix }}){% endblocktrans %}{% endif %}</li>
    {% endfor %}
  </ol>
{% else %}
  <p>{% trans "None" %}</p>
{% endif %}

<h4>{% blocktrans count staticfiles_apps|length as apps_count %}Static file app{% plural %}Static file apps{% endblocktrans %}</h4>
{% if staticfiles_apps %}
  <ol>
    {% for static_app in staticfiles_apps %}
      <li>{{ static_app }}</li>
    {% endfor %}
  </ol>
{% else %}
  <p>{% trans "None" %}</p>
{% endif %}

<h4>{% blocktrans count staticfiles|length as staticfiles_count %}Static file{% plural %}Static files{% endblocktrans %}</h4>
{% if staticfiles %}
  <dl>
    {% for staticfile in staticfiles %}
      <dt><strong><a class="toggleTemplate" href="{{ staticfile.url }}">{{ staticfile|stringformat:"s" }}</a></strong></dt>
      <dd><samp>{{ staticfile.real_path }}</samp></dd>
    {% endfor %}
  </dl>
{% else %}
  <p>{% trans "None" %}</p>
{% endif %}


{% for finder, payload in staticfiles_finders.items %}
  <h4>{{ finder }} ({% blocktrans count payload|length as payload_count %}{{ payload_count }} file{% plural %}{{ payload_count }} files{% endblocktrans %})</h4>
  <table>
    <thead>
      <tr>
        <th>{% trans 'Path' %}</th>
        <th>{% trans 'Location' %}</th>
      </tr>
    </thead>
    <tbody>
      {% for path, real_path in payload %}
        <tr>
          <td>{{ path }}</td>
          <td>{{ real_path }}</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>
{% endfor %}

@tim-schilling
Copy link
Member

Whoops, I meant I'd like to reproduce this with one of my applications. I'll see if using uvicorn and asgi with the example app reproduces it, but I may need you to create a reproducible example otherwise.

@dougwmorrow
Copy link
Author

Let me know.

@tim-schilling
Copy link
Member

@dougwmorrow yeah, I wasn't able to reproduce this on my project. Any chance you can help create a minimal reproducible example?

@dougwmorrow
Copy link
Author

Will look into it.

@matthiask
Copy link
Member

https://github.com/jazzband/django-debug-toolbar/blob/22c92e493cae2c96f72ce0021a43405fe1f084c9/debug_toolbar/panels/staticfiles.py#L23-L24

This is potentially broken. I don't immediately see why you'd want to use a Path() object as an argument to static() or {% static %} but it can happen (obviously), and we shouldn't fail.

I'd appreciate a test case, the fix would be to return str(self.path) in def __str__.

@dougwmorrow
Copy link
Author

dougwmorrow commented Oct 26, 2024

@tim-schilling Thanks for waiting. I was getting runners high working on my own project. I found the root cause. 3 files of mine showed up as PosixPath objects. I needed to update my vendor files from context_processors.py as seen below. Essentially, ensure x.relative_to(static_dir) is a string.

static_dir = settings.BASE_DIR / "static"
vendor_dir = static_dir / "vendor"
js_files = [str(x.relative_to(static_dir)) for x in vendor_dir.glob("/*.js")]
css_files = [str(x.relative_to(static_dir)) for x in vendor_dir.glob("
/*.css")]

No updates are needed for debug_toolbar/panels/staticfiles.html. Let me know if you need any additional information. Feel free to close this out.

@matthiask
Copy link
Member

I have pushed a fix in #2021 but didn't manage to construct a test for this just yet.

@dougwmorrow If you could test the fix that would be great. Adding str() in your own code -- as suggested above -- would be a good workaround for this issue, but we should fix it for real in the toolbar also.

@dougwmorrow
Copy link
Author

dougwmorrow commented Oct 27, 2024

@matthiask I've tested 2021 push.

  1. I removed str() from [x.relative_to(static_dir)...] etc.
  2. I adjusted for loop from debug_toolbar/templates/debug_toolbar/panels/staticfiles.html as seen below.
{% for staticfile in staticfiles %}
<dt>
  <strong>
    <!-- Display the index and type of each staticfile -->
    Index: {{ forloop.counter }}, Type: {{ staticfile|get_class_name }}
  </strong>
</dt>
<!-- Safely attempt to display the string representation -->
<li>
  {{ staticfile|stringformat:"s" }}
</li>
<li>{{ staticfile|safe_str }}</li>
<!-- Display other attributes if they are safe to access -->
<dd>
  <samp>{{ staticfile.real_path }}</samp>
</dd>
{% endfor %}
  1. I updated debug_toolbar/panels/staticfiles.py to include your suggestions:
class StaticFile:
    """
    Representing the different properties of a static file.
    """

    def __init__(self, *, path, url):
        self.path = path
        self._url = url

    def __str__(self):
        return self.path

    def real_path(self):
        return finders.find(self.path)

    def url(self):
        return self._url

The other suggestions were also added to the file.

  1. The files names were properly output in Static files tab in DJDT as seen below.
Static files
Index: 1, Type: StaticFile
vendor/htmx/htmx.min.js
vendor/htmx/htmx.min.js
src/static/vendor/htmx/htmx.min.js
Index: 2, Type: StaticFile
vendor/flowbite/flowbite.min.js
vendor/flowbite/flowbite.min.js
src/static/vendor/flowbite/flowbite.min.js
Index: 3, Type: StaticFile
vendor/jquery/jquery.min.js
vendor/jquery/jquery.min.js
src/static/vendor/jquery/jquery.min.js
  1. The below output is shown when I use the original code from debug_toolbar/panels/staticfiles.py. Without your suggestions.
Static files
Index: 1, Type: StaticFile
Error converting to string: __str__ returned non-string (type PosixPath)
src/static/vendor/htmx/htmx.min.js
Index: 2, Type: StaticFile
Error converting to string: __str__ returned non-string (type PosixPath)
src/static/vendor/flowbite/flowbite.min.js
Index: 3, Type: StaticFile
Error converting to string: __str__ returned non-string (type PosixPath)
src/static/vendor/jquery/jquery.min.js

I plan to update my file paths and so on, but I hope this level of clarity helps. I reverted the for loop in staticfiles.html back to it's original state. Everything looks good.

@matthiask
Copy link
Member

Thank you! I'm grateful for the additional confirmation that the updated code works.

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.

3 participants