[Django] #23436: Should use abspath for default settings.BASE_DIR

32 views
Skip to first unread message

Django

unread,
Sep 6, 2014, 5:55:40 AM9/6/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
--------------------------------------+--------------------
Reporter: hjwp | Owner: nobody
Type: Cleanup/optimization | Status: new
Component: Uncategorized | Version: 1.6
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------------+--------------------
ref the default project template's settings.py, BASE_DIR is defined as:

{{{
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
}}}

Because there's no abspath, you can get different results for BASE_DIR
depending on how settings.py is run. This is an edge case, since
settings.py is almost always imported by django in the right context, but
still -- compare:

python manage.py shell
>>> from django.conf import settings
print(BASE_DIR)

With:

python -i myproject/settings.py
>>> print(BASE_DIR)

As an aside, in general, in any path wrangling you should always call
abspath first. Try running this script, saved to a file:

import os
print(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

As to tests, the only one I can think of would be a pretty horrible one
involving actually callling out using subprocess.Popen to a python -i of
the project template settings file, so maybe ok not to bother?

credit to @CleanCut for turning me onto this weirdness!

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

Django

unread,
Sep 6, 2014, 5:56:08 AM9/6/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
-------------------------------------+-------------------------------------
Reporter: hjwp | Owner: nobody
Type: | Status: new
Cleanup/optimization | Version: 1.6
Component: Uncategorized | Resolution:
Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by hjwp):

* needs_better_patch: => 0
* needs_tests: => 0
* needs_docs: => 0


Old description:

> ref the default project template's settings.py, BASE_DIR is defined as:
>
> {{{
> BASE_DIR = os.path.dirname(os.path.dirname(__file__))
> }}}
>
> Because there's no abspath, you can get different results for BASE_DIR
> depending on how settings.py is run. This is an edge case, since
> settings.py is almost always imported by django in the right context, but
> still -- compare:
>
> python manage.py shell
> >>> from django.conf import settings
> print(BASE_DIR)
>
> With:
>
> python -i myproject/settings.py
> >>> print(BASE_DIR)
>
> As an aside, in general, in any path wrangling you should always call
> abspath first. Try running this script, saved to a file:
>
> import os
> print(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
> print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
>
> As to tests, the only one I can think of would be a pretty horrible one
> involving actually callling out using subprocess.Popen to a python -i of
> the project template settings file, so maybe ok not to bother?
>
> credit to @CleanCut for turning me onto this weirdness!

New description:

ref the default project template's settings.py, BASE_DIR is defined as:

{{{
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
}}}

Because there's no abspath, you can get different results for BASE_DIR
depending on how settings.py is run. This is an edge case, since
settings.py is almost always imported by django in the right context, but
still -- compare:

{{{
python manage.py shell
>>> from django.conf import settings
print(BASE_DIR)
}}}

With:

{{{
python -i myproject/settings.py
>>> print(BASE_DIR)
}}}

As an aside, in general, in any path wrangling you should always call
abspath first. Try running this script, saved to a file:

import os
print(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

As to tests, the only one I can think of would be a pretty horrible one
involving actually callling out using subprocess.Popen to a python -i of
the project template settings file, so maybe ok not to bother?

credit to @CleanCut for turning me onto this weirdness!

--

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:1>

Django

unread,
Sep 6, 2014, 5:56:46 AM9/6/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
-------------------------------------+-------------------------------------
Reporter: hjwp | Owner: nobody

Type: | Status: new
Cleanup/optimization | Version: 1.6
Component: Uncategorized | Resolution:
Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by hjwp:

Old description:

> ref the default project template's settings.py, BASE_DIR is defined as:
>
> {{{
> BASE_DIR = os.path.dirname(os.path.dirname(__file__))
> }}}
>
> Because there's no abspath, you can get different results for BASE_DIR
> depending on how settings.py is run. This is an edge case, since
> settings.py is almost always imported by django in the right context, but
> still -- compare:
>
> {{{
> python manage.py shell
> >>> from django.conf import settings
> print(BASE_DIR)
> }}}
>
> With:
>
> {{{
> python -i myproject/settings.py
> >>> print(BASE_DIR)
> }}}
>
> As an aside, in general, in any path wrangling you should always call
> abspath first. Try running this script, saved to a file:
>
> import os
> print(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
> print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
>
> As to tests, the only one I can think of would be a pretty horrible one
> involving actually callling out using subprocess.Popen to a python -i of
> the project template settings file, so maybe ok not to bother?
>
> credit to @CleanCut for turning me onto this weirdness!

New description:

ref the default project template's settings.py, BASE_DIR is defined as:

{{{
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
}}}

Because there's no abspath, you can get different results for BASE_DIR
depending on how settings.py is run. This is an edge case, since
settings.py is almost always imported by django in the right context, but
still -- compare:

{{{
python manage.py shell
>>> from django.conf import settings
print(BASE_DIR)
}}}

With:

{{{
python -i myproject/settings.py
>>> print(BASE_DIR)
}}}

As an aside, in general, in any path wrangling you should always call
abspath first. Try running this script, saved to a file:

{{{
import os
print(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
}}}

As to tests, the only one I can think of would be a pretty horrible one
involving actually callling out using subprocess.Popen to a python -i of
the project template settings file, so maybe ok not to bother?

credit to @CleanCut for turning me onto this weirdness!

--

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:2>

Django

unread,
Sep 6, 2014, 7:08:40 AM9/6/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
-------------------------------------+-------------------------------------
Reporter: hjwp | Owner: nobody
Type: | Status: closed
Cleanup/optimization | Version: 1.6
Component: Uncategorized | Resolution: duplicate

Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by timgraham):

* status: new => closed
* resolution: => duplicate


Comment:

Duplicate of #21409

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:3>

Django

unread,
Sep 6, 2014, 7:36:33 AM9/6/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
-------------------------------------+-------------------------------------
Reporter: hjwp | Owner: nobody

Type: | Status: closed
Cleanup/optimization | Version: 1.6
Component: Uncategorized | Resolution: duplicate
Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by hjwp):

Sorry, should have spotted that. I did do a search for BASE_DIR before
submitting, but it didn't show up...

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:4>

Django

unread,
Sep 6, 2014, 7:54:48 AM9/6/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
-------------------------------------+-------------------------------------
Reporter: hjwp | Owner: nobody
Type: | Status: new

Cleanup/optimization | Version: 1.6
Component: Uncategorized | Resolution:
Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by hjwp):

* status: closed => new
* resolution: duplicate =>


Comment:

with due respect, I'm going to tentatively re-open this ticket. #21409
seems to be about changing BASE_DIR to being a different directory,
whereas this ticket asks for BASE_DIR to be an absolute path.

#21409 was closed by @aaugustin, appropriately I think, because BASE_DIR
is exactly where it should be.

But after that, actually, the discussion in #21409 looks like it started
to be about whether the path should be absolute or relative. I couldn't
see any arguments for keeping it relative, and there's definitely an
argument against it (ie, it can cause confusion as per my initial
submission above, and also, it's the default in Python3.4 anyway, so if we
don't do it then that introduces different behaviours for django under
python2 vs 3)

One person did make a case for using joins of `os.pardir` instead of
double path.dirname, which I guess would be fine (although it feels like
the "more semantic" benefit is outweighed by the "harder to read" price).
But in the meantime, I feel like BASE_DIR should definitely be an absolute
path...

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:5>

Django

unread,
Sep 9, 2014, 2:40:05 PM9/9/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
-------------------------------------+-------------------------------------
Reporter: hjwp | Owner: nobody

Type: | Status: new
Cleanup/optimization | Version: 1.6
Component: Uncategorized | Resolution:
Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by timgraham):

