├── test_project
├── __init__.py
├── test_app
│ ├── __init__.py
│ ├── models.py
│ ├── urls.py
│ ├── signals.py
│ ├── views.py
│ └── tests.py
├── urls.py
├── templates
│ └── admin
│ │ └── base_site.html
└── settings.py
├── setup.py
├── MANIFEST.in
├── .gitignore
├── requirements
├── tox.ini
├── dev.in
├── dev38.txt
├── dev39.txt
├── dev35.txt
├── dev37.txt
└── dev36.txt
├── .coveragerc
├── .github
└── workflows
│ └── test.yml
├── manage.py
├── tox.ini
├── LICENSE
├── crum
├── signals.py
└── __init__.py
├── README.rst
├── Makefile
├── setup.cfg
└── docs
├── index.rst
├── Makefile
├── make.bat
└── conf.py
/test_project/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test_project/test_app/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test_project/test_app/models.py:
--------------------------------------------------------------------------------
1 | from . import signals # noqa
2 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Setuptools
4 | from setuptools import setup
5 |
6 |
7 | setup()
8 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include crum *.py
2 | include LICENSE
3 | include README.rst
4 | recursive-exclude test_project *
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 | build
3 | dist
4 | reports
5 | *.egg-info
6 | *.pyc
7 | *.pyo
8 | .coverage*
9 | .pytest_cache
10 | .tox
11 | *.sqlite3
12 |
--------------------------------------------------------------------------------
/requirements/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py3{5,6,7,8,9}
3 | skipsdist = true
4 |
5 | [testenv]
6 | commands =
7 | make -C .. update-requirements requirements
8 | whitelist_externals = make
9 | basepython =
10 | py35: python3.5
11 | py36: python3.6
12 | py37: python3.7
13 | py38: python3.8
14 | py39: python3.9
15 |
--------------------------------------------------------------------------------
/test_project/test_app/urls.py:
--------------------------------------------------------------------------------
1 | # Django
2 | try:
3 | from django.urls import re_path
4 | except ImportError:
5 | from django.conf.urls import url as re_path
6 |
7 | # Test App
8 | from test_project.test_app.views import index, api_index
9 |
10 |
11 | app_name = 'test_app'
12 |
13 | urlpatterns = [
14 | re_path(r'^$', index, name='index'),
15 | re_path(r'^api/$', api_index, name='api_index'),
16 | ]
17 |
--------------------------------------------------------------------------------
/requirements/dev.in:
--------------------------------------------------------------------------------
1 | # Development/test environment uses Python 3.5 - 3.8 (separate requirements files generated for each).
2 |
3 | bumpversion
4 | django
5 | django-debug-toolbar
6 | django-extensions
7 | djangorestframework
8 | flake8
9 | ipython
10 | pycodestyle
11 | pip-tools
12 | pytest
13 | pytest-cov
14 | pytest-django
15 | pytest-flake8
16 | pytest-runner
17 | setuptools-twine
18 | sphinx
19 | tox
20 | tox-gh-actions
21 | twine
22 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | branch = True
3 | omit =
4 | parallel = True
5 | data_file = .coverage${TOX_ENV_NAME}
6 |
7 | [report]
8 | # Regexes for lines to exclude from consideration
9 | exclude_lines =
10 | # Have to re-enable the standard pragma
11 | pragma: no cover
12 |
13 | # Don't complain about missing debug-only code:
14 | def __repr__
15 | if self\.debug
16 |
17 | # Don't complain if tests don't hit defensive assertion code:
18 | raise AssertionError
19 | raise NotImplementedError
20 |
21 | # Don't complain if non-runnable code isn't run:
22 | if 0:
23 | if __name__ == .__main__.:
24 |
25 | ignore_errors = True
26 |
27 |
--------------------------------------------------------------------------------
/test_project/urls.py:
--------------------------------------------------------------------------------
1 | # Django
2 | from django.conf import settings
3 | try:
4 | from django.urls import include, re_path
5 | except ImportError:
6 | from django.conf.urls import include, url as re_path
7 |
8 | include_kwargs = dict(namespace='test_app')
9 |
10 | urlpatterns = [
11 | re_path(r'^test_app/', include('test_project.test_app.urls', **include_kwargs)),
12 | ]
13 |
14 | if 'django.contrib.admin' in settings.INSTALLED_APPS:
15 | from django.contrib import admin
16 | admin.autodiscover()
17 | urlpatterns += [
18 | re_path(r'', admin.site.urls),
19 | ]
20 |
21 | if 'django.contrib.staticfiles' in settings.INSTALLED_APPS and settings.DEBUG:
22 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns
23 | urlpatterns += staticfiles_urlpatterns()
24 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | name: test
4 |
5 | on:
6 | push:
7 | pull_request:
8 | schedule:
9 | - cron: '0 9 9 * *'
10 |
11 | defaults:
12 | run:
13 | shell: bash
14 |
15 | jobs:
16 | test-tox:
17 | runs-on: ubuntu-latest
18 | strategy:
19 | matrix:
20 | python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v2
24 | - name: Setup Python ${{ matrix.python-version }}
25 | uses: actions/setup-python@v2
26 | with:
27 | python-version: ${{ matrix.python-version }}
28 | - name: Install requirements
29 | run: |
30 | python -m pip install -U pip
31 | python -m pip install -U setuptools
32 | make requirements
33 | - name: Test with Tox
34 | run: tox
35 |
--------------------------------------------------------------------------------
/test_project/templates/admin/base_site.html:
--------------------------------------------------------------------------------
1 | {# Admin site customizations. #}
2 | {% extends "admin/base.html" %}
3 | {% load i18n %}
4 |
5 | {% block title %}{{ title }} | {% trans 'Django CRUM Admin' %}{% endblock %}
6 |
7 | {% block extrastyle %}
8 | {{ block.super }}
9 | {% endblock %}
10 |
11 | {% block extrahead %}
12 | {{ block.super }}
13 | {% endblock %}
14 |
15 | {% block blockbots %}
16 | {{ block.super }}
17 | {% endblock %}
18 |
19 | {% block branding %}
20 |
{% trans 'Django CRUM Admin' %}
21 | {% endblock %}
22 |
23 | {% block userlinks %}
24 | {{ block.super }}
25 | {% endblock %}
26 |
27 | {% block nav-global %}
28 | {% if user.is_active and user.is_staff and not is_popup %}
29 | {# Placeholder for version/hostname info. #}
30 | {% endif %}
31 | {{ block.super }}
32 | {% endblock %}
33 |
34 | {% block content_title %}
35 | {{ block.super }}
36 | {% endblock %}
37 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Python
4 | import os
5 | import sys
6 |
7 | if __name__ == '__main__':
8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_project.settings')
9 | import django
10 | django.setup()
11 |
12 | # Use the runserver addr/port defined in settings.
13 | from django.conf import settings
14 | default_addr = getattr(settings, 'RUNSERVER_DEFAULT_ADDR', '127.0.0.1')
15 | default_port = getattr(settings, 'RUNSERVER_DEFAULT_PORT', 8000)
16 | from django.core.management.commands import runserver as core_runserver
17 | original_handle = core_runserver.Command.handle
18 |
19 | def handle(self, *args, **options):
20 | if not options.get('addrport'):
21 | options['addrport'] = '%s:%d' % (default_addr, int(default_port))
22 | elif options.get('addrport').isdigit():
23 | options['addrport'] = '%s:%d' % (default_addr, int(options['addrport']))
24 | return original_handle(self, *args, **options)
25 |
26 | core_runserver.Command.handle = handle
27 |
28 | from django.core.management import execute_from_command_line
29 | execute_from_command_line(sys.argv)
30 |
--------------------------------------------------------------------------------
/test_project/test_app/signals.py:
--------------------------------------------------------------------------------
1 | # Django
2 | from django.dispatch import receiver
3 |
4 | # Django-CRUM
5 | from crum import get_current_request
6 | from crum.signals import current_user_getter, current_user_setter
7 |
8 |
9 | @receiver(current_user_getter)
10 | def _get_current_user_always_raises_exception(sender, **kwargs):
11 | raise RuntimeError('this receiver always raises an exception')
12 |
13 |
14 | @receiver(current_user_getter)
15 | def _get_current_user_always_returns_invalid(sender, **kwargs):
16 | return []
17 |
18 |
19 | @receiver(current_user_getter)
20 | def _get_current_user_always_returns_false(sender, **kwargs):
21 | return False
22 |
23 |
24 | @receiver(current_user_getter)
25 | def _get_current_user_always_returns_list_with_false(sender, **kwargs):
26 | return [False]
27 |
28 |
29 | @receiver(current_user_setter)
30 | def _set_current_user_always_raises_exception(sender, **kwargs):
31 | raise RuntimeError('this receiver always raises an exception')
32 |
33 |
34 | @receiver(current_user_getter)
35 | def _get_current_user_from_drf_request(sender, **kwargs):
36 | request = get_current_request()
37 | drf_request = getattr(request, 'drf_request', None)
38 | return (getattr(drf_request, 'user', False), 0)
39 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist =
3 | py35-dj{111,20,21,22},
4 | py36-dj{20,21,22,30,31,32},
5 | py37-dj{20,21,22,30,31,32},
6 | py38-dj{22,30,31,32,main}
7 | py39-dj{22,30,31,32,main}
8 |
9 | [testenv]
10 | commands =
11 | coverage erase
12 | py.test {posargs}
13 | basepython =
14 | py35: python3.5
15 | py36: python3.6
16 | py37: python3.7
17 | py38: python3.8
18 | py39: python3.9
19 | deps =
20 | dj111: Django~=1.11.0
21 | dj111: djangorestframework~=3.11.0
22 | dj20: Django~=2.0.0
23 | dj20: djangorestframework
24 | dj21: Django~=2.1.0
25 | dj21: djangorestframework
26 | dj22: Django~=2.2.0
27 | dj22: djangorestframework
28 | dj30: Django~=3.0.0
29 | dj30: djangorestframework
30 | dj31: Django~=3.1.0
31 | dj31: djangorestframework
32 | dj32: Django>=3.2.0a0,<3.3
33 | dj32: djangorestframework
34 | djmain: https://github.com/django/django/zipball/main#egg=Django
35 | djmain: https://github.com/encode/django-rest-framework/zipball/master#egg=djangorestframework
36 | pytest
37 | pytest-cov
38 | pytest-django
39 | pytest-flake8
40 | pytest-runner
41 | setenv =
42 | DJANGO_SETTINGS_MODULE = test_project.settings
43 | PYTHONDONTWRITEBYTECODE = 1
44 | install_command = pip install --pre {opts} {packages}
45 |
46 | [testenv:py3{8,9}-djmain]
47 | ignore_outcome = true
48 |
49 | [gh-actions]
50 | python =
51 | 3.5: py35
52 | 3.6: py36
53 | 3.7: py37
54 | 3.8: py38
55 | 3.9: py39
56 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Nine More Minutes, Inc.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice,
8 | this list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in the
12 | documentation and/or other materials provided with the distribution.
13 |
14 | 3. Neither the name of Nine More Minutes, Inc. nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
19 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21 | EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/crum/signals.py:
--------------------------------------------------------------------------------
1 | # Django
2 | from django.dispatch import Signal, receiver
3 |
4 | __all__ = ['current_user_getter']
5 |
6 |
7 | # Signal used when getting current user. Receivers should return a tuple of
8 | # (user, priority).
9 | current_user_getter = Signal()
10 |
11 |
12 | # Signal used when setting current user. Takes one keyword argument: 'user'.
13 | # Receivers should store the current user as needed. Return values are ignored.
14 | current_user_setter = Signal()
15 |
16 |
17 | @receiver(current_user_getter)
18 | def _get_current_user_from_request(sender, **kwargs):
19 | """
20 | Signal handler to retrieve current user from request.
21 | """
22 | from crum import get_current_request
23 | return (getattr(get_current_request(), 'user', False), -10)
24 |
25 |
26 | @receiver(current_user_getter)
27 | def _get_current_user_from_thread_locals(sender, **kwargs):
28 | """
29 | Signal handler to retrieve current user from thread locals.
30 | """
31 | from crum import _thread_locals
32 | return (getattr(_thread_locals, 'user', False), 10)
33 |
34 |
35 | @receiver(current_user_setter)
36 | def _set_current_user_on_request(sender, **kwargs):
37 | """
38 | Signal handler to store current user to request.
39 | """
40 | from crum import get_current_request
41 | request = get_current_request()
42 | if request:
43 | request.user = kwargs['user']
44 |
45 |
46 | @receiver(current_user_setter)
47 | def _set_current_user_on_thread_locals(sender, **kwargs):
48 | """
49 | Signal handler to store current user on thread locals.
50 | """
51 | from crum import _thread_locals
52 | _thread_locals.user = kwargs['user']
53 |
--------------------------------------------------------------------------------
/test_project/test_app/views.py:
--------------------------------------------------------------------------------
1 | # Python
2 | from __future__ import with_statement
3 | from __future__ import unicode_literals
4 |
5 | # Django
6 | from django.http import HttpResponse
7 | from django.views.generic.base import View
8 | try:
9 | from django.utils.six import text_type
10 | except ImportError:
11 | text_type = str
12 |
13 | # Django-REST-Framework
14 | from rest_framework.response import Response
15 | from rest_framework.views import APIView
16 |
17 | # Django-CRUM
18 | from crum import get_current_user, impersonate
19 |
20 |
21 | class IndexView(View):
22 |
23 | def get(self, request):
24 | if request.GET.get('raise', ''):
25 | raise RuntimeError()
26 | if request.GET.get('impersonate', ''):
27 | with impersonate(None):
28 | current_user = text_type(get_current_user())
29 | else:
30 | current_user = text_type(get_current_user())
31 | return HttpResponse(current_user, content_type='text/plain')
32 |
33 |
34 | index = IndexView.as_view()
35 |
36 |
37 | class ApiIndexView(APIView):
38 |
39 | def initialize_request(self, request, *args, **kwargs):
40 | """Store the REST Framework request on the Django request."""
41 | req = super(ApiIndexView, self).initialize_request(request, *args,
42 | **kwargs)
43 | request.drf_request = req
44 | return req
45 |
46 | def get(self, request, format=None):
47 | if request.query_params.get('raise', ''):
48 | raise RuntimeError()
49 | if request.query_params.get('impersonate', ''):
50 | with impersonate(None):
51 | current_user = text_type(get_current_user())
52 | else:
53 | current_user = text_type(get_current_user())
54 | return Response(current_user)
55 |
56 |
57 | api_index = ApiIndexView.as_view()
58 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | |Build Status| |PyPI Version| |PyPI License| |Python Versions| |Django Versions| |Read the Docs|
2 |
3 | Django-CRUM
4 | ===========
5 |
6 | Django-CRUM (Current Request User Middleware) captures the current request and
7 | user in thread local storage.
8 |
9 | It enables apps to check permissions, capture audit trails or otherwise access
10 | the current request and user without requiring the request object to be passed
11 | directly. It also offers a context manager to allow for temporarily
12 | impersonating another user.
13 |
14 | It provides a signal to extend the built-in function for getting the current
15 | user, which could be helpful when using custom authentication methods or user
16 | models.
17 |
18 | Documentation can be found at `RTFD `_.
19 |
20 | It is tested against:
21 | * Django 1.11 (Python 3.5 and 3.6)
22 | * Django 2.0 (Python 3.5, 3.6 and 3.7)
23 | * Django 2.1 (Python 3.5, 3.6 and 3.7)
24 | * Django 2.2 (Python 3.5, 3.6, 3.7, 3.8 and 3.9)
25 | * Django 3.0 (Python 3.6, 3.7, 3.8 and 3.9)
26 | * Django 3.1 (Python 3.6, 3.7, 3.8 and 3.9)
27 | * Django 3.2 pre-release (Python 3.6, 3.7, 3.8 and 3.9)
28 | * Django main/4.0 (Python 3.8 and 3.9)
29 |
30 | .. |Build Status| image:: https://img.shields.io/github/workflow/status/ninemoreminutes/django-crum/test
31 | :target: https://github.com/ninemoreminutes/django-crum/actions?query=workflow%3Atest
32 | .. |PyPI Version| image:: https://img.shields.io/pypi/v/django-crum.svg
33 | :target: https://pypi.python.org/pypi/django-crum/
34 | .. |PyPI License| image:: https://img.shields.io/pypi/l/django-crum.svg
35 | :target: https://pypi.python.org/pypi/django-crum/
36 | .. |Python Versions| image:: https://img.shields.io/pypi/pyversions/django-crum.svg
37 | :target: https://pypi.python.org/pypi/django-crum/
38 | .. |Django Versions| image:: https://img.shields.io/pypi/djversions/django-crum.svg
39 | :target: https://pypi.org/project/django-crum/
40 | .. |Read the Docs| image:: https://img.shields.io/readthedocs/django-crum.svg
41 | :target: http://django-crum.readthedocs.io/
42 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PYTHON_MAJOR_MINOR := $(shell python -c "import sys; print('{0}{1}'.format(*sys.version_info))")
2 | REQUIREMENTS_TXT = requirements/dev$(PYTHON_MAJOR_MINOR).txt
3 |
4 | .PHONY: core-requirements
5 | core-requirements:
6 | pip install pip setuptools pip-tools
7 |
8 | .PHONY: update-requirements
9 | update-requirements: core-requirements
10 | pip install -U pip setuptools pip-tools
11 | pip-compile -U requirements/dev.in -o $(REQUIREMENTS_TXT)
12 |
13 | .PHONY: requirements
14 | requirements: core-requirements
15 | pip-sync $(REQUIREMENTS_TXT)
16 |
17 | .PHONY: clean-pyc
18 | clean-pyc: requirements
19 | find . -iname "*.pyc" -delete
20 | find . -iname __pycache__ | xargs rm -rf
21 |
22 | .PHONY: tox-update-requirements
23 | tox-update-requirements: clean-pyc
24 | tox -c requirements/tox.ini
25 |
26 | .PHONY: develop
27 | develop: clean-pyc
28 | python setup.py develop
29 |
30 | .PHONY: check
31 | check: develop
32 | python manage.py check
33 |
34 | .PHONY: migrate
35 | migrate: check
36 | python manage.py migrate --noinput
37 |
38 | .PHONY: runserver
39 | runserver: migrate
40 | python manage.py runserver
41 |
42 | reports:
43 | mkdir -p $@
44 |
45 | .PHONY: pycodestyle
46 | pycodestyle: reports requirements
47 | set -o pipefail && $@ | tee reports/$@.report
48 |
49 | .PHONY: flake8
50 | flake8: reports requirements
51 | set -o pipefail && $@ | tee reports/$@.report
52 |
53 | .PHONY: check8
54 | check8: pycodestyle flake8
55 |
56 | .PHONY: test
57 | test: clean-pyc
58 | python setup.py test
59 |
60 | .PHONY: clean-tox
61 | clean-tox:
62 | rm -rf .tox
63 | rm -rf .coveragepy*
64 |
65 | .PHONY: tox
66 | tox: clean-pyc
67 | tox
68 |
69 | .PHONY: tox-parallel
70 | tox-parallel: clean-pyc
71 | tox -p auto
72 |
73 | .PHONY: clean-all
74 | clean-all: clean-pyc clean-tox
75 | rm -rf *.egg-info .eggs .cache .coverage build reports
76 |
77 | .PHONY: bump-major
78 | bump-major: requirements
79 | bumpversion major
80 |
81 | .PHONY: bump-minor
82 | bump-minor: requirements
83 | bumpversion minor
84 |
85 | .PHONY: bump-patch
86 | bump-patch: requirements
87 | bumpversion patch
88 |
89 | .PHONY: docs
90 | docs: requirements
91 | python setup.py build_sphinx
92 |
93 | .PHONY: ship-it
94 | ship-it: requirements clean-pyc
95 | python setup.py ship_it
96 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | commit = True
3 | current_version = 0.7.9
4 | tag = True
5 | tag_name = {new_version}
6 |
7 | [metadata]
8 | name = django-crum
9 | version = attr: crum.__version__
10 | author = Nine More Minutes, Inc.
11 | author_email = support@ninemoreminutes.com
12 | description = Django middleware to capture current request and user.
13 | long_description = file: README.rst
14 | long_description_content_type = text/x-rst
15 | keywords = django, request, user, middleware, thread, local
16 | license = BSD
17 | url = https://github.com/ninemoreminutes/django-crum/
18 | project_urls =
19 | Documentation = https://django-crum.rtfd.org/
20 | Source = https://github.com/ninemoreminutes/django-crum/
21 | Tracker = https://github.com/ninemoreminutes/django-crum/issues
22 | classifiers =
23 | Development Status :: 4 - Beta
24 | Environment :: Web Environment
25 | Framework :: Django
26 | Framework :: Django :: 1.11
27 | Framework :: Django :: 2.0
28 | Framework :: Django :: 2.1
29 | Framework :: Django :: 2.2
30 | Framework :: Django :: 3.0
31 | Framework :: Django :: 3.1
32 | Framework :: Django :: 3.2
33 | Intended Audience :: Developers
34 | License :: OSI Approved :: BSD License
35 | Operating System :: OS Independent
36 | Programming Language :: Python
37 | Programming Language :: Python :: 3
38 | Programming Language :: Python :: 3.4
39 | Programming Language :: Python :: 3.5
40 | Programming Language :: Python :: 3.6
41 | Programming Language :: Python :: 3.7
42 | Programming Language :: Python :: 3.8
43 | Programming Language :: Python :: 3.9
44 | Topic :: Internet :: WWW/HTTP
45 | Topic :: Software Development :: Libraries
46 | Topic :: Software Development :: Libraries :: Python Modules
47 |
48 | [options]
49 | zip_safe = False
50 | packages = crum
51 | include_package_data = True
52 | setup_requires =
53 | pytest-runner
54 | setuptools-twine
55 | tests_require =
56 | django>=1.8
57 | djangorestframework
58 | pytest
59 | pytest-cov
60 | pytest-django
61 | pytest-flake8
62 | install_requires =
63 | django>=1.8
64 |
65 | [check]
66 | metadata = True
67 | restructuredtext = True
68 | strict = True
69 |
70 | [clean]
71 | all = True
72 |
73 | [egg_info]
74 | tag_build = .dev
75 |
76 | [build_sphinx]
77 | source_dir = docs
78 | build_dir = docs/_build
79 | all_files = True
80 | version = attr: crum.__version__
81 | release = attr: crum.__version__
82 |
83 | [upload_sphinx]
84 | upload_dir = docs/_build/html
85 |
86 | [upload_docs]
87 | upload_dir = docs/_build/html
88 |
89 | [bdist_wheel]
90 | universal = 1
91 |
92 | [aliases]
93 | dev_build = clean test egg_info sdist bdist_wheel twine_check build_sphinx
94 | release_build = clean test egg_info -b "" sdist bdist_wheel twine_check build_sphinx
95 | test = pytest
96 | ship_it = release_build twine_upload
97 |
98 | [bumpversion:file:crum/__init__.py]
99 |
100 | [bumpversion:file:docs/conf.py]
101 |
102 | [pycodestyle]
103 | ignore = E501
104 | exclude = .git,.tox,build,dist,docs
105 |
106 | [flake8]
107 | ignore = E501
108 | exclude = .git,.tox,build,dist,docs
109 |
110 | [tool:pytest]
111 | DJANGO_SETTINGS_MODULE = test_project.settings
112 | python_files = test*.py
113 | testpaths = crum test_project
114 | norecursedirs = .git .tox build dist docs
115 | flake8-ignore = E501
116 | addopts = --reuse-db --nomigrations --cache-clear --flake8 --cov crum --cov-append --cov-report term-missing
117 |
--------------------------------------------------------------------------------
/crum/__init__.py:
--------------------------------------------------------------------------------
1 | # Python
2 | import contextlib
3 | import logging
4 | import threading
5 |
6 | _thread_locals = threading.local()
7 |
8 | _logger = logging.getLogger('crum')
9 |
10 | __version__ = '0.7.9'
11 |
12 | __all__ = ['get_current_request', 'get_current_user', 'impersonate']
13 |
14 |
15 | @contextlib.contextmanager
16 | def impersonate(user=None):
17 | """
18 | Temporarily impersonate the given user for audit trails.
19 | """
20 | try:
21 | current_user = get_current_user(_return_false=True)
22 | set_current_user(user)
23 | yield user
24 | finally:
25 | set_current_user(current_user)
26 |
27 |
28 | def get_current_request():
29 | """
30 | Return the request associated with the current thread.
31 | """
32 | return getattr(_thread_locals, 'request', None)
33 |
34 |
35 | def set_current_request(request=None):
36 | """
37 | Update the request associated with the current thread.
38 | """
39 | _thread_locals.request = request
40 | # Clear the current user if also clearing the request.
41 | if not request:
42 | set_current_user(False)
43 |
44 |
45 | def get_current_user(_return_false=False):
46 | """
47 | Return the user associated with the current request thread.
48 | """
49 | from crum.signals import current_user_getter
50 | top_priority = -9999
51 | top_user = False if _return_false else None
52 | results = current_user_getter.send_robust(get_current_user)
53 | for receiver, response in results:
54 | priority = 0
55 | if isinstance(response, Exception):
56 | _logger.exception('%r raised exception: %s', receiver, response)
57 | continue
58 | elif isinstance(response, (tuple, list)) and response:
59 | user = response[0]
60 | if len(response) > 1:
61 | priority = response[1]
62 | elif response or response in (None, False):
63 | user = response
64 | else:
65 | _logger.error('%r returned invalid response: %r', receiver,
66 | response)
67 | continue
68 | if user is not False:
69 | if priority > top_priority:
70 | top_priority = priority
71 | top_user = user
72 | return top_user
73 |
74 |
75 | def set_current_user(user=None):
76 | """
77 | Update the user associated with the current request thread.
78 | """
79 | from crum.signals import current_user_setter
80 | results = current_user_setter.send_robust(set_current_user, user=user)
81 | for receiver, response in results:
82 | if isinstance(response, Exception):
83 | _logger.exception('%r raised exception: %s', receiver, response)
84 |
85 |
86 | class CurrentRequestUserMiddleware(object):
87 | """
88 | Middleware to capture the request and user from the current thread.
89 | """
90 |
91 | def __init__(self, get_response=None):
92 | self.get_response = get_response
93 |
94 | def __call__(self, request): # pragma: no cover
95 | response = self.process_request(request)
96 | try:
97 | response = self.get_response(request)
98 | except Exception as e:
99 | self.process_exception(request, e)
100 | raise
101 | return self.process_response(request, response)
102 |
103 | def process_request(self, request):
104 | set_current_request(request)
105 |
106 | def process_response(self, request, response):
107 | set_current_request(None)
108 | return response
109 |
110 | def process_exception(self, request, exception):
111 | set_current_request(None)
112 |
--------------------------------------------------------------------------------
/test_project/settings.py:
--------------------------------------------------------------------------------
1 | # Python
2 | import os
3 |
4 |
5 | # Absolute path to the directory containing this Django project.
6 | BASE_DIR = os.path.abspath(os.path.dirname(__file__))
7 |
8 | DEBUG = True
9 | TEMPLATE_DEBUG = DEBUG
10 |
11 | DATABASES = {
12 | 'default': {
13 | 'ENGINE': 'django.db.backends.sqlite3',
14 | 'NAME': os.path.join(BASE_DIR, 'test_project.sqlite3'),
15 | }
16 | }
17 |
18 | TIME_ZONE = 'America/New_York'
19 |
20 | USE_TZ = True
21 |
22 | SITE_ID = 1
23 |
24 | SECRET_KEY = '347e28aa55547c3195e028fdead3448817e74fe2'
25 |
26 | STATICFILES_DIRS = ()
27 |
28 | STATIC_URL = '/static/'
29 |
30 | STATIC_ROOT = os.path.join(BASE_DIR, 'public', 'static')
31 |
32 | MEDIA_URL = '/media/'
33 |
34 | MEDIA_ROOT = os.path.join(BASE_DIR, 'public', 'media')
35 |
36 | MIDDLEWARE = [
37 | 'django.middleware.security.SecurityMiddleware',
38 | 'django.contrib.sessions.middleware.SessionMiddleware',
39 | 'django.middleware.common.CommonMiddleware',
40 | 'django.middleware.csrf.CsrfViewMiddleware',
41 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
42 | 'django.contrib.messages.middleware.MessageMiddleware',
43 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
44 | 'crum.CurrentRequestUserMiddleware',
45 | ]
46 |
47 | TEMPLATES = [
48 | {
49 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
50 | 'DIRS': [
51 | os.path.join(BASE_DIR, 'templates'),
52 | ],
53 | 'APP_DIRS': True,
54 | 'OPTIONS': {},
55 | },
56 | ]
57 |
58 | TEMPLATE_DIRS = (
59 | os.path.join(BASE_DIR, 'templates'),
60 | )
61 |
62 | ROOT_URLCONF = 'test_project.urls'
63 |
64 | INSTALLED_APPS = (
65 | 'django.contrib.auth',
66 | 'django.contrib.contenttypes',
67 | 'django.contrib.sessions',
68 | 'django.contrib.sites',
69 | 'django.contrib.messages',
70 | 'django.contrib.staticfiles',
71 | 'django.contrib.admin',
72 | 'rest_framework',
73 | 'test_project.test_app',
74 | )
75 |
76 | try:
77 | import django_extensions # noqa
78 | INSTALLED_APPS += ('django_extensions',)
79 | except ImportError:
80 | pass
81 |
82 | try:
83 | import debug_toolbar # noqa
84 | INSTALLED_APPS += ('debug_toolbar',)
85 | MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
86 | DEBUG_TOOLBAR_CONFIG = {
87 | 'INTERCEPT_REDIRECTS': False,
88 | }
89 | except ImportError:
90 | pass
91 |
92 | RUNSERVER_DEFAULT_ADDR = '127.0.0.1'
93 | RUNSERVER_DEFAULT_PORT = '8034'
94 |
95 | INTERNAL_IPS = ('127.0.0.1',)
96 |
97 | LOGGING = {
98 | 'version': 1,
99 | 'disable_existing_loggers': False,
100 | 'filters': {
101 | 'require_debug_false': {
102 | '()': 'django.utils.log.RequireDebugFalse',
103 | },
104 | 'require_debug_true': {
105 | '()': 'django.utils.log.RequireDebugTrue',
106 | },
107 | },
108 | 'handlers': {
109 | 'console': {
110 | 'level': 'INFO',
111 | 'filters': ['require_debug_true'],
112 | 'class': 'logging.StreamHandler',
113 | },
114 | 'null': {
115 | 'class': 'logging.NullHandler',
116 | },
117 | 'mail_admins': {
118 | 'level': 'ERROR',
119 | 'filters': ['require_debug_false'],
120 | 'class': 'django.utils.log.AdminEmailHandler'
121 | }
122 | },
123 | 'loggers': {
124 | 'django': {
125 | 'handlers': ['console'],
126 | },
127 | 'django.request': {
128 | 'handlers': ['mail_admins'],
129 | 'level': 'ERROR',
130 | 'propagate': False,
131 | },
132 | 'django.security': {
133 | 'handlers': ['mail_admins'],
134 | 'level': 'ERROR',
135 | 'propagate': False,
136 | },
137 | 'py.warnings': {
138 | 'handlers': ['console'],
139 | },
140 | 'crum': {
141 | 'handlers': ['null'],
142 | },
143 | },
144 | }
145 |
--------------------------------------------------------------------------------
/requirements/dev38.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile --output-file=requirements/dev38.txt requirements/dev.in
6 | #
7 | alabaster==0.7.12
8 | # via sphinx
9 | appdirs==1.4.4
10 | # via virtualenv
11 | appnope==0.1.2
12 | # via ipython
13 | asgiref==3.3.4
14 | # via django
15 | attrs==20.3.0
16 | # via pytest
17 | babel==2.9.0
18 | # via sphinx
19 | backcall==0.2.0
20 | # via ipython
21 | bleach==3.3.0
22 | # via readme-renderer
23 | bump2version==1.0.1
24 | # via bumpversion
25 | bumpversion==0.6.0
26 | # via -r requirements/dev.in
27 | certifi==2020.12.5
28 | # via requests
29 | chardet==4.0.0
30 | # via requests
31 | click==7.1.2
32 | # via pip-tools
33 | colorama==0.4.4
34 | # via twine
35 | coverage==5.5
36 | # via pytest-cov
37 | decorator==5.0.5
38 | # via ipython
39 | distlib==0.3.1
40 | # via virtualenv
41 | django-debug-toolbar==3.2
42 | # via -r requirements/dev.in
43 | django-extensions==3.1.2
44 | # via -r requirements/dev.in
45 | django==3.2
46 | # via
47 | # -r requirements/dev.in
48 | # django-debug-toolbar
49 | # django-extensions
50 | # djangorestframework
51 | djangorestframework==3.12.4
52 | # via -r requirements/dev.in
53 | docutils==0.17
54 | # via
55 | # readme-renderer
56 | # sphinx
57 | filelock==3.0.12
58 | # via
59 | # tox
60 | # virtualenv
61 | flake8==3.9.0
62 | # via
63 | # -r requirements/dev.in
64 | # pytest-flake8
65 | idna==2.10
66 | # via requests
67 | imagesize==1.2.0
68 | # via sphinx
69 | importlib-metadata==3.10.0
70 | # via
71 | # keyring
72 | # twine
73 | iniconfig==1.1.1
74 | # via pytest
75 | ipython-genutils==0.2.0
76 | # via traitlets
77 | ipython==7.22.0
78 | # via -r requirements/dev.in
79 | jedi==0.18.0
80 | # via ipython
81 | jinja2==2.11.3
82 | # via sphinx
83 | keyring==23.0.1
84 | # via twine
85 | markupsafe==1.1.1
86 | # via jinja2
87 | mccabe==0.6.1
88 | # via flake8
89 | packaging==20.9
90 | # via
91 | # bleach
92 | # pytest
93 | # sphinx
94 | # tox
95 | parso==0.8.2
96 | # via jedi
97 | pep517==0.10.0
98 | # via pip-tools
99 | pexpect==4.8.0
100 | # via ipython
101 | pickleshare==0.7.5
102 | # via ipython
103 | pip-tools==6.0.1
104 | # via -r requirements/dev.in
105 | pkginfo==1.7.0
106 | # via twine
107 | pluggy==0.13.1
108 | # via
109 | # pytest
110 | # tox
111 | prompt-toolkit==3.0.18
112 | # via ipython
113 | ptyprocess==0.7.0
114 | # via pexpect
115 | py==1.10.0
116 | # via
117 | # pytest
118 | # tox
119 | pycodestyle==2.7.0
120 | # via
121 | # -r requirements/dev.in
122 | # flake8
123 | pyflakes==2.3.1
124 | # via flake8
125 | pygments==2.8.1
126 | # via
127 | # ipython
128 | # readme-renderer
129 | # sphinx
130 | pyparsing==2.4.7
131 | # via packaging
132 | pytest-cov==2.11.1
133 | # via -r requirements/dev.in
134 | pytest-django==4.1.0
135 | # via -r requirements/dev.in
136 | pytest-flake8==1.0.7
137 | # via -r requirements/dev.in
138 | pytest-runner==5.3.0
139 | # via -r requirements/dev.in
140 | pytest==6.2.3
141 | # via
142 | # -r requirements/dev.in
143 | # pytest-cov
144 | # pytest-django
145 | # pytest-flake8
146 | pytz==2021.1
147 | # via
148 | # babel
149 | # django
150 | readme-renderer==29.0
151 | # via twine
152 | requests-toolbelt==0.9.1
153 | # via twine
154 | requests==2.25.1
155 | # via
156 | # requests-toolbelt
157 | # sphinx
158 | # twine
159 | rfc3986==1.4.0
160 | # via twine
161 | setuptools-twine==0.1.3
162 | # via -r requirements/dev.in
163 | six==1.15.0
164 | # via
165 | # bleach
166 | # readme-renderer
167 | # tox
168 | # virtualenv
169 | snowballstemmer==2.1.0
170 | # via sphinx
171 | sphinx==3.5.3
172 | # via -r requirements/dev.in
173 | sphinxcontrib-applehelp==1.0.2
174 | # via sphinx
175 | sphinxcontrib-devhelp==1.0.2
176 | # via sphinx
177 | sphinxcontrib-htmlhelp==1.0.3
178 | # via sphinx
179 | sphinxcontrib-jsmath==1.0.1
180 | # via sphinx
181 | sphinxcontrib-qthelp==1.0.3
182 | # via sphinx
183 | sphinxcontrib-serializinghtml==1.1.4
184 | # via sphinx
185 | sqlparse==0.4.1
186 | # via
187 | # django
188 | # django-debug-toolbar
189 | toml==0.10.2
190 | # via
191 | # pep517
192 | # pytest
193 | # tox
194 | tox-gh-actions==2.4.0
195 | # via -r requirements/dev.in
196 | tox==3.23.0
197 | # via
198 | # -r requirements/dev.in
199 | # tox-gh-actions
200 | tqdm==4.60.0
201 | # via twine
202 | traitlets==5.0.5
203 | # via ipython
204 | twine==3.4.1
205 | # via
206 | # -r requirements/dev.in
207 | # setuptools-twine
208 | urllib3==1.26.4
209 | # via requests
210 | virtualenv==20.4.3
211 | # via tox
212 | wcwidth==0.2.5
213 | # via prompt-toolkit
214 | webencodings==0.5.1
215 | # via bleach
216 | zipp==3.4.1
217 | # via importlib-metadata
218 |
219 | # The following packages are considered to be unsafe in a requirements file:
220 | # pip
221 | # setuptools
222 |
--------------------------------------------------------------------------------
/requirements/dev39.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile --output-file=requirements/dev39.txt requirements/dev.in
6 | #
7 | alabaster==0.7.12
8 | # via sphinx
9 | appdirs==1.4.4
10 | # via virtualenv
11 | appnope==0.1.2
12 | # via ipython
13 | asgiref==3.3.4
14 | # via django
15 | attrs==20.3.0
16 | # via pytest
17 | babel==2.9.0
18 | # via sphinx
19 | backcall==0.2.0
20 | # via ipython
21 | bleach==3.3.0
22 | # via readme-renderer
23 | bump2version==1.0.1
24 | # via bumpversion
25 | bumpversion==0.6.0
26 | # via -r requirements/dev.in
27 | certifi==2020.12.5
28 | # via requests
29 | chardet==4.0.0
30 | # via requests
31 | click==7.1.2
32 | # via pip-tools
33 | colorama==0.4.4
34 | # via twine
35 | coverage==5.5
36 | # via pytest-cov
37 | decorator==5.0.5
38 | # via ipython
39 | distlib==0.3.1
40 | # via virtualenv
41 | django-debug-toolbar==3.2
42 | # via -r requirements/dev.in
43 | django-extensions==3.1.2
44 | # via -r requirements/dev.in
45 | django==3.2
46 | # via
47 | # -r requirements/dev.in
48 | # django-debug-toolbar
49 | # django-extensions
50 | # djangorestframework
51 | djangorestframework==3.12.4
52 | # via -r requirements/dev.in
53 | docutils==0.17
54 | # via
55 | # readme-renderer
56 | # sphinx
57 | filelock==3.0.12
58 | # via
59 | # tox
60 | # virtualenv
61 | flake8==3.9.0
62 | # via
63 | # -r requirements/dev.in
64 | # pytest-flake8
65 | idna==2.10
66 | # via requests
67 | imagesize==1.2.0
68 | # via sphinx
69 | importlib-metadata==3.10.0
70 | # via
71 | # keyring
72 | # twine
73 | iniconfig==1.1.1
74 | # via pytest
75 | ipython-genutils==0.2.0
76 | # via traitlets
77 | ipython==7.22.0
78 | # via -r requirements/dev.in
79 | jedi==0.18.0
80 | # via ipython
81 | jinja2==2.11.3
82 | # via sphinx
83 | keyring==23.0.1
84 | # via twine
85 | markupsafe==1.1.1
86 | # via jinja2
87 | mccabe==0.6.1
88 | # via flake8
89 | packaging==20.9
90 | # via
91 | # bleach
92 | # pytest
93 | # sphinx
94 | # tox
95 | parso==0.8.2
96 | # via jedi
97 | pep517==0.10.0
98 | # via pip-tools
99 | pexpect==4.8.0
100 | # via ipython
101 | pickleshare==0.7.5
102 | # via ipython
103 | pip-tools==6.0.1
104 | # via -r requirements/dev.in
105 | pkginfo==1.7.0
106 | # via twine
107 | pluggy==0.13.1
108 | # via
109 | # pytest
110 | # tox
111 | prompt-toolkit==3.0.18
112 | # via ipython
113 | ptyprocess==0.7.0
114 | # via pexpect
115 | py==1.10.0
116 | # via
117 | # pytest
118 | # tox
119 | pycodestyle==2.7.0
120 | # via
121 | # -r requirements/dev.in
122 | # flake8
123 | pyflakes==2.3.1
124 | # via flake8
125 | pygments==2.8.1
126 | # via
127 | # ipython
128 | # readme-renderer
129 | # sphinx
130 | pyparsing==2.4.7
131 | # via packaging
132 | pytest-cov==2.11.1
133 | # via -r requirements/dev.in
134 | pytest-django==4.1.0
135 | # via -r requirements/dev.in
136 | pytest-flake8==1.0.7
137 | # via -r requirements/dev.in
138 | pytest-runner==5.3.0
139 | # via -r requirements/dev.in
140 | pytest==6.2.3
141 | # via
142 | # -r requirements/dev.in
143 | # pytest-cov
144 | # pytest-django
145 | # pytest-flake8
146 | pytz==2021.1
147 | # via
148 | # babel
149 | # django
150 | readme-renderer==29.0
151 | # via twine
152 | requests-toolbelt==0.9.1
153 | # via twine
154 | requests==2.25.1
155 | # via
156 | # requests-toolbelt
157 | # sphinx
158 | # twine
159 | rfc3986==1.4.0
160 | # via twine
161 | setuptools-twine==0.1.3
162 | # via -r requirements/dev.in
163 | six==1.15.0
164 | # via
165 | # bleach
166 | # readme-renderer
167 | # tox
168 | # virtualenv
169 | snowballstemmer==2.1.0
170 | # via sphinx
171 | sphinx==3.5.3
172 | # via -r requirements/dev.in
173 | sphinxcontrib-applehelp==1.0.2
174 | # via sphinx
175 | sphinxcontrib-devhelp==1.0.2
176 | # via sphinx
177 | sphinxcontrib-htmlhelp==1.0.3
178 | # via sphinx
179 | sphinxcontrib-jsmath==1.0.1
180 | # via sphinx
181 | sphinxcontrib-qthelp==1.0.3
182 | # via sphinx
183 | sphinxcontrib-serializinghtml==1.1.4
184 | # via sphinx
185 | sqlparse==0.4.1
186 | # via
187 | # django
188 | # django-debug-toolbar
189 | toml==0.10.2
190 | # via
191 | # pep517
192 | # pytest
193 | # tox
194 | tox-gh-actions==2.4.0
195 | # via -r requirements/dev.in
196 | tox==3.23.0
197 | # via
198 | # -r requirements/dev.in
199 | # tox-gh-actions
200 | tqdm==4.60.0
201 | # via twine
202 | traitlets==5.0.5
203 | # via ipython
204 | twine==3.4.1
205 | # via
206 | # -r requirements/dev.in
207 | # setuptools-twine
208 | urllib3==1.26.4
209 | # via requests
210 | virtualenv==20.4.3
211 | # via tox
212 | wcwidth==0.2.5
213 | # via prompt-toolkit
214 | webencodings==0.5.1
215 | # via bleach
216 | zipp==3.4.1
217 | # via importlib-metadata
218 |
219 | # The following packages are considered to be unsafe in a requirements file:
220 | # pip
221 | # setuptools
222 |
--------------------------------------------------------------------------------
/requirements/dev35.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile --output-file=requirements/dev35.txt requirements/dev.in
6 | #
7 | alabaster==0.7.12
8 | # via sphinx
9 | appdirs==1.4.4
10 | # via virtualenv
11 | appnope==0.1.2
12 | # via ipython
13 | attrs==20.3.0
14 | # via pytest
15 | babel==2.9.0
16 | # via sphinx
17 | backcall==0.2.0
18 | # via ipython
19 | bleach==3.3.0
20 | # via readme-renderer
21 | bump2version==1.0.1
22 | # via bumpversion
23 | bumpversion==0.6.0
24 | # via -r requirements/dev.in
25 | certifi==2020.12.5
26 | # via requests
27 | chardet==4.0.0
28 | # via requests
29 | click==7.1.2
30 | # via pip-tools
31 | coverage==5.5
32 | # via pytest-cov
33 | decorator==5.0.5
34 | # via
35 | # ipython
36 | # traitlets
37 | distlib==0.3.1
38 | # via virtualenv
39 | django-debug-toolbar==3.1.1
40 | # via -r requirements/dev.in
41 | django-extensions==3.1.0
42 | # via -r requirements/dev.in
43 | django==2.2.20
44 | # via
45 | # -r requirements/dev.in
46 | # django-debug-toolbar
47 | # djangorestframework
48 | djangorestframework==3.12.4
49 | # via -r requirements/dev.in
50 | docutils==0.17
51 | # via
52 | # readme-renderer
53 | # sphinx
54 | filelock==3.0.12
55 | # via
56 | # tox
57 | # virtualenv
58 | flake8==3.9.0
59 | # via
60 | # -r requirements/dev.in
61 | # pytest-flake8
62 | idna==2.10
63 | # via requests
64 | imagesize==1.2.0
65 | # via sphinx
66 | importlib-metadata==2.1.1
67 | # via
68 | # flake8
69 | # pluggy
70 | # pytest
71 | # tox
72 | # virtualenv
73 | importlib-resources==3.2.1
74 | # via virtualenv
75 | iniconfig==1.1.1
76 | # via pytest
77 | ipython-genutils==0.2.0
78 | # via traitlets
79 | ipython==7.9.0
80 | # via -r requirements/dev.in
81 | jedi==0.17.2
82 | # via ipython
83 | jinja2==2.11.3
84 | # via sphinx
85 | markupsafe==1.1.1
86 | # via jinja2
87 | mccabe==0.6.1
88 | # via flake8
89 | packaging==20.9
90 | # via
91 | # bleach
92 | # pytest
93 | # sphinx
94 | # tox
95 | parso==0.7.1
96 | # via jedi
97 | pathlib2==2.3.5
98 | # via pytest
99 | pexpect==4.8.0
100 | # via ipython
101 | pickleshare==0.7.5
102 | # via ipython
103 | pip-tools==5.5.0
104 | # via -r requirements/dev.in
105 | pkginfo==1.7.0
106 | # via twine
107 | pluggy==0.13.1
108 | # via
109 | # pytest
110 | # tox
111 | prompt-toolkit==2.0.10
112 | # via ipython
113 | ptyprocess==0.7.0
114 | # via pexpect
115 | py==1.10.0
116 | # via
117 | # pytest
118 | # tox
119 | pycodestyle==2.7.0
120 | # via
121 | # -r requirements/dev.in
122 | # flake8
123 | pyflakes==2.3.1
124 | # via flake8
125 | pygments==2.8.1
126 | # via
127 | # ipython
128 | # readme-renderer
129 | # sphinx
130 | pyparsing==2.4.7
131 | # via packaging
132 | pytest-cov==2.11.1
133 | # via -r requirements/dev.in
134 | pytest-django==4.1.0
135 | # via -r requirements/dev.in
136 | pytest-flake8==1.0.7
137 | # via -r requirements/dev.in
138 | pytest-runner==5.2
139 | # via -r requirements/dev.in
140 | pytest==6.1.2
141 | # via
142 | # -r requirements/dev.in
143 | # pytest-cov
144 | # pytest-django
145 | # pytest-flake8
146 | pytz==2021.1
147 | # via
148 | # babel
149 | # django
150 | readme-renderer==29.0
151 | # via twine
152 | requests-toolbelt==0.9.1
153 | # via twine
154 | requests==2.25.1
155 | # via
156 | # requests-toolbelt
157 | # sphinx
158 | # twine
159 | setuptools-twine==0.1.3
160 | # via -r requirements/dev.in
161 | six==1.15.0
162 | # via
163 | # bleach
164 | # pathlib2
165 | # prompt-toolkit
166 | # readme-renderer
167 | # tox
168 | # traitlets
169 | # virtualenv
170 | snowballstemmer==2.1.0
171 | # via sphinx
172 | sphinx==3.5.3
173 | # via -r requirements/dev.in
174 | sphinxcontrib-applehelp==1.0.2
175 | # via sphinx
176 | sphinxcontrib-devhelp==1.0.2
177 | # via sphinx
178 | sphinxcontrib-htmlhelp==1.0.3
179 | # via sphinx
180 | sphinxcontrib-jsmath==1.0.1
181 | # via sphinx
182 | sphinxcontrib-qthelp==1.0.3
183 | # via sphinx
184 | sphinxcontrib-serializinghtml==1.1.4
185 | # via sphinx
186 | sqlparse==0.4.1
187 | # via
188 | # django
189 | # django-debug-toolbar
190 | toml==0.10.2
191 | # via
192 | # pytest
193 | # tox
194 | tox-gh-actions==2.4.0
195 | # via -r requirements/dev.in
196 | tox==3.23.0
197 | # via
198 | # -r requirements/dev.in
199 | # tox-gh-actions
200 | tqdm==4.60.0
201 | # via twine
202 | traitlets==4.3.3
203 | # via ipython
204 | twine==1.15.0
205 | # via
206 | # -r requirements/dev.in
207 | # setuptools-twine
208 | urllib3==1.26.4
209 | # via requests
210 | virtualenv==20.4.3
211 | # via tox
212 | wcwidth==0.2.5
213 | # via prompt-toolkit
214 | webencodings==0.5.1
215 | # via bleach
216 | zipp==1.2.0
217 | # via
218 | # importlib-metadata
219 | # importlib-resources
220 |
221 | # The following packages are considered to be unsafe in a requirements file:
222 | # pip
223 | # setuptools
224 |
--------------------------------------------------------------------------------
/requirements/dev37.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile --output-file=requirements/dev37.txt requirements/dev.in
6 | #
7 | alabaster==0.7.12
8 | # via sphinx
9 | appdirs==1.4.4
10 | # via virtualenv
11 | appnope==0.1.2
12 | # via ipython
13 | asgiref==3.3.4
14 | # via django
15 | attrs==20.3.0
16 | # via pytest
17 | babel==2.9.0
18 | # via sphinx
19 | backcall==0.2.0
20 | # via ipython
21 | bleach==3.3.0
22 | # via readme-renderer
23 | bump2version==1.0.1
24 | # via bumpversion
25 | bumpversion==0.6.0
26 | # via -r requirements/dev.in
27 | certifi==2020.12.5
28 | # via requests
29 | chardet==4.0.0
30 | # via requests
31 | click==7.1.2
32 | # via pip-tools
33 | colorama==0.4.4
34 | # via twine
35 | coverage==5.5
36 | # via pytest-cov
37 | decorator==5.0.5
38 | # via ipython
39 | distlib==0.3.1
40 | # via virtualenv
41 | django-debug-toolbar==3.2
42 | # via -r requirements/dev.in
43 | django-extensions==3.1.2
44 | # via -r requirements/dev.in
45 | django==3.2
46 | # via
47 | # -r requirements/dev.in
48 | # django-debug-toolbar
49 | # django-extensions
50 | # djangorestframework
51 | djangorestframework==3.12.4
52 | # via -r requirements/dev.in
53 | docutils==0.17
54 | # via
55 | # readme-renderer
56 | # sphinx
57 | filelock==3.0.12
58 | # via
59 | # tox
60 | # virtualenv
61 | flake8==3.9.0
62 | # via
63 | # -r requirements/dev.in
64 | # pytest-flake8
65 | idna==2.10
66 | # via requests
67 | imagesize==1.2.0
68 | # via sphinx
69 | importlib-metadata==3.10.0
70 | # via
71 | # flake8
72 | # keyring
73 | # pep517
74 | # pluggy
75 | # pytest
76 | # tox
77 | # twine
78 | # virtualenv
79 | iniconfig==1.1.1
80 | # via pytest
81 | ipython-genutils==0.2.0
82 | # via traitlets
83 | ipython==7.22.0
84 | # via -r requirements/dev.in
85 | jedi==0.18.0
86 | # via ipython
87 | jinja2==2.11.3
88 | # via sphinx
89 | keyring==23.0.1
90 | # via twine
91 | markupsafe==1.1.1
92 | # via jinja2
93 | mccabe==0.6.1
94 | # via flake8
95 | packaging==20.9
96 | # via
97 | # bleach
98 | # pytest
99 | # sphinx
100 | # tox
101 | parso==0.8.2
102 | # via jedi
103 | pep517==0.10.0
104 | # via pip-tools
105 | pexpect==4.8.0
106 | # via ipython
107 | pickleshare==0.7.5
108 | # via ipython
109 | pip-tools==6.0.1
110 | # via -r requirements/dev.in
111 | pkginfo==1.7.0
112 | # via twine
113 | pluggy==0.13.1
114 | # via
115 | # pytest
116 | # tox
117 | prompt-toolkit==3.0.18
118 | # via ipython
119 | ptyprocess==0.7.0
120 | # via pexpect
121 | py==1.10.0
122 | # via
123 | # pytest
124 | # tox
125 | pycodestyle==2.7.0
126 | # via
127 | # -r requirements/dev.in
128 | # flake8
129 | pyflakes==2.3.1
130 | # via flake8
131 | pygments==2.8.1
132 | # via
133 | # ipython
134 | # readme-renderer
135 | # sphinx
136 | pyparsing==2.4.7
137 | # via packaging
138 | pytest-cov==2.11.1
139 | # via -r requirements/dev.in
140 | pytest-django==4.1.0
141 | # via -r requirements/dev.in
142 | pytest-flake8==1.0.7
143 | # via -r requirements/dev.in
144 | pytest-runner==5.3.0
145 | # via -r requirements/dev.in
146 | pytest==6.2.3
147 | # via
148 | # -r requirements/dev.in
149 | # pytest-cov
150 | # pytest-django
151 | # pytest-flake8
152 | pytz==2021.1
153 | # via
154 | # babel
155 | # django
156 | readme-renderer==29.0
157 | # via twine
158 | requests-toolbelt==0.9.1
159 | # via twine
160 | requests==2.25.1
161 | # via
162 | # requests-toolbelt
163 | # sphinx
164 | # twine
165 | rfc3986==1.4.0
166 | # via twine
167 | setuptools-twine==0.1.3
168 | # via -r requirements/dev.in
169 | six==1.15.0
170 | # via
171 | # bleach
172 | # readme-renderer
173 | # tox
174 | # virtualenv
175 | snowballstemmer==2.1.0
176 | # via sphinx
177 | sphinx==3.5.3
178 | # via -r requirements/dev.in
179 | sphinxcontrib-applehelp==1.0.2
180 | # via sphinx
181 | sphinxcontrib-devhelp==1.0.2
182 | # via sphinx
183 | sphinxcontrib-htmlhelp==1.0.3
184 | # via sphinx
185 | sphinxcontrib-jsmath==1.0.1
186 | # via sphinx
187 | sphinxcontrib-qthelp==1.0.3
188 | # via sphinx
189 | sphinxcontrib-serializinghtml==1.1.4
190 | # via sphinx
191 | sqlparse==0.4.1
192 | # via
193 | # django
194 | # django-debug-toolbar
195 | toml==0.10.2
196 | # via
197 | # pep517
198 | # pytest
199 | # tox
200 | tox-gh-actions==2.4.0
201 | # via -r requirements/dev.in
202 | tox==3.23.0
203 | # via
204 | # -r requirements/dev.in
205 | # tox-gh-actions
206 | tqdm==4.60.0
207 | # via twine
208 | traitlets==5.0.5
209 | # via ipython
210 | twine==3.4.1
211 | # via
212 | # -r requirements/dev.in
213 | # setuptools-twine
214 | typing-extensions==3.7.4.3
215 | # via
216 | # asgiref
217 | # importlib-metadata
218 | urllib3==1.26.4
219 | # via requests
220 | virtualenv==20.4.3
221 | # via tox
222 | wcwidth==0.2.5
223 | # via prompt-toolkit
224 | webencodings==0.5.1
225 | # via bleach
226 | zipp==3.4.1
227 | # via
228 | # importlib-metadata
229 | # pep517
230 |
231 | # The following packages are considered to be unsafe in a requirements file:
232 | # pip
233 | # setuptools
234 |
--------------------------------------------------------------------------------
/test_project/test_app/tests.py:
--------------------------------------------------------------------------------
1 | # Python
2 | from __future__ import with_statement
3 | from __future__ import unicode_literals
4 | import base64
5 | try:
6 | from importlib import reload as reload_module
7 | except ImportError:
8 | from imp import reload as reload_module
9 | import json
10 |
11 | # Django
12 | from django.test import TestCase
13 | from django.test.client import Client
14 | try:
15 | from django.urls import reverse
16 | except ImportError:
17 | from django.core.urlresolvers import reverse
18 | from django.contrib.auth.models import User
19 | try:
20 | from django.utils.six import binary_type, text_type
21 | except ImportError:
22 | binary_type, text_type = bytes, str
23 |
24 | # Django-CRUM
25 | from crum import get_current_user, impersonate
26 |
27 |
28 | class TestCRUM(TestCase):
29 | """Test cases for the CRUM app."""
30 |
31 | def setUp(self):
32 | super(TestCRUM, self).setUp()
33 | self.user_password = User.objects.make_random_password()
34 | self.user = User.objects.create_user('user', 'user@example.com',
35 | self.user_password)
36 |
37 | def test_middleware(self):
38 | # For test coverage.
39 | import crum
40 | reload_module(crum)
41 | # Test anonymous user.
42 | self.assertEqual(get_current_user(), None)
43 | url = reverse('test_app:index')
44 | response = self.client.get(url)
45 | response_content = response.content.decode('utf-8')
46 | self.assertEqual(response.status_code, 200)
47 | self.assertEqual(response_content, 'AnonymousUser')
48 | self.assertEqual(get_current_user(), None)
49 | # Test logged in user.
50 | self.client.login(username=self.user.username,
51 | password=self.user_password)
52 | response = self.client.get(url)
53 | response_content = response.content.decode('utf-8')
54 | self.assertEqual(response.status_code, 200)
55 | self.assertEqual(response_content, text_type(self.user))
56 | self.assertEqual(get_current_user(), None)
57 | # Test impersonate context manager.
58 | with impersonate(self.user):
59 | self.assertEqual(get_current_user(), self.user)
60 | self.assertEqual(get_current_user(), None)
61 | # Test impersonate(None) within view requested by logged in user.
62 | self.client.login(username=self.user.username,
63 | password=self.user_password)
64 | response = self.client.get(url + '?impersonate=1')
65 | response_content = response.content.decode('utf-8')
66 | self.assertEqual(response.status_code, 200)
67 | self.assertEqual(response_content, text_type(None))
68 | self.assertEqual(get_current_user(), None)
69 | # Test when request raises exception.
70 | try:
71 | response = self.client.get(url + '?raise=1')
72 | except RuntimeError:
73 | response = None
74 | self.assertEqual(response, None)
75 | self.assertEqual(get_current_user(), None)
76 |
77 | def test_middleware_with_rest_framework(self):
78 | # Test anonymous user.
79 | self.assertEqual(get_current_user(), None)
80 | url = reverse('test_app:api_index')
81 | response = self.client.get(url)
82 | response_content = json.loads(response.content.decode('utf-8'))
83 | self.assertEqual(response.status_code, 200)
84 | self.assertEqual(response_content, text_type('AnonymousUser'))
85 | self.assertEqual(get_current_user(), None)
86 | # Test logged in user (session auth).
87 | self.client.login(username=self.user.username,
88 | password=self.user_password)
89 | response = self.client.get(url)
90 | response_content = json.loads(response.content.decode('utf-8'))
91 | self.assertEqual(response.status_code, 200)
92 | self.assertEqual(response_content, text_type(self.user))
93 | self.assertEqual(get_current_user(), None)
94 | # Test logged in user (basic auth).
95 | basic_auth = '{0}:{1}'.format(self.user.username, self.user_password)
96 | basic_auth = binary_type(basic_auth.encode('utf-8'))
97 | basic_auth = base64.b64encode(basic_auth).decode('ascii')
98 | client_kwargs = {'HTTP_AUTHORIZATION': 'Basic %s' % basic_auth}
99 | client = Client(**client_kwargs)
100 | response = client.get(url)
101 | response_content = json.loads(response.content.decode('utf-8'))
102 | self.assertEqual(response.status_code, 200)
103 | self.assertEqual(response_content, text_type(self.user))
104 | self.assertEqual(get_current_user(), None)
105 | # Test impersonate(None) within view requested by logged in user.
106 | self.client.login(username=self.user.username,
107 | password=self.user_password)
108 | response = self.client.get(url + '?impersonate=1')
109 | response_content = json.loads(response.content.decode('utf-8'))
110 | self.assertEqual(response.status_code, 200)
111 | self.assertEqual(response_content, text_type(None))
112 | self.assertEqual(get_current_user(), None)
113 | # Test when request raises exception.
114 | try:
115 | response = self.client.get(url + '?raise=1')
116 | except RuntimeError:
117 | response = None
118 | self.assertEqual(response, None)
119 | self.assertEqual(get_current_user(), None)
120 |
--------------------------------------------------------------------------------
/requirements/dev36.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile --output-file=requirements/dev36.txt requirements/dev.in
6 | #
7 | alabaster==0.7.12
8 | # via sphinx
9 | appdirs==1.4.4
10 | # via virtualenv
11 | appnope==0.1.2
12 | # via ipython
13 | asgiref==3.3.4
14 | # via django
15 | attrs==20.3.0
16 | # via pytest
17 | babel==2.9.0
18 | # via sphinx
19 | backcall==0.2.0
20 | # via ipython
21 | bleach==3.3.0
22 | # via readme-renderer
23 | bump2version==1.0.1
24 | # via bumpversion
25 | bumpversion==0.6.0
26 | # via -r requirements/dev.in
27 | certifi==2020.12.5
28 | # via requests
29 | chardet==4.0.0
30 | # via requests
31 | click==7.1.2
32 | # via pip-tools
33 | colorama==0.4.4
34 | # via twine
35 | coverage==5.5
36 | # via pytest-cov
37 | decorator==5.0.5
38 | # via
39 | # ipython
40 | # traitlets
41 | distlib==0.3.1
42 | # via virtualenv
43 | django-debug-toolbar==3.2
44 | # via -r requirements/dev.in
45 | django-extensions==3.1.2
46 | # via -r requirements/dev.in
47 | django==3.2
48 | # via
49 | # -r requirements/dev.in
50 | # django-debug-toolbar
51 | # django-extensions
52 | # djangorestframework
53 | djangorestframework==3.12.4
54 | # via -r requirements/dev.in
55 | docutils==0.17
56 | # via
57 | # readme-renderer
58 | # sphinx
59 | filelock==3.0.12
60 | # via
61 | # tox
62 | # virtualenv
63 | flake8==3.9.0
64 | # via
65 | # -r requirements/dev.in
66 | # pytest-flake8
67 | idna==2.10
68 | # via requests
69 | imagesize==1.2.0
70 | # via sphinx
71 | importlib-metadata==3.10.0
72 | # via
73 | # flake8
74 | # keyring
75 | # pep517
76 | # pluggy
77 | # pytest
78 | # tox
79 | # twine
80 | # virtualenv
81 | importlib-resources==5.1.2
82 | # via virtualenv
83 | iniconfig==1.1.1
84 | # via pytest
85 | ipython-genutils==0.2.0
86 | # via traitlets
87 | ipython==7.16.1
88 | # via -r requirements/dev.in
89 | jedi==0.18.0
90 | # via ipython
91 | jinja2==2.11.3
92 | # via sphinx
93 | keyring==23.0.1
94 | # via twine
95 | markupsafe==1.1.1
96 | # via jinja2
97 | mccabe==0.6.1
98 | # via flake8
99 | packaging==20.9
100 | # via
101 | # bleach
102 | # pytest
103 | # sphinx
104 | # tox
105 | parso==0.8.2
106 | # via jedi
107 | pep517==0.10.0
108 | # via pip-tools
109 | pexpect==4.8.0
110 | # via ipython
111 | pickleshare==0.7.5
112 | # via ipython
113 | pip-tools==6.0.1
114 | # via -r requirements/dev.in
115 | pkginfo==1.7.0
116 | # via twine
117 | pluggy==0.13.1
118 | # via
119 | # pytest
120 | # tox
121 | prompt-toolkit==3.0.18
122 | # via ipython
123 | ptyprocess==0.7.0
124 | # via pexpect
125 | py==1.10.0
126 | # via
127 | # pytest
128 | # tox
129 | pycodestyle==2.7.0
130 | # via
131 | # -r requirements/dev.in
132 | # flake8
133 | pyflakes==2.3.1
134 | # via flake8
135 | pygments==2.8.1
136 | # via
137 | # ipython
138 | # readme-renderer
139 | # sphinx
140 | pyparsing==2.4.7
141 | # via packaging
142 | pytest-cov==2.11.1
143 | # via -r requirements/dev.in
144 | pytest-django==4.1.0
145 | # via -r requirements/dev.in
146 | pytest-flake8==1.0.7
147 | # via -r requirements/dev.in
148 | pytest-runner==5.3.0
149 | # via -r requirements/dev.in
150 | pytest==6.2.3
151 | # via
152 | # -r requirements/dev.in
153 | # pytest-cov
154 | # pytest-django
155 | # pytest-flake8
156 | pytz==2021.1
157 | # via
158 | # babel
159 | # django
160 | readme-renderer==29.0
161 | # via twine
162 | requests-toolbelt==0.9.1
163 | # via twine
164 | requests==2.25.1
165 | # via
166 | # requests-toolbelt
167 | # sphinx
168 | # twine
169 | rfc3986==1.4.0
170 | # via twine
171 | setuptools-twine==0.1.3
172 | # via -r requirements/dev.in
173 | six==1.15.0
174 | # via
175 | # bleach
176 | # readme-renderer
177 | # tox
178 | # traitlets
179 | # virtualenv
180 | snowballstemmer==2.1.0
181 | # via sphinx
182 | sphinx==3.5.3
183 | # via -r requirements/dev.in
184 | sphinxcontrib-applehelp==1.0.2
185 | # via sphinx
186 | sphinxcontrib-devhelp==1.0.2
187 | # via sphinx
188 | sphinxcontrib-htmlhelp==1.0.3
189 | # via sphinx
190 | sphinxcontrib-jsmath==1.0.1
191 | # via sphinx
192 | sphinxcontrib-qthelp==1.0.3
193 | # via sphinx
194 | sphinxcontrib-serializinghtml==1.1.4
195 | # via sphinx
196 | sqlparse==0.4.1
197 | # via
198 | # django
199 | # django-debug-toolbar
200 | toml==0.10.2
201 | # via
202 | # pep517
203 | # pytest
204 | # tox
205 | tox-gh-actions==2.4.0
206 | # via -r requirements/dev.in
207 | tox==3.23.0
208 | # via
209 | # -r requirements/dev.in
210 | # tox-gh-actions
211 | tqdm==4.60.0
212 | # via twine
213 | traitlets==4.3.3
214 | # via ipython
215 | twine==3.4.1
216 | # via
217 | # -r requirements/dev.in
218 | # setuptools-twine
219 | typing-extensions==3.7.4.3
220 | # via
221 | # asgiref
222 | # importlib-metadata
223 | urllib3==1.26.4
224 | # via requests
225 | virtualenv==20.4.3
226 | # via tox
227 | wcwidth==0.2.5
228 | # via prompt-toolkit
229 | webencodings==0.5.1
230 | # via bleach
231 | zipp==3.4.1
232 | # via
233 | # importlib-metadata
234 | # importlib-resources
235 | # pep517
236 |
237 | # The following packages are considered to be unsafe in a requirements file:
238 | # pip
239 | # setuptools
240 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. Django-CRUM documentation master file, created by
2 | sphinx-quickstart on Sat Jul 6 00:44:15 2013.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Django-CRUM
7 | ===========
8 |
9 | **Django-CRUM (Current Request User Middleware)** captures the current request
10 | and user in thread local storage.
11 |
12 | It enables apps to check permissions, capture audit trails or otherwise access
13 | the current request and user without requiring the request object to be passed
14 | directly. It also offers a context manager to allow for temporarily
15 | impersonating another user.
16 |
17 | It provides a signal to extend the built-in function for getting the current
18 | user, which could be helpful when using custom authentication methods or user
19 | models.
20 |
21 | It is tested against:
22 | * Django 1.11 (Python 3.5 and 3.6)
23 | * Django 2.0 (Python 3.5, 3.6 and 3.7)
24 | * Django 2.1 (Python 3.5, 3.6 and 3.7)
25 | * Django 2.2 (Python 3.5, 3.6, 3.7, 3.8 and 3.9)
26 | * Django 3.0 (Python 3.6, 3.7, 3.8 and 3.9)
27 | * Django 3.1 (Python 3.6, 3.7, 3.8 and 3.9)
28 | * Django 3.2 pre-release (Python 3.6, 3.7, 3.8 and 3.9)
29 | * Django main/4.0 (Python 3.8 and 3.9)
30 |
31 | Installation
32 | ------------
33 |
34 | Install the application from PYPI::
35 |
36 | pip install django-crum
37 |
38 | Add ``CurrentRequestUserMiddleware`` to your
39 | ``MIDDLEWARE`` setting::
40 |
41 | MIDDLEWARE += ('crum.CurrentRequestUserMiddleware',)
42 |
43 | *That's it!*
44 |
45 | Usage
46 | -----
47 |
48 | The `crum` package exports three functions as its public API.
49 |
50 | get_current_request()
51 | ~~~~~~~~~~~~~~~~~~~~~
52 |
53 | ``get_current_request`` returns the current request instance, or ``None`` if
54 | called outside the scope of a request.
55 |
56 | For example, the ``Comment`` model below overrides its ``save`` method to track
57 | the IP address of each commenter::
58 |
59 | from django.db import models
60 | from crum import get_current_request
61 |
62 | class Comment(models.Model):
63 | created = models.DateTimeField(auto_now_add=True)
64 | comment = models.TextField()
65 | remote_addr = models.CharField(blank=True, default='')
66 |
67 | def save(self, *args, **kwargs):
68 | request = get_current_request()
69 | if request and not self.remote_addr:
70 | self.remote_addr = request.META['REMOTE_ADDR']
71 | super(Comment, self).save(*args, **kwargs)
72 |
73 | get_current_user()
74 | ~~~~~~~~~~~~~~~~~~
75 |
76 | ``get_current_user`` returns the user associated with the current request, or
77 | ``None`` if no user is available.
78 |
79 | If using the built-in ``User`` model from ``django.contrib.auth``, the returned
80 | value may be the special ``AnonymousUser``, which won't have a primary key.
81 |
82 | For example, the ``Thing`` model below records the user who created it as well
83 | as the last user who modified it::
84 |
85 | from django.db import models
86 | from crum import get_current_user
87 |
88 | class Thing(models.Model):
89 | created = models.DateTimeField(auto_now_add=True)
90 | created_by = models.ForeignKey('auth.User', blank=True, null=True,
91 | default=None)
92 | modified = models.DateTimeField(auto_now=True)
93 | modified_by = models.ForeignKey('auth.User', blank=True, null=True,
94 | default=None)
95 |
96 | def save(self, *args, **kwargs):
97 | user = get_current_user()
98 | if user and not user.pk:
99 | user = None
100 | if not self.pk:
101 | self.created_by = user
102 | self.modified_by = user
103 | super(Thing, self).save(*args, **kwargs)
104 |
105 | impersonate(user=None)
106 | ~~~~~~~~~~~~~~~~~~~~~~
107 |
108 | ``impersonate`` is a context manager used to temporarily change the current
109 | user as returned by ``get_current_user``. It is typically used to perform an
110 | action on behalf of a user or disable the default behavior of
111 | ``get_current_user``.
112 |
113 | For example, a background task may need to create or update ``Thing`` objects
114 | when there is no active request or user (such as from a management command)::
115 |
116 | from crum import impersonate
117 |
118 | def create_thing_for_user(user):
119 | with impersonate(user):
120 | # This Thing will indicated it was created by the given user.
121 | user_thing = Thing.objects.create()
122 | # But this Thing won't have a created_by user.
123 | other_thing = Thing.objects.create()
124 |
125 | When running from within a view, ``impersonate`` may be used to prevent certain
126 | actions from being attributed to the requesting user::
127 |
128 | from django.template.response import TemplateResponse
129 | from crum import impersonate
130 |
131 | def get_my_things(request):
132 | # Whenever this view is accessed, trigger some cleanup of Things.
133 | with impersonate(None):
134 | Thing.objects.cleanup()
135 | my_things = Thing.objects.filter(created_by=request.user)
136 | return TemplateResponse(request, 'my_things.html',
137 | {'things': my_things})
138 |
139 | Signals
140 | -------
141 |
142 | (New in 0.6.0) The `crum` package provides a signal to extend the capabilities
143 | of the `get_current_user()` function.
144 |
145 | current_user_getter
146 | ~~~~~~~~~~~~~~~~~~~
147 |
148 | The ``current_user_getter`` signal is dispatched for each call to
149 | ``get_current_user()``. Receivers for this signal should return a tuple of
150 | ``(user, priority)``. Receivers should return ``None`` for the user when there
151 | is no current user set, or ``False`` when they can not determine the current
152 | user.
153 |
154 | The priority value which will be used to determine which response contains the
155 | current user. The response with the highest priority will be used as long as
156 | the user returned is not ``False``, otherwise lower-priority responses will
157 | be used in order of next-highest priority. Built-in receivers for this signal
158 | use priorities of -10 (current request) and +10 (thread locals); any custom
159 | receivers should usually use -10 < priority < 10.
160 |
161 | The following example demonstrates how a custom receiver could be implemented
162 | to determine the current user from an auth token passed via an HTTP header::
163 |
164 | from django.dispatch import receiver
165 | from crum import get_current_request
166 | from crum.signals import current_user_getter
167 |
168 | @receiver(current_user_getter)
169 | def (sender, **kwargs):
170 | request = get_current_request()
171 | if request:
172 | token = request.META.get('HTTP_AUTH_TOKEN', None)
173 | try:
174 | auth_token = AuthToken.objects.get(token=token)
175 | return (auth_token.user, 0)
176 | except AuthToken.DoesNotExist:
177 | return (None, 0)
178 | return (False, 0)
179 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Django-CRUM.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Django-CRUM.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Django-CRUM"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Django-CRUM"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | goto end
41 | )
42 |
43 | if "%1" == "clean" (
44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
45 | del /q /s %BUILDDIR%\*
46 | goto end
47 | )
48 |
49 |
50 | %SPHINXBUILD% 2> nul
51 | if errorlevel 9009 (
52 | echo.
53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
54 | echo.installed, then set the SPHINXBUILD environment variable to point
55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
56 | echo.may add the Sphinx directory to PATH.
57 | echo.
58 | echo.If you don't have Sphinx installed, grab it from
59 | echo.http://sphinx-doc.org/
60 | exit /b 1
61 | )
62 |
63 | if "%1" == "html" (
64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
68 | goto end
69 | )
70 |
71 | if "%1" == "dirhtml" (
72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
76 | goto end
77 | )
78 |
79 | if "%1" == "singlehtml" (
80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
84 | goto end
85 | )
86 |
87 | if "%1" == "pickle" (
88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can process the pickle files.
92 | goto end
93 | )
94 |
95 | if "%1" == "json" (
96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
97 | if errorlevel 1 exit /b 1
98 | echo.
99 | echo.Build finished; now you can process the JSON files.
100 | goto end
101 | )
102 |
103 | if "%1" == "htmlhelp" (
104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
105 | if errorlevel 1 exit /b 1
106 | echo.
107 | echo.Build finished; now you can run HTML Help Workshop with the ^
108 | .hhp project file in %BUILDDIR%/htmlhelp.
109 | goto end
110 | )
111 |
112 | if "%1" == "qthelp" (
113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
114 | if errorlevel 1 exit /b 1
115 | echo.
116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
117 | .qhcp project file in %BUILDDIR%/qthelp, like this:
118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Django-CRUM.qhcp
119 | echo.To view the help file:
120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Django-CRUM.ghc
121 | goto end
122 | )
123 |
124 | if "%1" == "devhelp" (
125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished.
129 | goto end
130 | )
131 |
132 | if "%1" == "epub" (
133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
137 | goto end
138 | )
139 |
140 | if "%1" == "latex" (
141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
145 | goto end
146 | )
147 |
148 | if "%1" == "latexpdf" (
149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
150 | cd %BUILDDIR%/latex
151 | make all-pdf
152 | cd %BUILDDIR%/..
153 | echo.
154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
155 | goto end
156 | )
157 |
158 | if "%1" == "latexpdfja" (
159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
160 | cd %BUILDDIR%/latex
161 | make all-pdf-ja
162 | cd %BUILDDIR%/..
163 | echo.
164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
165 | goto end
166 | )
167 |
168 | if "%1" == "text" (
169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
170 | if errorlevel 1 exit /b 1
171 | echo.
172 | echo.Build finished. The text files are in %BUILDDIR%/text.
173 | goto end
174 | )
175 |
176 | if "%1" == "man" (
177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
178 | if errorlevel 1 exit /b 1
179 | echo.
180 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
181 | goto end
182 | )
183 |
184 | if "%1" == "texinfo" (
185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
186 | if errorlevel 1 exit /b 1
187 | echo.
188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
189 | goto end
190 | )
191 |
192 | if "%1" == "gettext" (
193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
194 | if errorlevel 1 exit /b 1
195 | echo.
196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
197 | goto end
198 | )
199 |
200 | if "%1" == "changes" (
201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
202 | if errorlevel 1 exit /b 1
203 | echo.
204 | echo.The overview file is in %BUILDDIR%/changes.
205 | goto end
206 | )
207 |
208 | if "%1" == "linkcheck" (
209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
210 | if errorlevel 1 exit /b 1
211 | echo.
212 | echo.Link check complete; look for any errors in the above output ^
213 | or in %BUILDDIR%/linkcheck/output.txt.
214 | goto end
215 | )
216 |
217 | if "%1" == "doctest" (
218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
219 | if errorlevel 1 exit /b 1
220 | echo.
221 | echo.Testing of doctests in the sources finished, look at the ^
222 | results in %BUILDDIR%/doctest/output.txt.
223 | goto end
224 | )
225 |
226 | if "%1" == "xml" (
227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
228 | if errorlevel 1 exit /b 1
229 | echo.
230 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
231 | goto end
232 | )
233 |
234 | if "%1" == "pseudoxml" (
235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
236 | if errorlevel 1 exit /b 1
237 | echo.
238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
239 | goto end
240 | )
241 |
242 | :end
243 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Django-CRUM documentation build configuration file, created by
4 | # sphinx-quickstart on Sat Jul 6 00:44:15 2013.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #sys.path.insert(0, os.path.abspath('.'))
20 |
21 | # -- General configuration -----------------------------------------------------
22 |
23 | # If your documentation needs a minimal Sphinx version, state it here.
24 | #needs_sphinx = '1.0'
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = []
29 |
30 | # Add any paths that contain templates here, relative to this directory.
31 | templates_path = ['_templates']
32 |
33 | # The suffix of source filenames.
34 | source_suffix = '.rst'
35 |
36 | # The encoding of source files.
37 | #source_encoding = 'utf-8-sig'
38 |
39 | # The master toctree document.
40 | master_doc = 'index'
41 |
42 | # General information about the project.
43 | project = u'Django-CRUM'
44 | copyright = u'2018, Nine More Minutes, Inc.'
45 |
46 | # The version info for the project you're documenting, acts as replacement for
47 | # |version| and |release|, also used in various other places throughout the
48 | # built documents.
49 | #
50 | # The short X.Y version.
51 | version = '0.7.9'
52 | # The full version, including alpha/beta/rc tags.
53 | release = '0.7.9'
54 |
55 | # The language for content autogenerated by Sphinx. Refer to documentation
56 | # for a list of supported languages.
57 | #language = None
58 |
59 | # There are two options for replacing |today|: either, you set today to some
60 | # non-false value, then it is used:
61 | #today = ''
62 | # Else, today_fmt is used as the format for a strftime call.
63 | #today_fmt = '%B %d, %Y'
64 |
65 | # List of patterns, relative to source directory, that match files and
66 | # directories to ignore when looking for source files.
67 | exclude_patterns = ['_build']
68 |
69 | # The reST default role (used for this markup: `text`) to use for all documents.
70 | #default_role = None
71 |
72 | # If true, '()' will be appended to :func: etc. cross-reference text.
73 | #add_function_parentheses = True
74 |
75 | # If true, the current module name will be prepended to all description
76 | # unit titles (such as .. function::).
77 | #add_module_names = True
78 |
79 | # If true, sectionauthor and moduleauthor directives will be shown in the
80 | # output. They are ignored by default.
81 | #show_authors = False
82 |
83 | # The name of the Pygments (syntax highlighting) style to use.
84 | pygments_style = 'sphinx'
85 |
86 | # A list of ignored prefixes for module index sorting.
87 | #modindex_common_prefix = []
88 |
89 | # If true, keep warnings as "system message" paragraphs in the built documents.
90 | #keep_warnings = False
91 |
92 |
93 | # -- Options for HTML output ---------------------------------------------------
94 |
95 | # The theme to use for HTML and HTML Help pages. See the documentation for
96 | # a list of builtin themes.
97 | html_theme = 'alabaster'
98 |
99 | # Theme options are theme-specific and customize the look and feel of a theme
100 | # further. For a list of options available for each theme, see the
101 | # documentation.
102 | #html_theme_options = {}
103 |
104 | # Add any paths that contain custom themes here, relative to this directory.
105 | #html_theme_path = []
106 |
107 | # The name for this set of Sphinx documents. If None, it defaults to
108 | # " v documentation".
109 | #html_title = None
110 |
111 | # A shorter title for the navigation bar. Default is the same as html_title.
112 | #html_short_title = None
113 |
114 | # The name of an image file (relative to this directory) to place at the top
115 | # of the sidebar.
116 | #html_logo = None
117 |
118 | # The name of an image file (within the static path) to use as favicon of the
119 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
120 | # pixels large.
121 | #html_favicon = None
122 |
123 | # Add any paths that contain custom static files (such as style sheets) here,
124 | # relative to this directory. They are copied after the builtin static files,
125 | # so a file named "default.css" will overwrite the builtin "default.css".
126 | html_static_path = ['_static']
127 |
128 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
129 | # using the given strftime format.
130 | #html_last_updated_fmt = '%b %d, %Y'
131 |
132 | # If true, SmartyPants will be used to convert quotes and dashes to
133 | # typographically correct entities.
134 | #html_use_smartypants = True
135 |
136 | # Custom sidebar templates, maps document names to template names.
137 | #html_sidebars = {}
138 |
139 | # Additional templates that should be rendered to pages, maps page names to
140 | # template names.
141 | #html_additional_pages = {}
142 |
143 | # If false, no module index is generated.
144 | #html_domain_indices = True
145 |
146 | # If false, no index is generated.
147 | #html_use_index = True
148 |
149 | # If true, the index is split into individual pages for each letter.
150 | #html_split_index = False
151 |
152 | # If true, links to the reST sources are added to the pages.
153 | #html_show_sourcelink = True
154 |
155 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
156 | #html_show_sphinx = True
157 |
158 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
159 | #html_show_copyright = True
160 |
161 | # If true, an OpenSearch description file will be output, and all pages will
162 | # contain a tag referring to it. The value of this option must be the
163 | # base URL from which the finished HTML is served.
164 | #html_use_opensearch = ''
165 |
166 | # This is the file name suffix for HTML files (e.g. ".xhtml").
167 | #html_file_suffix = None
168 |
169 | # Output file base name for HTML help builder.
170 | htmlhelp_basename = 'Django-CRUMdoc'
171 |
172 |
173 | # -- Options for LaTeX output --------------------------------------------------
174 |
175 | latex_elements = {
176 | # The paper size ('letterpaper' or 'a4paper').
177 | #'papersize': 'letterpaper',
178 |
179 | # The font size ('10pt', '11pt' or '12pt').
180 | #'pointsize': '10pt',
181 |
182 | # Additional stuff for the LaTeX preamble.
183 | #'preamble': '',
184 | }
185 |
186 | # Grouping the document tree into LaTeX files. List of tuples
187 | # (source start file, target name, title, author, documentclass [howto/manual]).
188 | latex_documents = [
189 | ('index', 'Django-CRUM.tex', u'Django-CRUM Documentation',
190 | u'Nine More Minutes, Inc.', 'manual'),
191 | ]
192 |
193 | # The name of an image file (relative to this directory) to place at the top of
194 | # the title page.
195 | #latex_logo = None
196 |
197 | # For "manual" documents, if this is true, then toplevel headings are parts,
198 | # not chapters.
199 | #latex_use_parts = False
200 |
201 | # If true, show page references after internal links.
202 | #latex_show_pagerefs = False
203 |
204 | # If true, show URL addresses after external links.
205 | #latex_show_urls = False
206 |
207 | # Documents to append as an appendix to all manuals.
208 | #latex_appendices = []
209 |
210 | # If false, no module index is generated.
211 | #latex_domain_indices = True
212 |
213 |
214 | # -- Options for manual page output --------------------------------------------
215 |
216 | # One entry per manual page. List of tuples
217 | # (source start file, name, description, authors, manual section).
218 | man_pages = [
219 | ('index', 'django-crum', u'Django-CRUM Documentation',
220 | [u'Nine More Minutes, Inc.'], 1)
221 | ]
222 |
223 | # If true, show URL addresses after external links.
224 | #man_show_urls = False
225 |
226 |
227 | # -- Options for Texinfo output ------------------------------------------------
228 |
229 | # Grouping the document tree into Texinfo files. List of tuples
230 | # (source start file, target name, title, author,
231 | # dir menu entry, description, category)
232 | texinfo_documents = [
233 | ('index', 'Django-CRUM', u'Django-CRUM Documentation',
234 | u'Nine More Minutes, Inc.', 'Django-CRUM', 'One line description of project.',
235 | 'Miscellaneous'),
236 | ]
237 |
238 | # Documents to append as an appendix to all manuals.
239 | #texinfo_appendices = []
240 |
241 | # If false, no module index is generated.
242 | #texinfo_domain_indices = True
243 |
244 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
245 | #texinfo_show_urls = 'footnote'
246 |
247 | # If true, do not generate a @detailmenu in the "Top" node's menu.
248 | #texinfo_no_detailmenu = False
249 |
--------------------------------------------------------------------------------