add fast api requirements multipart

dev-fastapi
Иван Маслов 2 years ago
parent a218ba057f
commit 6db4724cc5

@ -0,0 +1,15 @@
# This is the canonical package information.
__author__ = 'Andrew Dunham'
__license__ = 'Apache'
__copyright__ = "Copyright (c) 2012-2013, Andrew Dunham"
__version__ = "0.0.6"
from .multipart import (
FormParser,
MultipartParser,
QuerystringParser,
OctetStreamParser,
create_form_parser,
parse_form,
)

@ -0,0 +1,171 @@
import base64
import binascii
from .exceptions import DecodeError
class Base64Decoder:
"""This object provides an interface to decode a stream of Base64 data. It
is instantiated with an "underlying object", and whenever a write()
operation is performed, it will decode the incoming data as Base64, and
call write() on the underlying object. This is primarily used for decoding
form data encoded as Base64, but can be used for other purposes::
from multipart.decoders import Base64Decoder
fd = open("notb64.txt", "wb")
decoder = Base64Decoder(fd)
try:
decoder.write("Zm9vYmFy") # "foobar" in Base64
decoder.finalize()
finally:
decoder.close()
# The contents of "notb64.txt" should be "foobar".
This object will also pass all finalize() and close() calls to the
underlying object, if the underlying object supports them.
Note that this class maintains a cache of base64 chunks, so that a write of
arbitrary size can be performed. You must call :meth:`finalize` on this
object after all writes are completed to ensure that all data is flushed
to the underlying object.
:param underlying: the underlying object to pass writes to
"""
def __init__(self, underlying):
self.cache = bytearray()
self.underlying = underlying
def write(self, data):
"""Takes any input data provided, decodes it as base64, and passes it
on to the underlying object. If the data provided is invalid base64
data, then this method will raise
a :class:`multipart.exceptions.DecodeError`
:param data: base64 data to decode
"""
# Prepend any cache info to our data.
if len(self.cache) > 0:
data = self.cache + data
# Slice off a string that's a multiple of 4.
decode_len = (len(data) // 4) * 4
val = data[:decode_len]
# Decode and write, if we have any.
if len(val) > 0:
try:
decoded = base64.b64decode(val)
except binascii.Error:
raise DecodeError('There was an error raised while decoding '
'base64-encoded data.')
self.underlying.write(decoded)
# Get the remaining bytes and save in our cache.
remaining_len = len(data) % 4
if remaining_len > 0:
self.cache = data[-remaining_len:]
else:
self.cache = b''
# Return the length of the data to indicate no error.
return len(data)
def close(self):
"""Close this decoder. If the underlying object has a `close()`
method, this function will call it.
"""
if hasattr(self.underlying, 'close'):
self.underlying.close()
def finalize(self):
"""Finalize this object. This should be called when no more data
should be written to the stream. This function can raise a
:class:`multipart.exceptions.DecodeError` if there is some remaining
data in the cache.
If the underlying object has a `finalize()` method, this function will
call it.
"""
if len(self.cache) > 0:
raise DecodeError('There are %d bytes remaining in the '
'Base64Decoder cache when finalize() is called'
% len(self.cache))
if hasattr(self.underlying, 'finalize'):
self.underlying.finalize()
def __repr__(self):
return f"{self.__class__.__name__}(underlying={self.underlying!r})"
class QuotedPrintableDecoder:
"""This object provides an interface to decode a stream of quoted-printable
data. It is instantiated with an "underlying object", in the same manner
as the :class:`multipart.decoders.Base64Decoder` class. This class behaves
in exactly the same way, including maintaining a cache of quoted-printable
chunks.
:param underlying: the underlying object to pass writes to
"""
def __init__(self, underlying):
self.cache = b''
self.underlying = underlying
def write(self, data):
"""Takes any input data provided, decodes it as quoted-printable, and
passes it on to the underlying object.
:param data: quoted-printable data to decode
"""
# Prepend any cache info to our data.
if len(self.cache) > 0:
data = self.cache + data
# If the last 2 characters have an '=' sign in it, then we won't be
# able to decode the encoded value and we'll need to save it for the
# next decoding step.
if data[-2:].find(b'=') != -1:
enc, rest = data[:-2], data[-2:]
else:
enc = data
rest = b''
# Encode and write, if we have data.
if len(enc) > 0:
self.underlying.write(binascii.a2b_qp(enc))
# Save remaining in cache.
self.cache = rest
return len(data)
def close(self):
"""Close this decoder. If the underlying object has a `close()`
method, this function will call it.
"""
if hasattr(self.underlying, 'close'):
self.underlying.close()
def finalize(self):
"""Finalize this object. This should be called when no more data
should be written to the stream. This function will not raise any
exceptions, but it may write more data to the underlying object if
there is data remaining in the cache.
If the underlying object has a `finalize()` method, this function will
call it.
"""
# If we have a cache, write and then remove it.
if len(self.cache) > 0:
self.underlying.write(binascii.a2b_qp(self.cache))
self.cache = b''
# Finalize our underlying stream.
if hasattr(self.underlying, 'finalize'):
self.underlying.finalize()
def __repr__(self):
return f"{self.__class__.__name__}(underlying={self.underlying!r})"

@ -0,0 +1,46 @@
class FormParserError(ValueError):
"""Base error class for our form parser."""
pass
class ParseError(FormParserError):
"""This exception (or a subclass) is raised when there is an error while
parsing something.
"""
#: This is the offset in the input data chunk (*NOT* the overall stream) in
#: which the parse error occurred. It will be -1 if not specified.
offset = -1
class MultipartParseError(ParseError):
"""This is a specific error that is raised when the MultipartParser detects
an error while parsing.
"""
pass
class QuerystringParseError(ParseError):
"""This is a specific error that is raised when the QuerystringParser
detects an error while parsing.
"""
pass
class DecodeError(ParseError):
"""This exception is raised when there is a decoding error - for example
with the Base64Decoder or QuotedPrintableDecoder.
"""
pass
# On Python 3.3, IOError is the same as OSError, so we don't want to inherit
# from both of them. We handle this case below.
if IOError is not OSError: # pragma: no cover
class FileError(FormParserError, IOError, OSError):
"""Exception class for problems with the File class."""
pass
else: # pragma: no cover
class FileError(FormParserError, OSError):
"""Exception class for problems with the File class."""
pass

@ -0,0 +1,133 @@
import os
import re
import sys
import types
import functools
def ensure_in_path(path):
"""
Ensure that a given path is in the sys.path array
"""
if not os.path.isdir(path):
raise RuntimeError('Tried to add nonexisting path')
def _samefile(x, y):
try:
return os.path.samefile(x, y)
except OSError:
return False
except AttributeError:
# Probably on Windows.
path1 = os.path.abspath(x).lower()
path2 = os.path.abspath(y).lower()
return path1 == path2
# Remove existing copies of it.
for pth in sys.path:
if _samefile(pth, path):
sys.path.remove(pth)
# Add it at the beginning.
sys.path.insert(0, path)
# Check if pytest is imported. If so, we use it to create marking decorators.
# If not, we just create a function that does nothing.
try:
import pytest
except ImportError:
pytest = None
if pytest is not None:
slow_test = pytest.mark.slow_test
xfail = pytest.mark.xfail
else:
slow_test = lambda x: x
def xfail(*args, **kwargs):
if len(args) > 0 and isinstance(args[0], types.FunctionType):
return args[0]
return lambda x: x
# We don't use the pytest parametrizing function, since it seems to break
# with unittest.TestCase subclasses.
def parametrize(field_names, field_values):
# If we're not given a list of field names, we make it.
if not isinstance(field_names, (tuple, list)):
field_names = (field_names,)
field_values = [(val,) for val in field_values]
# Create a decorator that saves this list of field names and values on the
# function for later parametrizing.
def decorator(func):
func.__dict__['param_names'] = field_names
func.__dict__['param_values'] = field_values
return func
return decorator
# This is a metaclass that actually performs the parametrization.
class ParametrizingMetaclass(type):
IDENTIFIER_RE = re.compile('[^A-Za-z0-9]')
def __new__(klass, name, bases, attrs):
new_attrs = attrs.copy()
for attr_name, attr in attrs.items():
# We only care about functions
if not isinstance(attr, types.FunctionType):
continue
param_names = attr.__dict__.pop('param_names', None)
param_values = attr.__dict__.pop('param_values', None)
if param_names is None or param_values is None:
continue
# Create multiple copies of the function.
for i, values in enumerate(param_values):
assert len(param_names) == len(values)
# Get a repr of the values, and fix it to be a valid identifier
human = '_'.join(
[klass.IDENTIFIER_RE.sub('', repr(x)) for x in values]
)
# Create a new name.
# new_name = attr.__name__ + "_%d" % i
new_name = attr.__name__ + "__" + human
# Create a replacement function.
def create_new_func(func, names, values):
# Create a kwargs dictionary.
kwargs = dict(zip(names, values))
@functools.wraps(func)
def new_func(self):
return func(self, **kwargs)
# Manually set the name and return the new function.
new_func.__name__ = new_name
return new_func
# Actually create the new function.
new_func = create_new_func(attr, param_names, values)
# Save this new function in our attrs dict.
new_attrs[new_name] = new_func
# Remove the old attribute from our new dictionary.
del new_attrs[attr_name]
# We create the class as normal, except we use our new attributes.
return type.__new__(klass, name, bases, new_attrs)
# This is a class decorator that actually applies the above metaclass.
def parametrize_class(klass):
return ParametrizingMetaclass(klass.__name__,
klass.__bases__,
klass.__dict__)

@ -0,0 +1,5 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content- isposition: form-data; name="field"
This is a test.
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,3 @@
boundary: ----WebKitFormBoundaryTkr3kCBQlBe1nrhc
expected:
error: 51

@ -0,0 +1,5 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content-Disposition: form-data; n me="field"
This is a test.
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,13 @@
----boundary
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
--boundari
--boundaryq--boundary q--boundarq
--bounaryd--
--notbound--
--mismatch
--mismatch--
--boundary-Q
--boundary Q--boundaryQ
----boundary--

@ -0,0 +1,8 @@
boundary: --boundary
expected:
- name: file
type: file
file_name: test.txt
data: !!binary |
LS1ib3VuZGFyaQ0KLS1ib3VuZGFyeXEtLWJvdW5kYXJ5DXEtLWJvdW5kYXJxDQotLWJvdW5hcnlkLS0NCi0tbm90Ym91bmQtLQ0KLS1taXNtYXRjaA0KLS1taXNtYXRjaC0tDQotLWJvdW5kYXJ5LVENCi0tYm91bmRhcnkNUS0tYm91bmRhcnlR

@ -0,0 +1,6 @@
----boundary
Content-Disposition: form-data; name="field"
QQQQQQQQQQQQQQQQQQQQ
----boundaryQQQQQQQQQQQQQQQQQQQQ
----boundary--

@ -0,0 +1,8 @@
boundary: --boundary
expected:
- name: field
type: field
data: !!binary |
UVFRUVFRUVFRUVFRUVFRUVFRUVENCi0tLS1ib3VuZGFyeVFRUVFRUVFRUVFRUVFRUVFRUVFR

@ -0,0 +1,6 @@
----boundary
Content-Disposition: form-data; name="field"
QQQQQQQQQQQQQQQQQQQQ
----boundary QQQQQQQQQQQQQQQQQQQQ
----boundary--

@ -0,0 +1,8 @@
boundary: --boundary
expected:
- name: field
type: field
data: !!binary |
UVFRUVFRUVFRUVFRUVFRUVFRUVENCi0tLS1ib3VuZGFyeQ1RUVFRUVFRUVFRUVFRUVFRUVFRUQ==

@ -0,0 +1,6 @@
----boundary
Content-Disposition: form-data; name="field"
QQQQQQQQQQQQQQQQQQQQ
----boundary-QQQQQQQQQQQQQQQQQQQQ
----boundary--

@ -0,0 +1,8 @@
boundary: --boundary
expected:
- name: field
type: field
data: !!binary |
UVFRUVFRUVFRUVFRUVFRUVFRUVENCi0tLS1ib3VuZGFyeS1RUVFRUVFRUVFRUVFRUVFRUVFRUQ==

@ -0,0 +1,4 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content-Disposition: form-data; name="field"
QThis is a test.
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,5 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content-999position: form-data; name="field"
This is a test.
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,3 @@
boundary: ----WebKitFormBoundaryTkr3kCBQlBe1nrhc
expected:
error: 50

@ -0,0 +1,5 @@
------WebQitFormBoundaryTkr3kCBQlBe1nrhc
Content-Disposition: form-data; name="field"
This is a test.
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,7 @@
----boundary
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
Content-Transfer-Encoding: base64
VGVzdCAxMjM=
----boundary--

@ -0,0 +1,7 @@
boundary: --boundary
expected:
- name: file
type: file
file_name: test.txt
data: !!binary |
VGVzdCAxMjM=

@ -0,0 +1,5 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
: form-data; name="field"
This is a test.
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,3 @@
boundary: ----WebKitFormBoundaryTkr3kCBQlBe1nrhc
expected:
error: 42

@ -0,0 +1,9 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content-Disposition: form-data; name="field1"
field1
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content-Disposition: form-data; name="field2"
field2
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,10 @@
boundary: ----WebKitFormBoundaryTkr3kCBQlBe1nrhc
expected:
- name: field1
type: field
data: !!binary |
ZmllbGQx
- name: field2
type: field
data: !!binary |
ZmllbGQy

@ -0,0 +1,11 @@
------WebKitFormBoundarygbACTUR58IyeurVf
Content-Disposition: form-data; name="file1"; filename="test1.txt"
Content-Type: text/plain
Test file #1
------WebKitFormBoundarygbACTUR58IyeurVf
Content-Disposition: form-data; name="file2"; filename="test2.txt"
Content-Type: text/plain
Test file #2
------WebKitFormBoundarygbACTUR58IyeurVf--

@ -0,0 +1,13 @@
boundary: ----WebKitFormBoundarygbACTUR58IyeurVf
expected:
- name: file1
type: file
file_name: test1.txt
data: !!binary |
VGVzdCBmaWxlICMx
- name: file2
type: file
file_name: test2.txt
data: !!binary |
VGVzdCBmaWxlICMy

@ -0,0 +1,7 @@
----boundary
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable
foo=3Dbar
----boundary--

@ -0,0 +1,7 @@
boundary: --boundary
expected:
- name: file
type: file
file_name: test.txt
data: !!binary |
Zm9vPWJhcg==

@ -0,0 +1,5 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content-Disposition: form-data; name="field"
This is a test.
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,6 @@
boundary: ----WebKitFormBoundaryTkr3kCBQlBe1nrhc
expected:
- name: field
type: field
data: !!binary |
VGhpcyBpcyBhIHRlc3Qu

@ -0,0 +1,5 @@
--boundary
Content-Disposition: form-data; name="field"
0123456789ABCDEFGHIJ0123456789ABCDEFGHIJ
--boundary--

@ -0,0 +1,6 @@
boundary: --boundary
expected:
- name: field
type: field
data: !!binary |
MDEyMzQ1Njc4OUFCQ0RFRkdISUowMTIzNDU2Nzg5QUJDREVGR0hJSg==

@ -0,0 +1,5 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content-Disposition: form-data; name="field"
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,6 @@
boundary: ----WebKitFormBoundaryTkr3kCBQlBe1nrhc
expected:
- name: field
type: field
data: !!binary |
cXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXE=

@ -0,0 +1,10 @@
--boundary
Content-Disposition: form-data; name="field"
test1
--boundary
Content-Disposition: form-data; name="file"; filename="file.txt"
Content-Type: text/plain
test2
--boundary--

@ -0,0 +1,13 @@
boundary: boundary
expected:
- name: field
type: field
data: !!binary |
dGVzdDE=
- name: file
type: file
file_name: file.txt
data: !!binary |
dGVzdDI=

@ -0,0 +1,7 @@
------WebKitFormBoundaryTkr3kCBQlBe1nrhc
Content-Disposition: form-data; name="field"
This is a test.
------WebKitFormBoundaryTkr3kCBQlBe1nrhc--

@ -0,0 +1,6 @@
boundary: ----WebKitFormBoundaryTkr3kCBQlBe1nrhc
expected:
- name: field
type: field
data: !!binary |
VGhpcyBpcyBhIHRlc3Qu

@ -0,0 +1,6 @@
------WebKitFormBoundary5BZGOJCWtXGYC9HW
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
This is a test file.
------WebKitFormBoundary5BZGOJCWtXGYC9HW--

@ -0,0 +1,8 @@
boundary: ----WebKitFormBoundary5BZGOJCWtXGYC9HW
expected:
- name: file
type: file
file_name: test.txt
data: !!binary |
VGhpcyBpcyBhIHRlc3QgZmlsZS4=

@ -0,0 +1,6 @@
------WebKitFormBoundaryI9SCEFp2lpx5DR2K
Content-Disposition: form-data; name="file"; filename="???.txt"
Content-Type: text/plain
これはテストです。
------WebKitFormBoundaryI9SCEFp2lpx5DR2K--

@ -0,0 +1,8 @@
boundary: ----WebKitFormBoundaryI9SCEFp2lpx5DR2K
expected:
- name: file
type: file
file_name: ???.txt
data: !!binary |
44GT44KM44Gv44OG44K544OI44Gn44GZ44CC

@ -0,0 +1,69 @@
Metadata-Version: 2.1
Name: python-multipart
Version: 0.0.6
Summary: A streaming multipart parser for Python
Project-URL: Homepage, https://github.com/andrew-d/python-multipart
Project-URL: Documentation, https://andrew-d.github.io/python-multipart/
Project-URL: Changelog, https://github.com/andrew-d/python-multipart/tags
Project-URL: Source, https://github.com/andrew-d/python-multipart
Author-email: Andrew Dunham <andrew@du.nham.ca>
License-Expression: Apache-2.0
License-File: LICENSE.txt
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.7
Provides-Extra: dev
Requires-Dist: atomicwrites==1.2.1; extra == 'dev'
Requires-Dist: attrs==19.2.0; extra == 'dev'
Requires-Dist: coverage==6.5.0; extra == 'dev'
Requires-Dist: hatch; extra == 'dev'
Requires-Dist: invoke==1.7.3; extra == 'dev'
Requires-Dist: more-itertools==4.3.0; extra == 'dev'
Requires-Dist: pbr==4.3.0; extra == 'dev'
Requires-Dist: pluggy==1.0.0; extra == 'dev'
Requires-Dist: py==1.11.0; extra == 'dev'
Requires-Dist: pytest-cov==4.0.0; extra == 'dev'
Requires-Dist: pytest-timeout==2.1.0; extra == 'dev'
Requires-Dist: pytest==7.2.0; extra == 'dev'
Requires-Dist: pyyaml==5.1; extra == 'dev'
Description-Content-Type: text/x-rst
==================
Python-Multipart
==================
.. image:: https://github.com/andrew-d/python-multipart/actions/workflows/test.yaml/badge.svg
:target: https://github.com/andrew-d/python-multipart/actions
python-multipart is an Apache2 licensed streaming multipart parser for Python.
Test coverage is currently 100%.
Documentation is available `here`_.
.. _here: https://andrew-d.github.io/python-multipart/
Why?
----
Because streaming uploads are awesome for large files.
How to Test
-----------
If you want to test:
.. code-block:: bash
$ pip install .[dev]
$ inv test

@ -0,0 +1,62 @@
multipart/__init__.py,sha256=EaZd7hXXXNz5RWfzZ4lr-wKWXC4anMNWE7u4tPXtWr0,335
multipart/__pycache__/__init__.cpython-37.pyc,,
multipart/__pycache__/decoders.cpython-37.pyc,,
multipart/__pycache__/exceptions.cpython-37.pyc,,
multipart/__pycache__/multipart.cpython-37.pyc,,
multipart/decoders.py,sha256=6LeCVARmDrQgmMsaul1WUIf79Q-mLE9swhGxumQe_98,6107
multipart/exceptions.py,sha256=yDZ9pqq3Y9ZMCvj2TkAvOcNdMjFHjLnHl4luFnzt750,1410
multipart/multipart.py,sha256=ZRc1beZCgCIXkYe0Xwxh_g4nFdrp3eEid4XODYIfqgQ,71230
multipart/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
multipart/tests/__pycache__/__init__.cpython-37.pyc,,
multipart/tests/__pycache__/compat.cpython-37.pyc,,
multipart/tests/__pycache__/test_multipart.cpython-37.pyc,,
multipart/tests/compat.py,sha256=3aowcimO1SYU6WqS3GlUJ3jmkgLH63e8AsUPjlta1xU,4266
multipart/tests/test_data/http/CR_in_header.http,sha256=XEimN_BgEqQXCqK463bMgD9PKIQeLrQhWt2M3vNr9cE,149
multipart/tests/test_data/http/CR_in_header.yaml,sha256=OEzE2PqK78fi9kjM23YOu4xM0zQ_LRwSiwqFNAmku50,73
multipart/tests/test_data/http/CR_in_header_value.http,sha256=pf4sP-l4_hzZ8Kr51gUE6CFcCifuWSZ10-vnx6mtXDg,149
multipart/tests/test_data/http/CR_in_header_value.yaml,sha256=WjqJNYL-cUH2n9k-Xdy1YDvSfDqqXxsiinBDn3HTUu4,73
multipart/tests/test_data/http/almost_match_boundary.http,sha256=jIsp1M6BHQIHF9o965z3Pt8TFncVvaBj5N43hprRpBM,264
multipart/tests/test_data/http/almost_match_boundary.yaml,sha256=Hr7WZBwZrbf4vjurjRzGGeY9tFVJLRRmV1rEFXop-6s,300
multipart/tests/test_data/http/almost_match_boundary_without_CR.http,sha256=KviMqo_FUy1N1-b-YUfyWhs5PmN6_fU7qhMYFTGnUhI,132
multipart/tests/test_data/http/almost_match_boundary_without_CR.yaml,sha256=HjlUni-nuX3bG2-3FILo4GLBpLD4DImQ48VPlfnfIWY,167
multipart/tests/test_data/http/almost_match_boundary_without_LF.http,sha256=KylmJ0O-RfnUnXbjVhwJpzHsWqNTPJn29_wfsvrG7AM,133
multipart/tests/test_data/http/almost_match_boundary_without_LF.yaml,sha256=tkzz_kOFZtkarmMnTen355nm8McPwbmPmWGMxUUBSzU,171
multipart/tests/test_data/http/almost_match_boundary_without_final_hyphen.http,sha256=L6bzRistD4X5TTd1zBtfR6gM4EQL77_iBI_Pgaw4ufw,133
multipart/tests/test_data/http/almost_match_boundary_without_final_hyphen.yaml,sha256=cFKxwFMYTo9PKRb04Iai__mY9KG29IPkSm3p80DgEZw,171
multipart/tests/test_data/http/bad_end_of_headers.http,sha256=ucEDylTCg1_hdEVkIc-1k8ZQ-CBIf5uXfDKbSBsSaF0,149
multipart/tests/test_data/http/bad_end_of_headers.yaml,sha256=1UHERY2D7tp0HEUl5xD4SiotP2skETmBOF5EjcG2HTw,73
multipart/tests/test_data/http/bad_header_char.http,sha256=zTqXFNQ9yrbc82vubPg95T4edg1Ueh2xadlVD2lO51A,149
multipart/tests/test_data/http/bad_header_char.yaml,sha256=9ykVsASnvYvX51qtkCJqhgegeN-hoSU40MsYQvqeVNo,73
multipart/tests/test_data/http/bad_initial_boundary.http,sha256=IGFSkpmw21XfAXr0xOHwj0vnhxyj-uCWVjcljo68LLo,149
multipart/tests/test_data/http/bad_initial_boundary.yaml,sha256=eBSbue0BYDYhYtKdBCnm1LGq0O_fOMwV6ZoLpZFDFM4,72
multipart/tests/test_data/http/base64_encoding.http,sha256=fDbr4BgLdNS8kYiTO7g4HxB81hvmiD2sRUCAoijfRx0,173
multipart/tests/test_data/http/base64_encoding.yaml,sha256=cz2KxZxoi81MiXRh7DmJQOWcdqQH5ahkrJydGYv4hpU,125
multipart/tests/test_data/http/empty_header.http,sha256=-wSHHSLu1D2wfdC8Zcaw5TX_USTvWz56CANpsceOZYQ,130
multipart/tests/test_data/http/empty_header.yaml,sha256=4xdVCYJ-l88HMXkMLNkSQoLNgURoGcKzR1AclPLpkOc,73
multipart/tests/test_data/http/multiple_fields.http,sha256=6p93ls_B7bk8mXPYhsrFwvktSX8CuRdUH4vn-EZBaRM,242
multipart/tests/test_data/http/multiple_fields.yaml,sha256=mePM5DVfAzty7QNEEyMu2qrFI28TbG9yWRvWFpWj7Jo,197
multipart/tests/test_data/http/multiple_files.http,sha256=EtmagVBVpsFGnCqlwfKgswQfU8lGa3QNkP6GVJBa5A0,348
multipart/tests/test_data/http/multiple_files.yaml,sha256=QO9JMgTvkL2EmIWAl8LcbDrkfNmDk0eA5SOk3gFuFWE,260
multipart/tests/test_data/http/quoted_printable_encoding.http,sha256=--yYceg17SmqIJsazw-SFChdxeTAq8zV4lzPVM_QMrM,180
multipart/tests/test_data/http/quoted_printable_encoding.yaml,sha256=G_L6lnP-e4uHfGpYQFopxDdpbd_EbxL2oY8N910BTOI,127
multipart/tests/test_data/http/single_field.http,sha256=JjdSwFiM0mG07HYzBCcjzeqgqAA9glx-VcRUjkOh8cA,149
multipart/tests/test_data/http/single_field.yaml,sha256=HMXd14-m9sKBvTsnzWOaG12_3wve5SoXeUISF93wlRc,139
multipart/tests/test_data/http/single_field_blocks.http,sha256=4laZAIbFmxERZtgPWzuOihvEhLWD1NGTSdqZ6Ra58Ns,115
multipart/tests/test_data/http/single_field_blocks.yaml,sha256=6mKvHtmiXh6OxoibJsx5pUreIMyQyPb_DWy7GEG9BX8,147
multipart/tests/test_data/http/single_field_longer.http,sha256=BTBt1MsUaxuHauu-mljb3lU-8Z2dpjRN_lkZW4pkDXA,262
multipart/tests/test_data/http/single_field_longer.yaml,sha256=aENhQPtHaTPIvgJbdiDHvcOtcthEEUHCQIEfLj0aalY,293
multipart/tests/test_data/http/single_field_single_file.http,sha256=G4dV0iCSjvEk5DSJ1VXWy6R8Hon3-WOExep41nPWVeQ,192
multipart/tests/test_data/http/single_field_single_file.yaml,sha256=QO9gqdXQsoizLji9r8kdlPWHJB5vO7wszqP1fHvsNV8,189
multipart/tests/test_data/http/single_field_with_leading_newlines.http,sha256=YfNEUdZxbi4bBGTU4T4WSQZ6QJDJlcLZUczYzGU5Jaw,153
multipart/tests/test_data/http/single_field_with_leading_newlines.yaml,sha256=HMXd14-m9sKBvTsnzWOaG12_3wve5SoXeUISF93wlRc,139
multipart/tests/test_data/http/single_file.http,sha256=axRB0Keb4uhAfHxt7Na1x9-PQHCiiKK8s38a2GG860E,202
multipart/tests/test_data/http/single_file.yaml,sha256=eUKyGkNTDrXdGni4EyEDbxDBTfAKsstVQ5O5SWghYTc,170
multipart/tests/test_data/http/utf8_filename.http,sha256=w_Ryf4hC_KJo7v-a18dJFECqm21nzA5Z18dsGyu6zjA,208
multipart/tests/test_data/http/utf8_filename.yaml,sha256=KpDc4e-yYp_JUXa-S5lp591tzoEybgywtGian0kQFPc,177
multipart/tests/test_multipart.py,sha256=VrxoOtXO4NWpT1OJqo7FWWIybnxGReumIWCR-FDIHCk,38988
python_multipart-0.0.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
python_multipart-0.0.6.dist-info/METADATA,sha256=J4WQf99XHSSg_EDG7fGgJGotS_Hp7ViCtpY4rQ2OgyM,2459
python_multipart-0.0.6.dist-info/RECORD,,
python_multipart-0.0.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
python_multipart-0.0.6.dist-info/WHEEL,sha256=Fd6mP6ydyRguakwUJ05oBE7fh2IPxgtDN9IwHJ9OqJQ,87
python_multipart-0.0.6.dist-info/licenses/LICENSE.txt,sha256=qOgzF2zWF9rwC51tOfoVyo7evG0WQwec0vSJPAwom-I,556

@ -0,0 +1,4 @@
Wheel-Version: 1.0
Generator: hatchling 1.13.0
Root-Is-Purelib: true
Tag: py3-none-any

@ -0,0 +1,14 @@
Copyright 2012, Andrew Dunham
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -67,7 +67,8 @@ setup(name='pyOpenRPA',
'Jinja2>=2.2.11.2', 'Jinja2>=2.2.11.2',
'selenium>=3.141.0', 'selenium>=3.141.0',
'fastapi>=0.81.0', 'fastapi>=0.81.0',
'uvicorn>=0.18.3' 'uvicorn>=0.18.3',
'python-multipart>=0.0.6'
], ],
extras_require={ extras_require={
':sys_platform == "win32"': [ ':sys_platform == "win32"': [

Loading…
Cancel
Save