diff --git a/.gitignore b/.gitignore index 6936d49..b74ec6f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /include /lib /build +/docs/_build .Python .tox /examples/protected_downloads/htmlcov diff --git a/.travis.yml b/.travis.yml index e3d7541..586180c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,16 @@ python: - "3.8" - pypy3.6-7.1.1 +matrix: + include: + - python: "3.7" + env: TOX_ENV=docs + install: pip install tox + script: tox -e $TOX_ENV + - python: "3.7" + env: TOX_ENV=lint,isort + install: pip install tox + script: tox -e $TOX_ENV install: pip install tox-travis codecov @@ -17,9 +27,3 @@ script: tox after_success: - cd examples/protected_downloads - codecov - -matrix: - include: - - python: "3.6" - env: TOX_ENV=flake8 - script: tox -e $TOX_ENV diff --git a/README.rst b/README.rst index 3a7cc55..7cdaa99 100644 --- a/README.rst +++ b/README.rst @@ -5,8 +5,7 @@ Django Sendfile 2 .. image:: https://travis-ci.org/moggers87/django-sendfile2.svg?branch=master :target: https://travis-ci.org/moggers87/django-sendfile2 -- Download: https://pypi.org/project/django-sendfile2/ -- Source: https://github.com/moggers87/django-sendfile2 +.. inclusion-marker-do-not-remove-start This is a wrapper around web-server specific methods for sending files to web clients. This is useful when Django needs to check permissions associated @@ -16,69 +15,19 @@ serving large files is not what Django is made for. Note this should not be used for regular file serving (e.g. css etc), only for cases where you need Django to do some work before serving the actual file. -The interface is a single function `sendfile(request, filename, -attachment=False, attachment_filename=None)`, which returns a HTTPResponse -object. - -:: - - from django_sendfile import sendfile - - # send myfile.pdf to user - return sendfile(request, '/home/john/myfile.pdf') - - # send myfile.pdf as an attachment (with name myfile.pdf) - return sendfile(request, '/home/john/myfile.pdf', attachment=True) - - # send myfile.pdf as an attachment with a different name - return sendfile(request, '/home/john/myfile.pdf', attachment=True, attachment_filename='full-name.pdf') - - - -Backends are specified using the setting `SENDFILE_BACKEND`. Currently -available backends are: - -* `django_sendfile.backends.development` - for use with Django development server - only. DO NOT USE IN PRODUCTION -* `django_sendfile.backends.simple` - "simple" backend that uses Django file objects - to attempt to stream files from disk (note middleware may cause files to be - loaded fully into memory) -* `django_sendfile.backends.xsendfile` - sets X-Sendfile header (as used by - mod_xsendfile/Apache and Lighthttpd) -* `django_sendfile.backends.mod_wsgi` - sets Location with 200 code to trigger - internal redirect (daemon mode mod_wsgi only - see below) -* `django_sendfile.backends.nginx` - sets X-Accel-Redirect header to trigger internal - redirect to file - -If you want to write your own backend simply create a module with a `sendfile` -function matching: - -:: - - def sendfile(request, filename): - '''Return HttpResponse object for serving file''' - - -Then specify the full path to the module in `SENDFILE_BACKEND`. You only need -to implement the sending of the file. Adding the content-disposition headers -etc is done elsewhere. - -Supported Django Versions -========================= - -Django 2.1, 2.2, and 3.0 are currently supported by this library. +- Download: https://pypi.org/project/django-sendfile2/ +- Source: https://github.com/moggers87/django-sendfile2 +- Documentation: https://django-sendfile2.readthedocs.io/ Supported Python Versions ========================= Python 3.5, 3.6. 3.7, and 3.8 are currently supported by this library. -Installation -============ - -:: +Supported Django Versions +========================= - pip install django-sendfile2 +Django 2.1, 2.2, and 3.0 are currently supported by this library. Fork ==== @@ -88,136 +37,11 @@ This project is a fork of `django-sendfile appears mostly dead and has a number of outstanding bugs (especially with Python 3). -Development backend -=================== - -The Development backend is only meant for use while writing code. It uses -Django's static file serving code to do the job, which is only meant for -development. It reads the whole file into memory and the sends it down the -wire - not good for big files, but OK when you are just testing things out. - -It will work with the Django dev server and anywhere else you can run Django. - -Simple backend -============== - -This backend is one step up from the development backend. It uses Django's -`django.core.files.base.File` class to try and stream files from disk. However -some middleware (e.g. GzipMiddleware) that rewrites content will causes the -entire file to be loaded into memory. So only use this backend if you are not -using middleware that rewrites content or you only have very small files. - - -xsendfile backend -================= - -Install either mod_xsendfile_ in Apache_ or use Lighthttpd_. You may need to -configure mod_xsendfile_, but that should be as simple as: - -:: - - XSendFile On - -In your virtualhost file/conf file. - - -mod_wsgi backend -================ - -The mod_wsgi backend will only work when using mod_wsgi in daemon mode, not in -embedded mode. It requires a bit more work to get it to do the same job as -xsendfile though. However some may find it easier to setup, as they don't need -to compile and install mod_xsendfile_. - -Firstly there are two more Django settings: - -* `SENDFILE_ROOT` - this is a directoy where all files that will be used with - sendfile must be located -* `SENDFILE_URL` - internal URL prefix for all files served via sendfile - -These settings are needed as this backend makes mod_wsgi_ send an internal -redirect, so we have to convert a file path into a URL. This means that the -files are visible via Apache_ by default too. So we need to get Apache_ to -hide those files from anything that's not an internal redirect. To so this we -can use some mod_rewrite_ magic along these lines: - -:: - - RewriteEngine On - # see if we're on an internal redirect or not - RewriteCond %{THE_REQUEST} ^[\S]+\ /private/ - RewriteRule ^/private/ - [F] - - Alias /private/ /home/john/Development/myapp/private/ - - Order deny,allow - Allow from all - - - -In this case I have also set: - -:: - - SENDFILE_ROOT = '/home/john/Development/myapp/private/' - SENDFILE_URL = '/private' - - -All files are stored in a folder called 'private'. We forbid access to this -folder (`RewriteRule ^/private/ - [F]`) if someone tries to access it directly -(`RewriteCond %{THE_REQUEST} ^[\S]+\ /private/`) by checking the original -request (`THE_REQUEST`). - -Allegedly `IS_SUBREQ` can be used to `perform the same job -`_, -but I was unable to get this working. - - -Nginx backend -============= - -As with the mod_wsgi backend you need to set two extra settings: - -* `SENDFILE_ROOT` - this is a directory where all files that will be used with - sendfile must be located -* `SENDFILE_URL` - internal URL prefix for all files served via sendfile - -You then need to configure Nginx to only allow internal access to the files you -wish to serve. More details on this `are here -`_. - -For example though, if I use the Django settings: - -:: - - SENDFILE_ROOT = '/home/john/Development/django-sendfile/examples/protected_downloads/protected' - SENDFILE_URL = '/protected' - -Then the matching location block in nginx.conf would be: - -:: - - location /protected/ { - internal; - root /home/john/Development/django-sendfile/examples/protected_downloads; - } - -You need to pay attention to whether you have trailing slashes or not on the -SENDFILE_URL and root values, otherwise you may not get the right URL being -sent to Nginx and you may get 404s. You should be able to see what file Nginx -is trying to load in the error.log if this happens. From there it should be -fairly easy to work out what the right settings are. - Funding ======= If you have found django-sendfile2 to be useful and would like to see its continued development, please consider `buying me a coffee -`__. - -.. _mod_xsendfile: https://tn123.org/mod_xsendfile/ -.. _Apache: http://httpd.apache.org/ -.. _Lighthttpd: http://www.lighttpd.net/ -.. _mod_wsgi: http://www.modwsgi.org/ -.. _mod_rewrite: http://httpd.apache.org/docs/current/mod/mod_rewrite.html +`_. +.. inclusion-marker-do-not-remove-end diff --git a/django_sendfile/__init__.py b/django_sendfile/__init__.py index cf92cb6..40fcf00 100644 --- a/django_sendfile/__init__.py +++ b/django_sendfile/__init__.py @@ -1,5 +1,5 @@ -from .sendfile import sendfile # noqa from ._version import get_versions +from .utils import sendfile # noqa __version__ = get_versions()['version'] del get_versions diff --git a/django_sendfile/backends/development.py b/django_sendfile/backends/development.py index 2c3822d..d321932 100644 --- a/django_sendfile/backends/development.py +++ b/django_sendfile/backends/development.py @@ -4,13 +4,14 @@ def sendfile(request, filename, **kwargs): - ''' - Send file using django dev static file server. + """ + Send file using Django dev static file server. - DO NOT USE IN PRODUCTION - this is only to be used when developing and is provided - for convenience only - ''' + .. warning:: + + Do not use in production. This is only to be used when developing and + is provided for convenience only + """ dirname = os.path.dirname(filename) basename = os.path.basename(filename) return serve(request, basename, dirname) diff --git a/django_sendfile/backends/simple.py b/django_sendfile/backends/simple.py index e012372..11bc02c 100644 --- a/django_sendfile/backends/simple.py +++ b/django_sendfile/backends/simple.py @@ -1,4 +1,4 @@ -from email.utils import parsedate_tz, mktime_tz +from email.utils import mktime_tz, parsedate_tz import os import re import stat diff --git a/django_sendfile/tests.py b/django_sendfile/tests.py index fe950ff..f95b915 100644 --- a/django_sendfile/tests.py +++ b/django_sendfile/tests.py @@ -6,11 +6,12 @@ import shutil from django.conf import settings -from django.http import HttpResponse, Http404, HttpRequest +from django.http import Http404, HttpRequest, HttpResponse from django.test import TestCase from django.utils.encoding import smart_str -from django_sendfile.sendfile import sendfile as real_sendfile, _get_sendfile +from .utils import _get_sendfile +from .utils import sendfile as real_sendfile def sendfile(request, filename, **kwargs): diff --git a/django_sendfile/sendfile.py b/django_sendfile/utils.py similarity index 75% rename from django_sendfile/sendfile.py rename to django_sendfile/utils.py index 08bc698..25d117a 100644 --- a/django_sendfile/sendfile.py +++ b/django_sendfile/utils.py @@ -22,25 +22,25 @@ def _get_sendfile(): def sendfile(request, filename, attachment=False, attachment_filename=None, mimetype=None, encoding=None): - ''' - create a response to send file using backend configured in SENDFILE_BACKEND + """ + Create a response to send file using backend configured in ``SENDFILE_BACKEND`` - Filename is the absolute path to the file to send. + ``filename`` is the absolute path to the file to send. - If attachment is True the content-disposition header will be set accordingly. + If ``attachment`` is ``True`` the ``Content-Disposition`` header will be set accordingly. This will typically prompt the user to download the file, rather - than view it. But even if False, the user may still be prompted, depending + than view it. But even if ``False``, the user may still be prompted, depending on the browser capabilities and configuration. - The content-disposition filename depends on the value of attachment_filename: + The ``Content-Disposition`` filename depends on the value of ``attachment_filename``: - None (default): Same as filename - False: No content-disposition filename - String: Value used as filename + ``None`` (default): Same as ``filename`` + ``False``: No ``Content-Disposition`` filename + ``String``: Value used as filename - If no mimetype or encoding are specified, then they will be guessed via the - filename (using the standard python mimetypes module) - ''' + If neither ``mimetype`` or ``encoding`` are specified, then they will be guessed via the + filename (using the standard Python mimetypes module) + """ _sendfile = _get_sendfile() if not os.path.exists(filename): diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..b122572 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,23 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= -W +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +apidocs: + cd ..; sphinx-apidoc -f -e -o docs/ django_sendfile "*tests*" + +.PHONY: help apidocs Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/backends.rst b/docs/backends.rst new file mode 100644 index 0000000..9d6b0ac --- /dev/null +++ b/docs/backends.rst @@ -0,0 +1,156 @@ +Backends +-------- + +Backends are specified by setting ``SENDFILE_BACKEND`` to the dotted path of +the backend you wish to use. E.g.: + +.. code-block:: python + :caption: settings.py + + SENDFILE_BACKEND = "django_sendfile.backends.simple" + +Development backend +=================== + +:py:mod:`django_sendfile.backends.development` + +The Development backend is only meant for use while writing code. It uses +Django's static file serving code to do the job, which is only meant for +development. It reads the whole file into memory and the sends it down the +wire - not good for big files, but OK when you are just testing things out. + +It will work with the Django dev server and anywhere else you can run Django. + +Simple backend +============== + +:py:mod:`django_sendfile.backends.simple` + +This backend is one step up from the development backend. It uses Django's +``django.core.files.base.File`` class to try and stream files from disk. However +some middleware (e.g. GzipMiddleware) that rewrites content will causes the +entire file to be loaded into memory. So only use this backend if you are not +using middleware that rewrites content or you only have very small files. + + +mod_wsgi backend +================ + +:py:mod:`django_sendfile.backends.mod_wsgi` + +The mod_wsgi backend will only work when using mod_wsgi in daemon mode, not in +embedded mode. It requires a bit more work to get it to do the same job as +xsendfile though. However some may find it easier to setup, as they don't need +to compile and install mod_xsendfile_. + +Firstly there are two more Django settings: + +* ``SENDFILE_ROOT`` - this is a directoy where all files that will be used with + sendfile must be located +* ``SENDFILE_URL`` - internal URL prefix for all files served via sendfile + +These settings are needed as this backend makes mod_wsgi_ send an internal +redirect, so we have to convert a file path into a URL. This means that the +files are visible via Apache_ by default too. So we need to get Apache_ to +hide those files from anything that's not an internal redirect. To so this we +can use some mod_rewrite_ magic along these lines: + +.. code-block:: apache + + RewriteEngine On + # see if we're on an internal redirect or not + RewriteCond %{THE_REQUEST} ^[\S]+\ /private/ + RewriteRule ^/private/ - [F] + + Alias /private/ /home/john/Development/myapp/private/ + + Order deny,allow + Allow from all + + + +In this case I have also set: + +.. code-block:: python + :caption: settings.py + + SENDFILE_ROOT = '/home/john/Development/myapp/private/' + SENDFILE_URL = '/private' + + +All files are stored in a folder called 'private'. We forbid access to this +folder (``RewriteRule ^/private/ - [F]``) if someone tries to access it directly +(``RewriteCond %{THE_REQUEST} ^[\S]+\ /private/``) by checking the original +request (``THE_REQUEST``). + +Allegedly ``IS_SUBREQ`` can be used to `perform the same job +`_, +but I was unable to get this working. + + +Nginx backend +============= + +:py:mod:`django_sendfile.backends.nginx` + +As with the mod_wsgi backend you need to set two extra settings: + +* ``SENDFILE_ROOT`` - this is a directory where all files that will be used with + sendfile must be located +* ``SENDFILE_URL`` - internal URL prefix for all files served via sendfile + +You then need to configure Nginx to only allow internal access to the files you +wish to serve. More details on this `are here +`_. + +For example though, if I use the Django settings: + +.. code-block:: python + :caption: settings.py + + SENDFILE_ROOT = '/home/john/Development/django-sendfile/examples/protected_downloads/protected' + SENDFILE_URL = '/protected' + +Then the matching location block in nginx.conf would be: + +.. code-block:: nginx + + location /protected/ { + internal; + root /home/john/Development/django-sendfile/examples/protected_downloads; + } + +You need to pay attention to whether you have trailing slashes or not on the +``SENDFILE_URL`` and ``SENDFILE_ROOT`` values, otherwise you may not get the +right URL being sent to Nginx and you may get 404s. You should be able to see +what file Nginx is trying to load in the error.log if this happens. From there +it should be fairly easy to work out what the right settings are. + + +xsendfile backend +================= + +:py:mod:`django_sendfile.backends.xsendfile` + +Install either mod_xsendfile_ in Apache_ or use Lighthttpd_. You may need to +configure mod_xsendfile_, but that should be as simple as: + +.. code-block:: lighty + + XSendFile On + +In your virtualhost file/conf file. + +As with the mod_wsgi backend you need to set two extra settings: + +* ``SENDFILE_ROOT`` - this is a directory where all files that will be used with + sendfile must be located +* ``SENDFILE_URL`` - internal URL prefix for all files served via sendfile + + +.. _mod_xsendfile: https://tn123.org/mod_xsendfile/ +.. _Apache: http://httpd.apache.org/ +.. _Lighthttpd: http://www.lighttpd.net/ +.. _mod_wsgi: http://www.modwsgi.org/ +.. _mod_rewrite: http://httpd.apache.org/docs/current/mod/mod_rewrite.html + diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..4f1a321 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,75 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +from django.conf import settings + +import django_sendfile + +settings.configure( + DEBUG=False, + INSTALLED_APPS=(), +) + + +# -- Project information ----------------------------------------------------- + +project = 'django-sendfile2' +copyright = '2020, Matt Molyneaux' +author = 'Matt Molyneaux' + +version = ".".join(django_sendfile.__version__.split(".")[:2]) +release = django_sendfile.__version__ + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage', + 'sphinx.ext.doctest', + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +pygments_style = 'sphinx' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = [] + +# -- Extension configuration ------------------------------------------------- + +autoclass_content = "both" diff --git a/docs/custom-backend.rst b/docs/custom-backend.rst new file mode 100644 index 0000000..61d02df --- /dev/null +++ b/docs/custom-backend.rst @@ -0,0 +1,29 @@ +Custom Backend +-------------- + +A django-sendfile2 backend is simply a Python module that contains a callable +named ``sendfile``, for example: + +.. code-block:: python + :caption: myModule.py + + def sendfile(request, filename, **kwargs): + response = HttpResponse() + response["X-My-Custom-Header"] = filename + return response + +Assuming the module is in your Python path and named ``myModule``, you'd set ``SENDILE_BACKEND`` like so: + +.. code-block:: python + :caption: settings.py + + SENDFILE_BACKEND = "myModule" + +…and use django-sendfile2 in your views as you would normally. + +.. warning:: + + Don't get confused between this ``sendfile`` callable and + :py:func:`~django_sendfile.utils.sendfile` used in your views. The latter + accepts slightly different arguments and takes care of various + ``Content-*`` headers. diff --git a/docs/django_sendfile.backends.development.rst b/docs/django_sendfile.backends.development.rst new file mode 100644 index 0000000..86c1ccd --- /dev/null +++ b/docs/django_sendfile.backends.development.rst @@ -0,0 +1,7 @@ +django\_sendfile.backends.development module +============================================ + +.. automodule:: django_sendfile.backends.development + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/django_sendfile.backends.mod_wsgi.rst b/docs/django_sendfile.backends.mod_wsgi.rst new file mode 100644 index 0000000..a195573 --- /dev/null +++ b/docs/django_sendfile.backends.mod_wsgi.rst @@ -0,0 +1,7 @@ +django\_sendfile.backends.mod\_wsgi module +========================================== + +.. automodule:: django_sendfile.backends.mod_wsgi + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/django_sendfile.backends.nginx.rst b/docs/django_sendfile.backends.nginx.rst new file mode 100644 index 0000000..45df931 --- /dev/null +++ b/docs/django_sendfile.backends.nginx.rst @@ -0,0 +1,7 @@ +django\_sendfile.backends.nginx module +====================================== + +.. automodule:: django_sendfile.backends.nginx + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/django_sendfile.backends.rst b/docs/django_sendfile.backends.rst new file mode 100644 index 0000000..d915832 --- /dev/null +++ b/docs/django_sendfile.backends.rst @@ -0,0 +1,21 @@ +django\_sendfile.backends package +================================= + +Submodules +---------- + +.. toctree:: + + django_sendfile.backends.development + django_sendfile.backends.mod_wsgi + django_sendfile.backends.nginx + django_sendfile.backends.simple + django_sendfile.backends.xsendfile + +Module contents +--------------- + +.. automodule:: django_sendfile.backends + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/django_sendfile.backends.simple.rst b/docs/django_sendfile.backends.simple.rst new file mode 100644 index 0000000..46f147b --- /dev/null +++ b/docs/django_sendfile.backends.simple.rst @@ -0,0 +1,7 @@ +django\_sendfile.backends.simple module +======================================= + +.. automodule:: django_sendfile.backends.simple + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/django_sendfile.backends.xsendfile.rst b/docs/django_sendfile.backends.xsendfile.rst new file mode 100644 index 0000000..e00f252 --- /dev/null +++ b/docs/django_sendfile.backends.xsendfile.rst @@ -0,0 +1,7 @@ +django\_sendfile.backends.xsendfile module +========================================== + +.. automodule:: django_sendfile.backends.xsendfile + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/django_sendfile.rst b/docs/django_sendfile.rst new file mode 100644 index 0000000..0afbb77 --- /dev/null +++ b/docs/django_sendfile.rst @@ -0,0 +1,24 @@ +django\_sendfile package +======================== + +Subpackages +----------- + +.. toctree:: + + django_sendfile.backends + +Submodules +---------- + +.. toctree:: + + django_sendfile.utils + +Module contents +--------------- + +.. automodule:: django_sendfile + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/django_sendfile.utils.rst b/docs/django_sendfile.utils.rst new file mode 100644 index 0000000..3f9f269 --- /dev/null +++ b/docs/django_sendfile.utils.rst @@ -0,0 +1,7 @@ +django\_sendfile.utils module +================================ + +.. automodule:: django_sendfile.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/getting-started.rst b/docs/getting-started.rst new file mode 100644 index 0000000..8bd3c5e --- /dev/null +++ b/docs/getting-started.rst @@ -0,0 +1,47 @@ +Getting Started +--------------- + +Installation +============ + +Install via pip: + +.. code-block:: shell + + pip install django-sendfile2 + +And then add ``django_sendfile`` to ``INSTALLED_APPS`` in your settings module. + +.. note:: + + It is not strictly nessessary to have django_sendfile in + ``INSTALLED_APPS``, but this may change in future. + +You will also need to select a backend via ``SENDFILE_BACKEND`` in your +settings module. Additionally, you may need to set ``SENDFILE_URL`` and +``SENDFILE_ROOT``. See the :doc:`backends` documentation for more details. + + +Use In Views +============ + +Use the :py:func:`~django_sendfile.sendfile` function instead of the usual +``HttpResponse`` function: + +.. code-block:: python + + from django_sendfile import sendfile + + @login_required + def my_secret_view(request): + return sendfile(request, "/opt/my_secret.txt", mimetype="text/plain") + +Alternatively, if you prefer class based views something like this would be required: + +.. code-block:: python + + from django_sendfile import sendfile + + class MySecretView(LoginRequiredMixin, View): + def render_to_response(self, context): + return sendfile diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..1d2c185 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,29 @@ +.. django-sendfile2 documentation master file, created by + sphinx-quickstart on Mon Feb 3 01:45:59 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to django-sendfile2's documentation! +============================================ + +.. include:: ../README.rst + :start-after: inclusion-marker-do-not-remove-start + :end-before: inclusion-marker-do-not-remove-end + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + getting-started + backends + custom-backend + django_sendfile2 API Guide + security + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..2119f51 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/modules.rst b/docs/modules.rst new file mode 100644 index 0000000..65a28c2 --- /dev/null +++ b/docs/modules.rst @@ -0,0 +1,7 @@ +django_sendfile +=============== + +.. toctree:: + :maxdepth: 4 + + django_sendfile diff --git a/docs/security.rst b/docs/security.rst new file mode 100644 index 0000000..0b52025 --- /dev/null +++ b/docs/security.rst @@ -0,0 +1,11 @@ +Security +======== + +If you find a security issue with django-sendfile2, email `security@moggers87.co.uk +`__. If you want to send an encrypted report, then +please use key id 0x878B5A2A1D47C084. + +django-sendfile2 follows the same security reporting model that has worked for other +open source projects: If you report a security vulnerability, it will be acted +on immediately and a fix with complete full disclosure will go out to everyone +at the same time. diff --git a/examples/protected_downloads/download/migrations/0001_initial.py b/examples/protected_downloads/download/migrations/0001_initial.py index 11a7643..cf38fbf 100644 --- a/examples/protected_downloads/download/migrations/0001_initial.py +++ b/examples/protected_downloads/download/migrations/0001_initial.py @@ -1,8 +1,8 @@ # Generated by Django 2.2.2 on 2019-06-05 19:03 from django.conf import settings -import django.core.files.storage from django.db import migrations, models +import django.core.files.storage class Migration(migrations.Migration): diff --git a/examples/protected_downloads/download/models.py b/examples/protected_downloads/download/models.py index 6b07549..912e4d7 100644 --- a/examples/protected_downloads/download/models.py +++ b/examples/protected_downloads/download/models.py @@ -1,7 +1,6 @@ -from django.db import models - from django.conf import settings from django.core.files.storage import FileSystemStorage +from django.db import models from django.urls import reverse sendfile_storage = FileSystemStorage(location=settings.SENDFILE_ROOT) diff --git a/examples/protected_downloads/download/views.py b/examples/protected_downloads/download/views.py index 0a8f97d..a06f929 100644 --- a/examples/protected_downloads/download/views.py +++ b/examples/protected_downloads/download/views.py @@ -1,7 +1,7 @@ from django.contrib.auth.decorators import login_required -from django.shortcuts import get_object_or_404, render -from django.http import HttpResponseForbidden from django.db.models import Q +from django.http import HttpResponseForbidden +from django.shortcuts import get_object_or_404, render from django.template import RequestContext from django_sendfile import sendfile diff --git a/examples/protected_downloads/urls.py b/examples/protected_downloads/urls.py index d4dabb2..c9e9c33 100644 --- a/examples/protected_downloads/urls.py +++ b/examples/protected_downloads/urls.py @@ -1,6 +1,6 @@ from django.conf import urls - from django.contrib import admin + admin.autodiscover() urlpatterns = [ diff --git a/setup.py b/setup.py index ee87896..f6faba5 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,6 @@ import versioneer - setup( name='django-sendfile2', version=versioneer.get_version(), @@ -19,6 +18,12 @@ install_requires=['django'], packages=['django_sendfile', 'django_sendfile.backends'], + extras_require={ + "docs": [ + "sphinx", + "sphinx_rtd_theme", + ], + }, package_dir={ 'django_sendfile': 'django_sendfile', 'django_sendfile.backends': 'django_sendfile/backends', @@ -26,7 +31,6 @@ package_data={ 'django_sendfile': ['testfile.txt'], }, - zip_safe=True, classifiers=[ 'Development Status :: 4 - Beta', diff --git a/tox.ini b/tox.ini index 3e86904..0cba2bb 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,9 @@ envlist = clean py{py3,36,37,38}-django{21,22,30} py35-django{21,22} - flake8 + docs + lint + isort coverage [testenv] @@ -22,7 +24,6 @@ setenv = PYTHONWARNINGS = default python = pypy: clean,pypy,coverage pypy3: clean,pypy3,coverage - 2.7: clean,py27,coverage 3.5: clean,py35,coverage 3.6: clean,py36,coverage 3.7: clean,py37,coverage @@ -40,12 +41,31 @@ commands = coverage report coverage html -[testenv:flake8] +[testenv:lint] basepython=python changedir = commands=flake8 sendfile examples/protected_downloads *.py {posargs} deps=flake8 +[testenv:isort] +commands = isort --check-only --diff +changedir = +deps = isort + +[testenv:docs] +whitelist_externals = make +changedir = docs +commands = make html +extras = docs + +[isort] +line_length = 100 +from_first = true +use_parentheses = true +skip_glob = + django_sendfile/_version.py + versioneer.py + [flake8] max_line_length=100 exclude = @@ -55,3 +75,4 @@ exclude = .tox __pycache__ docs +