diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index 25205528e59..d75a5f712b1 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -7,12 +7,14 @@ from .connector import * from .client import * from .errors import * +from .helpers import * from .parsers import * from .streams import * __all__ = (client.__all__ + errors.__all__ + + helpers.__all__ + parsers.__all__ + protocol.__all__ + connector.__all__ + diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 874ce28b7a4..983efac410c 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -1,13 +1,17 @@ """Various helper functions""" +__all__ = ['BasicAuth', 'FormData', 'parse_mimetype'] + import base64 import io import os import uuid import urllib.parse from collections import namedtuple +from wsgiref.handlers import format_date_time class BasicAuth(namedtuple('BasicAuth', ['login', 'password', 'encoding'])): + """Http basic authentication helper.""" def __new__(cls, login, password='', encoding='latin1'): if login is None: @@ -19,12 +23,14 @@ def __new__(cls, login, password='', encoding='latin1'): return super().__new__(cls, login, password, encoding) def encode(self): + """Encode credentials.""" creds = ('%s:%s' % (self.login, self.password)).encode(self.encoding) return 'Basic %s' % base64.b64encode(creds).decode(self.encoding) class FormData: - """Generate multipart/form-data body.""" + """Helper class for multipart/form-data and + application/x-www-form-urlencoded body generation.""" def __init__(self, fields): self._fields = [] @@ -176,3 +182,60 @@ def guess_filename(obj, default=None): if name and name[0] != '<' and name[-1] != '>': return os.path.split(name)[-1] return default + + +def atoms(message, environ, response, request_time): + """Gets atoms for log formatting.""" + if message: + r = '{} {} HTTP/{}.{}'.format( + message.method, message.path, + message.version[0], message.version[1]) + else: + r = '' + + atoms = { + 'h': environ.get('REMOTE_ADDR', '-'), + 'l': '-', + 'u': '-', + 't': format_date_time(None), + 'r': r, + 's': str(response.status), + 'b': str(response.output_length), + 'f': environ.get('HTTP_REFERER', '-'), + 'a': environ.get('HTTP_USER_AGENT', '-'), + 'T': str(int(request_time)), + 'D': str(request_time).split('.', 1)[-1][:5], + 'p': "<%s>" % os.getpid() + } + + return atoms + + +class SafeAtoms(dict): + """Copy from gunicorn""" + + def __init__(self, atoms, i_headers, o_headers): + dict.__init__(self) + + self._i_headers = i_headers + self._o_headers = o_headers + + for key, value in atoms.items(): + self[key] = value.replace('"', '\\"') + + def __getitem__(self, k): + if k.startswith('{'): + if k.endswith('}i'): + headers = self._i_headers + elif k.endswith('}o'): + headers = self._o_headers + else: + headers = None + + if headers is not None: + return headers.get(k[1:-2], '-') + + if k in self: + return super(SafeAtoms, self).__getitem__(k) + else: + return '-' diff --git a/aiohttp/server.py b/aiohttp/server.py index 4c169fa7a5b..2c05657b569 100644 --- a/aiohttp/server.py +++ b/aiohttp/server.py @@ -9,7 +9,7 @@ import socket import aiohttp -from aiohttp import errors, streams, utils +from aiohttp import errors, streams, helpers from aiohttp.log import server_log, access_log @@ -116,8 +116,8 @@ def log_access(self, message, environ, response, time): if self.access_log and self.access_log_format: try: environ = environ if environ is not None else {} - atoms = utils.SafeAtoms( - utils.atoms(message, environ, response, time), + atoms = helpers.SafeAtoms( + helpers.atoms(message, environ, response, time), getattr(message, 'headers', None), getattr(response, 'headers', None)) self.access_log.info(self.access_log_format % atoms) diff --git a/aiohttp/utils.py b/aiohttp/utils.py deleted file mode 100644 index 39d368c62ab..00000000000 --- a/aiohttp/utils.py +++ /dev/null @@ -1,59 +0,0 @@ -import os -from wsgiref.handlers import format_date_time - - -def atoms(message, environ, response, request_time): - """Gets atoms for log formatting.""" - if message: - r = '{} {} HTTP/{}.{}'.format( - message.method, message.path, - message.version[0], message.version[1]) - else: - r = '' - - atoms = { - 'h': environ.get('REMOTE_ADDR', '-'), - 'l': '-', - 'u': '-', - 't': format_date_time(None), - 'r': r, - 's': str(response.status), - 'b': str(response.output_length), - 'f': environ.get('HTTP_REFERER', '-'), - 'a': environ.get('HTTP_USER_AGENT', '-'), - 'T': str(int(request_time)), - 'D': str(request_time).split('.', 1)[-1][:5], - 'p': "<%s>" % os.getpid() - } - - return atoms - - -class SafeAtoms(dict): - """Copy from gunicorn""" - - def __init__(self, atoms, i_headers, o_headers): - dict.__init__(self) - - self._i_headers = i_headers - self._o_headers = o_headers - - for key, value in atoms.items(): - self[key] = value.replace('"', '\\"') - - def __getitem__(self, k): - if k.startswith('{'): - if k.endswith('}i'): - headers = self._i_headers - elif k.endswith('}o'): - headers = self._o_headers - else: - headers = None - - if headers is not None: - return headers.get(k[1:-2], '-') - - if k in self: - return super(SafeAtoms, self).__getitem__(k) - else: - return '-' diff --git a/docs/api.rst b/docs/api.rst index ae399e72a3a..92dbec0508c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -30,6 +30,14 @@ aiohttp.errors module :undoc-members: :show-inheritance: +aiohttp.helpers module +--------------------- + +.. automodule:: aiohttp.helpers + :members: + :undoc-members: + :show-inheritance: + aiohttp.parsers module ---------------------- @@ -54,10 +62,10 @@ aiohttp.server module :undoc-members: :show-inheritance: -aiohttp.utils module --------------------- +aiohttp.streams module +--------------------- -.. automodule:: aiohttp.utils +.. automodule:: aiohttp.streams :members: :undoc-members: :show-inheritance: diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 0d3180ac7b9..7bfeb5a140c 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -2,6 +2,7 @@ import unittest.mock from aiohttp import helpers +from aiohttp import multidict class HelpersTests(unittest.TestCase): @@ -52,3 +53,22 @@ def test_basic_auth(self): self.assertEqual(auth.login, 'nkim') self.assertEqual(auth.password, 'pwd') self.assertEqual(auth.encode(), 'Basic bmtpbTpwd2Q=') + + +class SafeAtomsTests(unittest.TestCase): + + def test_get_non_existing(self): + atoms = helpers.SafeAtoms( + {}, multidict.MultiDict(), multidict.MultiDict()) + self.assertEqual(atoms['unknown'], '-') + + def test_get_lower(self): + i_headers = multidict.MultiDict([('test', '123')]) + o_headers = multidict.MultiDict([('TEST', '123')]) + + atoms = helpers.SafeAtoms({}, i_headers, o_headers) + self.assertEqual(atoms['{test}i'], '123') + self.assertEqual(atoms['{test}o'], '-') + self.assertEqual(atoms['{TEST}o'], '123') + self.assertEqual(atoms['{UNKNOWN}o'], '-') + self.assertEqual(atoms['{UNKNOWN}'], '-') diff --git a/tests/test_utils.py b/tests/test_utils.py deleted file mode 100644 index 4486734e04f..00000000000 --- a/tests/test_utils.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Tests for aiohttp/utils.py""" -import unittest -import unittest.mock - -from aiohttp import utils -from aiohttp import multidict - - -class SafeAtomsTests(unittest.TestCase): - - def test_get_non_existing(self): - atoms = utils.SafeAtoms( - {}, multidict.MultiDict(), multidict.MultiDict()) - self.assertEqual(atoms['unknown'], '-') - - def test_get_lower(self): - i_headers = multidict.MultiDict([('test', '123')]) - o_headers = multidict.MultiDict([('TEST', '123')]) - - atoms = utils.SafeAtoms({}, i_headers, o_headers) - self.assertEqual(atoms['{test}i'], '123') - self.assertEqual(atoms['{test}o'], '-') - self.assertEqual(atoms['{TEST}o'], '123') - self.assertEqual(atoms['{UNKNOWN}o'], '-') - self.assertEqual(atoms['{UNKNOWN}'], '-')