Skip to content

Commit

Permalink
reformated, called_with test decorator added
Browse files Browse the repository at this point in the history
  • Loading branch information
petr-s committed Mar 8, 2016
1 parent 29c3feb commit c11679f
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 17 deletions.
31 changes: 21 additions & 10 deletions clearest/core.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
from abc import ABCMeta, abstractmethod
from collections import defaultdict
import functools
import inspect
import re
from abc import ABCMeta, abstractmethod
from collections import defaultdict
from copy import deepcopy

try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse

import six

from clearest.exceptions import MissingArgumentError, AlreadyRegisteredError, NotUniqueError, HttpError, HttpBadRequest, \
from clearest.exceptions import MissingArgumentError, AlreadyRegisteredError, NotUniqueError, HttpError, \
HttpNotFound
from clearest.http import HTTP_GET, HTTP_POST, CONTENT_TYPE, MIME_TEXT_PLAIN, HTTP_OK
from clearest.wsgi import REQUEST_METHOD, PATH_INFO, QUERY_STRING

KEY_PATTERN = re.compile("\{(.*)\}")
STATUS_FMT = "{code} {msg}"


class Key(object):
def __init__(self, name, pre):
self.name = name
Expand All @@ -29,10 +32,11 @@ def __eq__(self, other):
def __hash__(self):
return hash(self.pre)


def parse_path(path):
if path is None:
raise TypeError
parts = path[1:].split("/") if path.startswith("/") else path.split("/") # meh
parts = path[1:].split("/") if path.startswith("/") else path.split("/") # meh
for index, part in enumerate(parts):
found = KEY_PATTERN.match(part)
if found:
Expand Down Expand Up @@ -60,17 +64,21 @@ def check_function(path, fn_name, args):
raise NotUniqueError(part.name)
names.add(part.name)


def all_registered():
return BaseDecorator.registered


def unregister_all():
BaseDecorator.registered.clear()


def is_matching(signature, args, path, query):
if len(signature) != len(path):
return False
return True


def application(environ, start_response):
try:
if environ[REQUEST_METHOD] in all_registered():
Expand Down Expand Up @@ -99,29 +107,32 @@ def __init__(self, path):
self.path = parse_path(path)

def __call__(self, fn):
@functools.wraps(fn)
def wrapped(*args, **kwargs):
result = fn(*args, **kwargs)
wrapped.__dict__ = deepcopy(fn.__dict__)
return result

registered = BaseDecorator.registered[self.type()]
if self.path in registered:
old = registered[self.path][0].__name__
path = "/".join(x.name if isinstance(x, Key) else x for x in self.path)
raise AlreadyRegisteredError(path, old)
fn_args = get_function_args(fn)
check_function(self.path, fn.__name__, fn_args)
registered[self.path] = fn, fn_args

@functools.wraps(fn)
def wrapped(*args, **kwargs):
return fn(*args, **kwargs)
registered[self.path] = wrapped, fn_args
return wrapped

@abstractmethod
def type(self):
pass


class GET(BaseDecorator):
def type(self):
return HTTP_GET


class POST(BaseDecorator):
def type(self):
return HTTP_POST

13 changes: 10 additions & 3 deletions clearest/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,33 @@

class MissingArgumentError(Exception):
def __init__(self, fn_name, arg):
super(MissingArgumentError, self).__init__("function {name} is missing argument {arg}!".format(name=fn_name, arg=arg))
super(MissingArgumentError, self).__init__(
"function {name} is missing argument {arg}!".format(name=fn_name, arg=arg))


class AlreadyRegisteredError(Exception):
def __init__(self, path, old_fn_name):
super(AlreadyRegisteredError, self).__init__("path {path} is already registered to the function {old}!".format(old=old_fn_name, path=path))
super(AlreadyRegisteredError, self).__init__(
"path {path} is already registered to the function {old}!".format(old=old_fn_name, path=path))


class NotUniqueError(Exception):
def __init__(self, var_name):
super(NotUniqueError, self).__init__("variable {var} is not unique".format(var=var_name))


class HttpError(Exception):
def __init__(self, code, msg):
super(HttpError, self).__init__()
self.code = code
self.msg = msg


class HttpBadRequest(HttpError):
def __init__(self):
super(HttpBadRequest, self).__init__(*HTTP_BAD_REQUEST)


class HttpNotFound(HttpError):
def __init__(self):
super(HttpNotFound, self).__init__(*HTTP_NOT_FOUND)
super(HttpNotFound, self).__init__(*HTTP_NOT_FOUND)
11 changes: 9 additions & 2 deletions tests/test_application.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from clearest import POST, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, GET, unregister_all, HTTP_OK
from clearest import POST, HTTP_NOT_FOUND, GET, unregister_all, HTTP_OK
from tests.util import called_with
from tests.wsgi import WSGITestCase


class Test(WSGITestCase):
def setUp(self):
unregister_all()
Expand All @@ -13,19 +15,24 @@ def test_application_not_found_2(self):
@POST("/asd")
def asd():
return {}

self.get("/asd")
self.assertEqual(HTTP_NOT_FOUND, self.status)

def test_application_not_found_3(self):
@GET("/asd")
def asd():
return {}

self.get("/asd/42")
self.assertEqual(HTTP_NOT_FOUND, self.status)

def test_application_simple_query(self):
@GET("/asd")
@called_with
def asd():
return {}

self.get("asd")
self.assertEqual(HTTP_OK, self.status)
self.assertEqual(((), {}), asd.called_with)
self.assertEqual(HTTP_OK, self.status)
9 changes: 8 additions & 1 deletion tests/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from unittest import TestCase

from clearest import GET, MissingArgumentError, AlreadyRegisteredError, NotUniqueError, unregister_all


class Test(TestCase):
def setUp(self):
unregister_all()
Expand All @@ -10,6 +12,7 @@ def test_fn():
@GET("/{key}")
def asd():
pass

self.assertRaises(MissingArgumentError, test_fn)

def test_decorator_already_registered_simple(self):
Expand All @@ -21,6 +24,7 @@ def asd():
@GET("/asd/asd")
def asd2():
pass

self.assertRaises(AlreadyRegisteredError, test_fn)

def test_decorator_already_registered_keys_simple(self):
Expand All @@ -32,6 +36,7 @@ def asd(a):
@GET("/asd/{b}")
def asd2(b):
pass

self.assertRaises(AlreadyRegisteredError, test_fn)

def test_decorator_already_registered_keys_complex(self):
Expand All @@ -43,11 +48,13 @@ def asd(a, b):
@GET("/asd/{a}/asd/{c}")
def asd2(b, c):
pass

self.assertRaises(AlreadyRegisteredError, test_fn)

def test_decorator_var_not_unique(self):
def test_fn():
@GET("/asd/{a}/{a}")
def asd(a, b):
pass
self.assertRaises(NotUniqueError, test_fn)

self.assertRaises(NotUniqueError, test_fn)
10 changes: 10 additions & 0 deletions tests/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import functools


def called_with(fn):
@functools.wraps(fn)
def wrapped(*args, **kwargs):
wrapped.called_with = args, kwargs
return fn(*args, **kwargs)

return wrapped
3 changes: 2 additions & 1 deletion tests/wsgi.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from unittest import TestCase

from clearest.core import application
from clearest.http import *
from clearest.wsgi import *
from clearest.core import application


class WSGITestCase(TestCase):
Expand Down

0 comments on commit c11679f

Please sign in to comment.