I'm still not sure what problems this would cause in practice. In the
example you give, `python -i myproject/settings.py`, it seems like
`__file__` doesn't return an absolute on Python 3.4 since it's covered by:
"with the sole exception of `__main__.__file__` when a script has been
executed directly using a relative path".

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:6>

Django

unread,
Sep 28, 2014, 7:32:24 AM9/28/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
--------------------------------------+------------------------------------

Reporter: hjwp | Owner: nobody
Type: Cleanup/optimization | Status: new
Component: Core (Other) | Version: 1.6
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Changes (by aaugustin):

* component: Uncategorized => Core (Other)
* stage: Unreviewed => Accepted


Comment:

I don't see what purpose would be served by executing a Django settings
module with `python path/to/settings.py`. Is there a use case for which
this is a proper solution? As Tim explained, we can't cover that case on
Python 3.4+ anyway, so I'm going to ignore it.

For consistency between Python 3.4+ and earlier versions, I'm going to add
the `abspath` call, even thought I'm still convinced it doesn't change
anything, except maybe in contrived made-up examples.

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:7>

Django

unread,
Sep 28, 2014, 7:44:23 AM9/28/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
--------------------------------------+------------------------------------

Reporter: hjwp | Owner: nobody
Type: Cleanup/optimization | Status: new
Component: Core (Other) | Version: 1.6
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------

Comment (by aaugustin):

I don't want to backport this to 1.7.x because the default settings are
often reproducted in training materials, especially books, and even small
differences can unsettle beginners.

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:8>

Django

unread,
Sep 28, 2014, 7:45:22 AM9/28/14
to django-...@googlegroups.com
#23436: Should use abspath for default settings.BASE_DIR
--------------------------------------+------------------------------------
Reporter: hjwp | Owner: nobody
Type: Cleanup/optimization | Status: closed

Component: Core (Other) | Version: 1.6
Severity: Normal | Resolution: fixed

Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Changes (by Aymeric Augustin <aymeric.augustin@…>):

* status: new => closed

* resolution: => fixed


Comment:

In [changeset:"caf5cd7ba7c9d73194e394a26c81f1a677d54a6c"]:
{{{
#!CommitTicketReference repository=""
revision="caf5cd7ba7c9d73194e394a26c81f1a677d54a6c"
Fixed #23436 -- Made BASE_DIR absolute in settings template.

This ensures consistency between Python 3.4+ and earlier versions.

Thanks Harry Percival for the report.
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/23436#comment:9>

Reply all
Reply to author
Forward
0 new messages