-
-
Notifications
You must be signed in to change notification settings - Fork 7k
Fix handling of optional guardian module #6038
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
Conversation
Currently you cannot import something from rest_framework without configuring Django, since accessing `settings.INSTALLED_APPS` for the django-guardian throws an ImproperlyConfigured error: from rest_framework.exceptions import ErrorDetail ~/Vcs/django-rest-framework/rest_framework/exceptions.py in <module>() 16 17 from rest_framework import status ---> 18 from rest_framework.compat import unicode_to_repr 19 from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList 20 ~/Vcs/django-rest-framework/rest_framework/compat.py in <module>() 144 guardian = None 145 try: --> 146 if 'guardian' in settings.INSTALLED_APPS: 147 import guardian # noqa 148 except ImportError: ~/Vcs/django-rest-framework/.venv/lib/python3.6/site-packages/django/conf/__init__.py in __getattr__(self, name) 54 """Return the value of a setting and cache it in self.__dict__.""" 55 if self._wrapped is empty: ---> 56 self._setup(name) 57 val = getattr(self._wrapped, name) 58 self.__dict__[name] = val ~/Vcs/django-rest-framework/.venv/lib/python3.6/site-packages/django/conf/__init__.py in _setup(self, name) 39 "You must either define the environment variable %s " 40 "or call settings.configure() before accessing settings." ---> 41 % (desc, ENVIRONMENT_VARIABLE)) 42 43 self._wrapped = Settings(settings_module) ImproperlyConfigured: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings. I've thought about just trying to import it always, but that is some overhead for something optional, and would also regress encode#1712 - except for when handling the `ImproperlyConfigured` from there. Since it only gets used by a single class this patch moves the code there.
try: | ||
import guardian | ||
except ImportError: | ||
raise ImportError('Using DjangoObjectPermissionsFilter, but django-guardian is not installed') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think changing this to an ImportError instead of AssertionError is fine?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably use a RuntimeError
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also include a comment linking to the issue. (As we do in filter_queryset
)
# Fixes (#1712). We keep the try/except for the test suite. | ||
guardian = None | ||
try: | ||
if 'guardian' in settings.INSTALLED_APPS: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the INSTALLED_APPS
bit necessary? i.e. Could we not just try the import guardian
? (And so keep this in compat
, which is my only reservation here. Otherwise I'd be 👍)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would regress #1712 then.
And catching the ImproperlyConfigured error then (in a generic way) also could have the side effect of swallowing Django's own.
OK. Thanks. I’ll have a think about it. (Will need to talk to management, but it would be nice to be able to do it.) |
script: | ||
- python setup.py bdist_wheel | ||
- tox --installpkg ./dist/djangorestframework-*.whl | ||
- tox # test sdist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This env isn't listed in tox?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does not have to be (AFAIK).
Or at least I've assumed that tox-travis will use the explicit env, but haven't checked the build job itself.
(tox itself allows for tox -e dist-optionals
)
tox.ini
Outdated
@@ -14,6 +15,8 @@ DJANGO = | |||
2.0: django20 | |||
2.1: django21 | |||
master: djangomaster | |||
OPTIONALS = | |||
1: optionals |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would prefer to override the [testenv]
block instead of introducing an environment variable. eg,
[testenv:py36-django20-optionals
deps =
{[testenv]deps}
-rrequirments/requirments-optionals.txt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to run it on Travis then? via an explicit TOXENV?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done it like that now (see #6041).
Hm. I'm kind of the opinion that we just break django-guardian support out of core, similar to how django-filter was broken out of core. This is partly driven by the fact that django-guardian seems to have largely ceased development. It's been almost a year since its latest release, and during that time, there's only been ~20 odd commits, most of them related to basic maintenance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks - some comments here. Let's try to keep the change footprint as minimal as possible.
Aside: The other tack onto this would be to use import guardian
in compat.py
without the if in INSTALLED_APPS
check, and push this issue onto Django guardian instead. (ie. should allow import prior to django.configure()
)
- python setup.py bdist_wheel | ||
- tox --installpkg ./dist/djangorestframework-*.whl | ||
- tox # test sdist | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes don't immediately appear connected to the rest of the PR - let's drop them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are necessary to cover the change in the tests, but I can move that into a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=> #6041
tox.ini
Outdated
@@ -14,6 +15,8 @@ DJANGO = | |||
2.0: django20 | |||
2.1: django21 | |||
master: djangomaster | |||
OPTIONALS = | |||
1: optionals |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we drop this and just keep the tox.ini as-is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is required for running tests with (and without) optionals on Travis.
tox-travis uses this then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blueyed see my comment above on how to override without requiring the additional factor.
try: | ||
import guardian | ||
except ImportError: | ||
raise ImportError('Using DjangoObjectPermissionsFilter, but django-guardian is not installed') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably use a RuntimeError
.
try: | ||
import guardian | ||
except ImportError: | ||
raise ImportError('Using DjangoObjectPermissionsFilter, but django-guardian is not installed') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also include a comment linking to the issue. (As we do in filter_queryset
)
@tomchristie - thoughts on breaking this out into a separate package? |
@rpkilby - In favor. (Tho I guess that's a separate issue to this. Even as a third party lib it'd still need to use deferred import to avoid this issue, right?) |
@tomchristie - As a third party package the issue is a little less critical, since you're not likely to directly import it. That said, the change is still an improvement. I'm happy to do the work, breaking this out into a separate package. Should we do this under the encode organization, or should I place it under my personal account? |
Personal account. |
Closing in favor of #6054, given the additional testing/CI changes. |
@tomchristie, I've broken out the code related to django-guardian into https://github.com/rpkilby/django-rest-framework-guardian Specifically, this is just the If everything looks good, I'll go ahead and publish django-rest-framework-guardian, and we can start deprecating here. |
Currently you cannot import something from rest_framework without
configuring Django, since accessing
settings.INSTALLED_APPS
for thedjango-guardian throws an ImproperlyConfigured error:
I've thought about just trying to import it always, but that is some
overhead for something optional, and would also regress #1712 - except
for when handling the
ImproperlyConfigured
from there.Since it only gets used by a single class this patch moves the code
there.