Skip to content

Commit

Permalink
New dispatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
pylover committed Dec 13, 2017
1 parent 5f719fd commit 20b3bc5
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 48 deletions.
1 change: 0 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Features
- Url-Encoded, Multipart and JSON form parsing.
- No ``request`` and or ``response`` objects is available, everything is combined in ``nanohttp.context``.
- A very flexible configuration system: `pymlconf <https://github.com/pylover/pymlconf>`_
- Dispatching arguments using the `obj.__annonations__ <https://docs.python.org/3/library/typing.html>`_
- Method(verb) dispatcher.

Roadmap
Expand Down
2 changes: 1 addition & 1 deletion nanohttp/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __call__(self, environ, start_response):
remaining_paths = path.split('/') if path else []

# Calling the controller, actually this will be serve our request
response_body = self.__root__(remaining_paths)
response_body = self.__root__(*remaining_paths)

if response_body:
# The goal is to yield an iterable, to encode and iter over it at the end of this method.
Expand Down
5 changes: 4 additions & 1 deletion nanohttp/contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ def response_content_type(self):

@response_content_type.setter
def response_content_type(self, v):
self.response_headers['Content-Type'] = '%s; charset=%s' % (v, self.response_encoding)
if v is None:
del self.response_headers['Content-Type']
else:
self.response_headers['Content-Type'] = '%s; charset=%s' % (v, self.response_encoding)

@classmethod
def get_current(cls):
Expand Down
93 changes: 49 additions & 44 deletions nanohttp/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

logging.basicConfig(level=logging.INFO)

UNLIMITED = -1

class Controller(object):
__nanohttp__ = dict(
Expand All @@ -20,70 +21,74 @@ class Controller(object):
default_action='index'
)

def _dispatch(self, remaining_paths):
if len(remaining_paths) == 0:
path = self.__nanohttp__['default_action']
else:
path = remaining_paths.pop(0)

# Ensuring the handler
handler = getattr(self, path, None)
if handler is None:
handler = getattr(self, self.__nanohttp__['default_action'], None)
if handler is not None:
remaining_paths = (path, ) + remaining_paths

args_count = len(remaining_paths)
if (handler is None or not hasattr(handler, '__nanohttp__')) \
or (hasattr(handler, '__argcount__') and handler.__argcount__ < args_count) \
or (hasattr(handler, '__annotations__') and len(handler.__annotations__) < args_count):
def _get_default_handler(self, remaining_paths):
default_action = self.__nanohttp__['default_action']
handler = getattr(self, default_action, None)
if not handler:
raise HttpNotFound()

if 'any' != handler.__http_methods__ and context.method not in handler.__http_methods__:
return handler, remaining_paths

def _find_handler(self, remaining_paths):
if not remaining_paths or not hasattr(self, remaining_paths[0]):
# Handler is not found, trying default handler
return self._get_default_handler(remaining_paths)

return getattr(self, remaining_paths[0], None), remaining_paths[1:]

# noinspection PyMethodMayBeStatic
def _validate_handler(self, handler, remaining_paths):
if not callable(handler) or not hasattr(handler, '__nanohttp__'):
raise HttpNotFound()

# noinspection PyUnresolvedReferences
manifest = handler.__nanohttp__
positionals = manifest.get('positional_arguments', UNLIMITED)
optionals = manifest.get('optional_arguments', UNLIMITED)
available_arguments = len(remaining_paths)
verbs = manifest.get('verbs', 'any')

if UNLIMITED not in (optionals, positionals) and \
(positionals > available_arguments or available_arguments > (positionals + optionals)):
raise HttpNotFound()

if verbs is not 'any' and context.method not in verbs:
raise HttpMethodNotAllowed()

return handler, remaining_paths

# noinspection PyMethodMayBeStatic
def _serve_handler(self, handler, remaining_paths):
context.response_encoding = handler.__nanohttp__['encoding']
context.response_content_type = handler.__nanohttp__['content_type']
context.response_encoding = handler.__nanohttp__.get('encoding', None)
context.response_content_type = handler.__nanohttp__.get('content_type', None)
return handler(*remaining_paths)

def __call__(self, remaining_paths):
handler, remaining_paths = self._dispatch(remaining_paths)
def __call__(self, *remaining_paths):
handler, remaining_paths = self._find_handler(list(remaining_paths))
handler, remaining_paths = self._validate_handler(handler, remaining_paths)
return self._serve_handler(handler, remaining_paths)


class RestController(Controller):

def _dispatch(self, *remaining_paths):
handler = None
if remaining_paths:
first_path = remaining_paths[0]
if hasattr(self, first_path):
remaining_paths = remaining_paths[1:]
handler = getattr(self, first_path)

if handler is None:
def _find_handler(self, remaining_paths):
if remaining_paths and hasattr(self, remaining_paths[0]):
return getattr(self, remaining_paths[0], None), remaining_paths[1:]

if not hasattr(self, context.method):
raise HttpMethodNotAllowed()
else:
handler = getattr(self, context.method)

if handler is None or not hasattr(handler, '__http_methods__'):
raise HttpNotFound()

# FIXME: check argcount
if hasattr(handler, '__annotations__') and len(handler.__annotations__) < len(remaining_paths):
raise HttpNotFound()
# Handler is not found, trying verb
if not hasattr(self, context.method):
raise HttpMethodNotAllowed()

return handler, remaining_paths
return getattr(self, context.method), remaining_paths


class Static(Controller):
__response_encoding__ = None
__nanohttp__ = dict(
verbs='any',
encoding=None,
default_action='index'
)

__chunk_size__ = 0x4000

def __init__(self, directory='.', default_document='index.html'):
Expand Down
2 changes: 1 addition & 1 deletion nanohttp/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ def decorator(func):
verbs=verbs,
encoding=encoding,
content_type=content_type,
optional_arguments=optional_arguments,
positional_arguments=positional_arguments,
optional_arguments=optional_arguments,
default_action='index'
)

Expand Down

0 comments on commit 20b3bc5

Please sign in to comment.