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

error of collectstatic for AttributeError: 'Path' object has no attribute 'startswith' #86

Closed
raccoonyy opened this issue Jun 13, 2016 · 6 comments

Comments

@raccoonyy
Copy link

raccoonyy commented Jun 13, 2016

Question: Why I have to use root property of Path instance?

1. First try

# settings.py
STATIC_ROOT = root.path('collected_static/')
STATICFILES_DIRS = [
    root.path("web/static"),
    ("components", root.path("bower_components/")),
]

then, run collectstatic command print out:

Traceback (most recent call last):
  File "./manage.py", line 12, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/management/__init__.py", line 353, in execute_from_command_line
    utility.execute()
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/management/__init__.py", line 345, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/management/__init__.py", line 195, in fetch_command
    klass = load_command_class(app_name, subcommand)
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/management/__init__.py", line 40, in load_command_class
    return module.Command()
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 32, in __init__
    self.storage.path('')
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/utils/functional.py", line 204, in inner
    self._setup()
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/contrib/staticfiles/storage.py", line 394, in _setup
    self._wrapped = get_storage_class(settings.STATICFILES_STORAGE)()
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/contrib/staticfiles/storage.py", line 39, in __init__
    *args, **kwargs)
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/files/storage.py", line 185, in __init__
    self.location = abspathu(self.base_location)
  File "/Users/raccoony/.pyenv/versions/3.5.0/lib/python3.5/posixpath.py", line 357, in abspath
    if not isabs(path):
  File "/Users/raccoony/.pyenv/versions/3.5.0/lib/python3.5/posixpath.py", line 64, in isabs
    return s.startswith(sep)
AttributeError: 'Path' object has no attribute 'startswith'

when I print out STATICFILES_DIRS

[<Path:/Users/raccoony/smartstudy/box/web/static>, ('components', <Path:/Users/raccoony/smartstudy/box/bower_components>)]

not path strings but Path instaces.

2. so I tried root property

# settings.py (modeified)
STATIC_ROOT = root.path('collected_static/').root
STATICFILES_DIRS = [
    root.path("web/static").root,
    ("components", root.path("bower_components/").root),
]

then, run collectstatic will be:

Traceback (most recent call last):
  File "./manage.py", line 12, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/management/__init__.py", line 353, in execute_from_command_line
    utility.execute()
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/management/__init__.py", line 345, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/management/__init__.py", line 195, in fetch_command
    klass = load_command_class(app_name, subcommand)
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/core/management/__init__.py", line 40, in load_command_class
    return module.Command()
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/contrib/staticfiles/management/commands/collectstatic.py", line 32, in __init__
    self.storage.path('')
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/utils/functional.py", line 204, in inner
    self._setup()
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/contrib/staticfiles/storage.py", line 394, in _setup
    self._wrapped = get_storage_class(settings.STATICFILES_STORAGE)()
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/contrib/staticfiles/storage.py", line 37, in __init__
    check_settings(base_url)
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/django/contrib/staticfiles/utils.py", line 58, in check_settings
    (settings.MEDIA_ROOT == settings.STATIC_ROOT)):
  File "/Users/raccoony/.pyenv/versions/3.5.0/envs/box/lib/python3.5/site-packages/environ/environ.py", line 670, in __eq__
    return self.__root__ == other.__root__
AttributeError: 'str' object has no attribute '__root__'

3. I used root property to MEDIA_ROOT too.

# settings.py (2nd modified)
STATIC_ROOT = root.path('collected_static/').root
STATICFILES_DIRS = [
    root.path("web/static").root,
    ("components", root.path("bower_components/").root),
]

MEDIA_ROOT = root.path("media").root

then, collectstatic runs without error.

@raccoonyy raccoonyy changed the title fail of collectstatic for AttributeError: 'Path' object has no attribute 'startswith' error of collectstatic for AttributeError: 'Path' object has no attribute 'startswith' Jun 15, 2016
@raccoonyy
Copy link
Author

I solved this problem by str(root.path('path')) instead of root.path('path').root

@daimon99
Copy link

daimon99 commented Sep 7, 2016

I think you still use path the wrong way.
You should use like this:

STATIC_ROOT = root('collected_static')

but not

STATIC_ROOT = root.path('collected_static')

When you need a str object, you should use root('sub path'), when you wanta a Path object to chain it, you should use root.path('sub path')

@raccoonyy
Copy link
Author

wow!

Thanks for your reply.

You are correct.

mlissner added a commit to freelawproject/courtlistener that referenced this issue Apr 14, 2022
It turns out that environ, though a very good package, does a bit of a
funny thing. Instead of relying on pathlib to do path stuff, it ships
its own pathlib-alike. I'm guessing this is because it pre-dates
pathlib, but regardless, and unfortunately, their built-in path
library is a bit buggy. One type of bug is that it can't always
compare strings with it's Path objects properly, throwing errors like
those seen in:

    joke2k/django-environ#86

So, to fix this: Simply don't use the built-in pathlib-alike, and use
the real pathlib library instead.
@mlissner
Copy link

I ran into this bug too. I haven't read all of environ's source code, but for us this happened because we had a setting configured using env.Path, and then we had it overridden in a django test via a decorator, like so:

@override_settings(
    MEDIA_ROOT=os.path.join(settings.INSTALL_ROOT, "cl/assets/media/test/")
)

Something about that combination made the built in env.path thing try to compare one of its Path objects with a str, which failed, as in the first comment.

The solution, which you can see here was to not use environ's path objects, and instead rely on pathlib.

I'm not super familiar with this package and why it has its own pathlib-alike (guessing it pre-dates py3?), but I think I'd suggest removing it since it appears to have bugs and (probably?) isn't necessary anymore. Instead, we could pretty easily, I think, migrate people to pathlib in their settings, though that'd be a backwards incompatible change.

@sergeyklay
Copy link
Collaborator

I'm not super familiar with this package and why it has its own pathlib-alike (guessing it pre-dates py3?), but I think I'd suggest removing it since it appears to have bugs and (probably?) isn't necessary anymore. Instead, we could pretty easily, I think, migrate people to pathlib in their settings, though that'd be a backwards incompatible change.

This is on my plans, but right now I don't have enough time for that. I'll try to return to this topic a bit later

@axelkolo
Copy link

axelkolo commented Nov 8, 2022

t

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

No branches or pull requests

5 participants