diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index b87ae969..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = python -msphinx -SPHINXPROJ = streaming-form-data -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help 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) \ No newline at end of file diff --git a/docs/_templates/usefullinks.html b/docs/_templates/usefullinks.html deleted file mode 100644 index 5c3dc560..00000000 --- a/docs/_templates/usefullinks.html +++ /dev/null @@ -1,8 +0,0 @@ -

Useful links

- - diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 43b83779..00000000 --- a/docs/conf.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# streaming-form-data documentation build configuration file, created by -# sphinx-quickstart on Mon Jun 19 18:35:29 2017. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# 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('.')) - -import sphinx_rtd_theme - - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'streaming-form-data' -copyright = '2017 - 2023, Siddhant Goel' -author = 'Siddhant Goel' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '1.13' -# The full version, including alpha/beta/rc tags. -release = '1.13.0' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- 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' - -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# - -# html_theme_options = { -# 'show_powered_by': True, -# 'github_user': 'siddhantgoel', -# 'github_repo': 'streaming-form-data', -# 'github_banner': True, -# 'show_related': False -# } - -# 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 = ['_static'] - -html_sidebars = { - 'index': ['localtoc.html', 'relations.html', 'sourcelink.html', - 'usefullinks.html', 'searchbox.html'] -} - - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. -htmlhelp_basename = 'streaming-form-datadoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, - 'streaming-form-data.tex', - 'streaming-form-data Documentation', - 'Siddhant Goel', - 'manual'), -] - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'streaming-form-data', 'streaming-form-data Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'streaming-form-data', 'streaming-form-data Documentation', - author, 'streaming-form-data', 'One line description of project.', - 'Miscellaneous'), -] diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 0876febe..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,285 +0,0 @@ -Streaming multipart/form-data parser -==================================== - -.. image:: https://travis-ci.org/siddhantgoel/streaming-form-data.svg?branch=stable - :target: https://travis-ci.org/siddhantgoel/streaming-form-data - -.. image:: https://badge.fury.io/py/streaming-form-data.svg - :target: https://pypi.python.org/pypi/streaming-form-data - - -:code:`streaming_form_data` provides a Python parser for parsing -:code:`multipart/form-data` input chunks (the most commonly used encoding when -submitting data through HTML forms). - -Chunk size is determined by the API user, but currently there are no -restrictions on what the chunk size should be, since the parser works -byte-by-byte (which means that passing the entire input as a single chunk should -also work). - -Installation ------------- - -.. code-block:: bash - - $ pip install streaming_form_data - -The core parser is written in :code:`Cython`, which is a superset of Python that -compiles the input down to a C extension which can then be imported in normal -Python code. - -The compiled C parser code is included in the PyPI package, hence the -installation requires a working C compiler. - -Usage ------ - -.. code-block:: python - - >>> from streaming_form_data import StreamingFormDataParser - >>> from streaming_form_data.targets import ValueTarget, FileTarget, NullTarget - >>> - >>> headers = {'Content-Type': 'multipart/form-data; boundary=boundary'} - >>> - >>> parser = StreamingFormDataParser(headers=headers) - >>> - >>> parser.register('name', ValueTarget()) - >>> parser.register('file', FileTarget('/tmp/file.txt')) - >>> parser.register('discard-me', NullTarget()) - >>> - >>> for chunk in request.body: - ... parser.data_received(chunk) - ... - >>> - -Usage can broadly be split into three stages. - -1. Initialization -~~~~~~~~~~~~~~~~~ - -The :code:`StreamingFormDataParser` class expects a dictionary of HTTP request -headers when being instantiated. These headers are used to determine the input -:code:`Content-Type` and a few other metadata. - -Optionally, you can enable strict mode in the parser by setting the :code:`strict` -keyword argument to :code:`True`. In strict mode, the parser throws -:code:`UnexpectedPartException` if it starts to parse a field whose name has not -been registered. When not in strict mode, unexpected parts are silently ignored. - -2. Input Registration -~~~~~~~~~~~~~~~~~~~~~ - -HTML forms typically have multiple fields. For instance, a form could have a -text input field called :code:`name` and a file input field called -:code:`file`. - -This needs to be communicated to the parser using the :code:`parser.register` -function. This function expects two arguments - the name of the input field, and -the associated :code:`Target` class (which determines how the input should be -handled). - -For instance, if you want to store the contents of the :code:`name` field in an -in-memory variable, and the :code:`file` field in a file on disk, you can tell -this to the parser as follows. - -.. code-block:: python - - >>> name_target = ValueTarget() - >>> file_target = FileTarget('/tmp/file.dat') - >>> - >>> parser.register('name', name_target) - >>> parser.register('file', file_target) - -Registering multiple targets is also supported. - -.. code-block:: python - - >>> name_target = ValueTarget() - >>> sha256_target = SHA256Target() - >>> - >>> parser.register('file', name_target) - >>> parser.register('file', sha256_target) - -In this case, the contents of the :code:`file` field would be streamed to both -the :code:`ValueTarget` as well as the :code:`SHA256Target`. - -3. Streaming data -~~~~~~~~~~~~~~~~~ - -At this stage the parser has everything it needs to be able to work. Depending -on what web framework you're using, just pass the actual HTTP request body to -the parser, either one chunk at a time or the complete thing at once. - -.. code-block:: python - - >> chunk = read_next_chunk() # depends on your web framework of choice - >> - >> parser.data_received(chunk) - - -API ---- - -:code:`StreamingFormDataParser` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This class is the main entry point. It expects a dictionary of HTTP request -:code:`headers` and has a keyword argument :code:`strict`. The headers are used -to determine the input :code:`Content-Type` and a few other metadata. The strict -flag is used to enable or disable the strict mode. - -:code:`Target` classes -~~~~~~~~~~~~~~~~~~~~~~ - -When registering inputs with the parser, instances of subclasses of the -:code:`Target` class should be used. These target classes ultimately determine -what to do with the data. - -:code:`ValueTarget` -``````````````````` - -:code:`ValueTarget` objects hold the input in memory. - -.. code-block:: python - - >>> target = ValueTarget() - -:code:`FileTarget` -`````````````````` - -:code:`FileTarget` objects stream the contents to a file on-disk. - -.. code-block:: python - - >>> target = FileTarget('/tmp/file.txt') - -:code:`DirectoryTarget` -``````````````````````` - -:code:`DirectoryTarget` objects stream the contents to a directory on-disk. - -.. code-block:: python - - >>> target = DirectoryTarget('/tmp/uploads/') - -:code:`SHA256Target` -```````````````````` - -:code:`SHA256Target` objects calculate a :code:`SHA256` hash of the given input, -and hold the result in memory. - -.. code-block:: python - - >>> target = SHA256Target() - -:code:`NullTarget` -`````````````````` - -:code:`NullTarget` objects discard the input completely. - -.. code-block:: python - - >>> target = NullTarget() - -:code:`S3Target` -```````````````` - -:code:`S3Target` objects stream the contents of a file to an S3 bucket. - -.. code-block:: python - - >>> target = S3Target("s3:///path/to/key", "wb") - -:code:`CSVTarget` -```````````````` - -:code:`CSVTarget` objects process and release CSV lines in chunks. - -.. code-block:: python - - >>> target = CSVTarget() - -Custom :code:`Target` classes -````````````````````````````` - -It's possible to define custom targets for your specific use case by inheriting -the :code:`streaming_form_data.targets.BaseTarget` class and overriding the -:code:`on_data_received` function. - -.. code-block:: python - - >>> from streaming_form_data.targets import BaseTarget - >>> - >>> class CustomTarget(BaseTarget): - ... def on_data_received(self, chunk): - ... do_something(chunk) - -If the :code:`Content-Disposition` header included the :code:`filename` -directive, this value will be available as the :code:`self.multipart_filename` -attribute in :code:`Target` classes. - -Similarly, if the :code:`Content-Type` header is available for the uploaded -files, this value will be available as the :code:`self.multipart_content_type` -attribute in :code:`Target` classes. - -:code:`Validator` classes -~~~~~~~~~~~~~~~~~~~~~~~~~ - -:code:`Target` classes accept a :code:`validator` callable when being -instantiated. Every time :code:`data_received` is called with a given -:code:`chunk`, the target runs this :code:`chunk` through the given callable. - -This is useful for performing certain validation tasks like making sure the -input size is not exceeding a certain value. This is shown in the following code -snippet. - -.. code-block:: python - - >>> from streaming_form_data.targets import ValueTarget - >>> - >>> target = ValueTarget(validator=MaxSizeValidator(100)) - - -Exceptions -~~~~~~~~~~ - -:code:`ParseFailedException` -```````````````````````````` -This exception is the base class of the :code:`streaming_form_data` exceptions. -It can be raised during initialization, registering parts or reading chunks. - -:code:`UnexpectedPartException` -``````````````````````````````` -This exception is raised when the parser is in strict mode and starts to parse -an unexpected part. It contains :code:`part_name` attribute to check the name of -the unexpected part. In can only be raised from :code:`data_received`. - -.. code-block:: python - >>> try: - >>> parser.data_received(chunk) - >>> except streaming_form_data.parser.UnexpectedPartException as e: - >>> print(e.part_name) - >>> raise - - -Examples --------- - -- :code:`Bottle` - https://git.io/vhCUy -- :code:`Flask` - https://git.io/fjPoA -- :code:`Tornado` - https://git.io/vhCUM - -If you'd like to document usage with another web framework (which ideally -allows chunked HTTP reads), please open an issue or a pull request. - - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - - -Indices and tables -================== - -* :ref:`search`