Skip to content

Commit

Permalink
Merge pull request #288 from kxepal/update-multipart
Browse files Browse the repository at this point in the history
Update multipart
  • Loading branch information
fafhrd91 committed Mar 12, 2015
2 parents 3fdf015 + 51df895 commit 042a779
Show file tree
Hide file tree
Showing 6 changed files with 382 additions and 36 deletions.
59 changes: 28 additions & 31 deletions aiohttp/multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ def is_quoted(string):
return string[0] == string[-1] == '"'

def is_rfc5987(string):
# this isn't very correct
return "''" in string
return is_token(string) and string.count("'") == 2

def is_extended_param(string):
return string.endswith('*')
Expand Down Expand Up @@ -103,20 +102,12 @@ def unescape(text, *, chars=''.join(map(re.escape, CHAR))):
continue

elif is_extended_param(key):
if is_quoted(value):
warnings.warn(BadContentDispositionParam(item))
continue
elif is_rfc5987(value):
if is_rfc5987(value):
encoding, _, value = value.split("'", 2)
encoding = encoding or 'utf-8'
elif "'":
warnings.warn(BadContentDispositionParam(item))
continue
elif not is_token(value):
else:
warnings.warn(BadContentDispositionParam(item))
continue
else:
encoding = 'utf-8'

try:
value = unquote(value, encoding, 'strict')
Expand Down Expand Up @@ -569,17 +560,16 @@ def __init__(self, obj, headers=None, *, chunk_size=8192):
}

def _fill_headers_with_defaults(self):
"""Updates part headers by """
if CONTENT_LENGTH not in self.headers:
content_length = self._guess_content_length(self.obj)
if content_length is not None:
self.headers[CONTENT_LENGTH] = str(content_length)

if CONTENT_TYPE not in self.headers:
content_type = self._guess_content_type(self.obj)
if content_type is not None:
self.headers[CONTENT_TYPE] = content_type

if CONTENT_LENGTH not in self.headers:
content_length = self._guess_content_length(self.obj)
if content_length is not None:
self.headers[CONTENT_LENGTH] = str(content_length)

if CONTENT_DISPOSITION not in self.headers:
filename = self._guess_filename(self.obj)
if filename is not None:
Expand All @@ -588,12 +578,20 @@ def _fill_headers_with_defaults(self):
def _guess_content_length(self, obj):
if isinstance(obj, bytes):
return len(obj)
elif isinstance(obj, str):
*_, params = parse_mimetype(self.headers.get(CONTENT_TYPE))
charset = params.get('charset', 'us-ascii')
return len(obj.encode(charset))
elif isinstance(obj, io.StringIO):
*_, params = parse_mimetype(self.headers.get(CONTENT_TYPE))
charset = params.get('charset', 'us-ascii')
return len(obj.getvalue().encode(charset)) - obj.tell()
elif isinstance(obj, io.BytesIO):
return len(obj.getvalue()) - obj.tell()
elif isinstance(obj, io.IOBase):
try:
return os.fstat(obj.fileno()).st_size - obj.tell()
except (AttributeError, OSError):
if isinstance(obj, io.BytesIO):
return len(obj.getvalue()) - obj.tell()
return None
else:
return None
Expand All @@ -602,7 +600,7 @@ def _guess_content_type(self, obj, default='application/octet-stream'):
if hasattr(obj, 'name'):
name = getattr(obj, 'name')
return mimetypes.guess_type(name)[0]
elif isinstance(obj, str):
elif isinstance(obj, (str, io.StringIO)):
return 'text/plain; charset=utf-8'
else:
return default
Expand Down Expand Up @@ -635,21 +633,20 @@ def serialize(self):
for item in self.headers.items()
)
yield b'\r\n\r\n'
yield from self._maybe_encode_stream(self._serialize_obj())
yield b'\r\n'

def _serialize_obj(self):
obj = self.obj
mtype, stype, *_ = parse_mimetype(self.headers.get(CONTENT_TYPE))
serializer = self._serialize_map.get((mtype, stype))
if serializer is not None:
stream = serializer(obj)
else:
for key in self._serialize_map:
if not isinstance(key, tuple) and isinstance(obj, key):
stream = self._serialize_map[key](obj)
break
else:
stream = self._serialize_default(obj)
yield from self._maybe_encode_stream(stream)
yield b'\r\n'
return serializer(obj)

for key in self._serialize_map:
if not isinstance(key, tuple) and isinstance(obj, key):
return self._serialize_map[key](obj)
return self._serialize_default(obj)

def _serialize_bytes(self, obj):
yield obj
Expand Down
12 changes: 11 additions & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
.. _aiohttp-api:

Helpers API
===========

All public names from submodules ``client``, ``connector``,
``errors``, ``parsers``, ``protocol``, ``server``, ``utils``,
``errors``, ``multipart``, ``parsers``, ``protocol``, ``server``, ``utils``,
``websocket`` and ``wsgi`` are exported into ``aiohttp``
namespace.

Expand Down Expand Up @@ -38,6 +40,14 @@ aiohttp.helpers module
:undoc-members:
:show-inheritance:

aiohttp.multipart module
------------------------

.. automodule:: aiohttp.multipart
:members:
:undoc-members:
:show-inheritance:

aiohttp.parsers module
----------------------

Expand Down
1 change: 1 addition & 0 deletions docs/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ If you pass file object as data parameter, aiohttp will stream it to server
automatically. Check :class:`aiohttp.stream.StreamReader` for supported format
information.

.. seealso:: :ref:`aiohttp-multipart`

Streaming uploads
-----------------
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ Contents:
web_reference
server
multidict
multipart
api
gunicorn
contributing
Expand Down
Loading

0 comments on commit 042a779

Please sign in to comment.