Skip to content

Commit

Permalink
Implemented multiple paths on route decorator #108
Browse files Browse the repository at this point in the history
  • Loading branch information
gi0baro committed Dec 19, 2016
1 parent 1d093dc commit 6d1845c
Showing 1 changed file with 152 additions and 129 deletions.
281 changes: 152 additions & 129 deletions weppy/expose.py
Expand Up @@ -56,20 +56,22 @@ class Expose(with_metaclass(MetaExpose)):
'(([?*+])|(\([^()]*\))|(\[[^\[\]]*\])|(\<[^<>]*\>))')

def __init__(
self, path=None, name=None, template=None, pipeline=None,
self, paths=[], name=None, template=None, pipeline=None,
injectors=None, schemes=None, hostname=None, methods=None, prefix=None,
template_folder=None, template_path=None, **kwargs
):
if callable(path):
if callable(paths):
raise SyntaxError('Use @route(), not @route.')
self.schemes = schemes or ('http', 'https')
if not isinstance(self.schemes, (list, tuple)):
self.schemes = (self.schemes,)
self.schemes = (self.schemes, )
self.methods = methods or ('get', 'post', 'head')
if not isinstance(self.methods, (list, tuple)):
self.methods = (self.methods,)
self.methods = (self.methods, )
self.hostname = hostname
self.path = path
self.paths = paths
if not isinstance(self.paths, (list, tuple)):
self.paths = (self.paths, )
self.name = name
self.template = template
self.template_folder = template_folder
Expand Down Expand Up @@ -101,103 +103,7 @@ def build_name(self):
# allow only one level of naming if name is builded
if len(short.split(os.sep)) > 1:
short = short.split(os.sep)[-1]
return '.'.join(short.split(os.sep) + [self.func_name])

def store_argtypes(self):
parsers = {
'int': Expose._parse_int_reqarg,
'float': Expose._parse_float_reqarg,
'date': Expose._parse_date_reqarg
}
opt_parsers = {
'int': Expose._parse_int_reqarg_opt,
'float': Expose._parse_float_reqarg_opt,
'date': Expose._parse_date_reqarg_opt
}
pipeline = []
for key in parsers.keys():
optionals = []
for element in re.compile(
"\(([^<]+)?<{}\:(\w+)>\)\?".format(key)
).findall(self.path):
optionals.append(element[1])
elements = set(
re.compile("<{}\:(\w+)>".format(key)).findall(self.path))
args = elements - set(optionals)
if args:
parser = self._wrap_reqargs_parser(parsers[key], args)
pipeline.append(parser)
if optionals:
parser = self._wrap_reqargs_parser(opt_parsers[key], optionals)
pipeline.append(parser)
if pipeline:
self.parse_reqargs = self._wrap_reqargs_pipeline(pipeline)
else:
self.parse_reqargs = self._parse_reqargs

@staticmethod
def _parse_reqargs(match):
return match.groupdict()

@staticmethod
def _parse_int_reqarg(args, route_args):
for arg in args:
route_args[arg] = int(route_args[arg])

@staticmethod
def _parse_int_reqarg_opt(args, route_args):
for arg in args:
if route_args[arg] is None:
continue
route_args[arg] = int(route_args[arg])

@staticmethod
def _parse_float_reqarg(args, route_args):
for arg in args:
route_args[arg] = float(route_args[arg])

@staticmethod
def _parse_float_reqarg_opt(args, route_args):
for arg in args:
if route_args[arg] is None:
continue
route_args[arg] = float(route_args[arg])

@staticmethod
def _parse_date_reqarg(args, route_args):
try:
for arg in args:
route_args[arg] = datetime.strptime(
route_args[arg], "%Y-%m-%d")
except Exception:
raise HTTP(404)

@staticmethod
def _parse_date_reqarg_opt(args, route_args):
try:
for arg in args:
if route_args[arg] is None:
continue
route_args[arg] = datetime.strptime(
route_args[arg], "%Y-%m-%d")
except Exception:
raise HTTP(404)

@staticmethod
def _wrap_reqargs_parser(parser, args):
@wraps(parser)
def wrapped(route_args):
return parser(args, route_args)
return wrapped

@staticmethod
def _wrap_reqargs_pipeline(parsers):
def wrapped(match):
route_args = match.groupdict()
for parser in parsers:
parser(route_args)
return route_args
return wrapped
return '.'.join(short.split(os.sep) + [self.f_name])

@classmethod
def build_regex(cls, schemes, hostname, methods, path):
Expand Down Expand Up @@ -252,51 +158,46 @@ def add_route(cls, route):
'host': route[1].hostname,
'path': cls.remove_decoration(route[1].path)}

def __call__(self, func):
self.func_name = func.__name__
self.filename = os.path.realpath(func.__code__.co_filename)
def __call__(self, f):
self.f_name = f.__name__
self.filename = os.path.realpath(f.__code__.co_filename)
self.hostname = self.hostname or \
self.application.config.hostname_default
if not self.path:
self.path = '/' + func.__name__
if not self.paths:
self.paths.append("/" + f.__name__)
if not self.name:
self.name = self.build_name()
# is it good?
if self.name.endswith("."):
self.name = self.name + self.func_name
self.name = self.name + self.f_name
#
if not self.path.startswith('/'):
self.path = '/' + self.path
if self.prefix:
if not self.prefix.startswith('/'):
self.prefix = '/' + self.prefix
self.path = (self.path != '/' and self.prefix + self.path) \
or self.prefix
if not self.template:
self.template = self.func_name + \
self.template = self.f_name + \
self.application.template_default_extension
if self.template_folder:
self.template = os.path.join(self.template_folder, self.template)
self.template_path = self.template_path or \
self.application.template_path
wrapped_func = Pipeline(self.pipeline)(func)
self.func = wrapped_func
self.regex = self.build_regex(
self.schemes, self.hostname, self.methods, self.path)
self.store_argtypes()
route = (re.compile(self.regex), self)
self.add_route(route)
self._routes_str[self.name] = "%s %s://%s%s -> %s" % (
"|".join(s.upper() for s in self.methods),
"|".join(s for s in self.schemes),
self.hostname or "<any>",
self.path,
self.name
)
wrapped_f = Pipeline(self.pipeline)(f)
self.f = wrapped_f
for idx, path in enumerate(self.paths):
routeobj = Route(self, path, idx)
route = (re.compile(routeobj.regex), routeobj)
self.add_route(route)
self._routes_str[routeobj.name] = "%s %s://%s%s -> %s" % (
"|".join(s.upper() for s in self.methods),
"|".join(s for s in self.schemes),
self.hostname or "<any>",
routeobj.path,
self.name
)
for proc_handler in self.processors:
proc_handler(self)
self._routing_stack.pop()
return func
return f

@classmethod
def match_lang(cls, path):
Expand Down Expand Up @@ -362,7 +263,7 @@ def dispatch(cls):
request.name = route.name
cls._before_dispatch(route)
try:
route.func(**reqargs)
route.f(**reqargs)
except:
cls._after_dispatch(route)
raise
Expand All @@ -378,6 +279,128 @@ def exposing(cls):
return cls._routing_stack[-1]


class Route(object):
def __init__(self, exposer, path, idx):
self.exposer = exposer
self.path = path
self.name = self.exposer.name if idx == 0 else \
"{}_{}".format(self.exposer.name, idx)
self.schemes = self.exposer.schemes
self.methods = self.exposer.methods
self.pipeline = self.exposer.pipeline
self.f = self.exposer.f
if not self.path.startswith('/'):
self.path = '/' + self.path
if self.exposer.prefix:
self.path = (
(self.path != '/' and self.prefix + self.path) or
self.exposer.prefix)
self.regex = self.exposer.build_regex(
self.schemes, self.hostname, self.methods, self.path)
self.build_argparser()

@property
def hostname(self):
return self.exposer.hostname

def build_argparser(self):
parsers = {
'int': Route._parse_int_reqarg,
'float': Route._parse_float_reqarg,
'date': Route._parse_date_reqarg
}
opt_parsers = {
'int': Route._parse_int_reqarg_opt,
'float': Route._parse_float_reqarg_opt,
'date': Route._parse_date_reqarg_opt
}
pipeline = []
for key in parsers.keys():
optionals = []
for element in re.compile(
"\(([^<]+)?<{}\:(\w+)>\)\?".format(key)
).findall(self.path):
optionals.append(element[1])
elements = set(
re.compile("<{}\:(\w+)>".format(key)).findall(self.path))
args = elements - set(optionals)
if args:
parser = self._wrap_reqargs_parser(parsers[key], args)
pipeline.append(parser)
if optionals:
parser = self._wrap_reqargs_parser(
opt_parsers[key], optionals)
pipeline.append(parser)
if pipeline:
self.parse_reqargs = self._wrap_reqargs_pipeline(pipeline)
else:
self.parse_reqargs = self._parse_reqargs

@staticmethod
def _parse_reqargs(match):
return match.groupdict()

@staticmethod
def _parse_int_reqarg(args, route_args):
for arg in args:
route_args[arg] = int(route_args[arg])

@staticmethod
def _parse_int_reqarg_opt(args, route_args):
for arg in args:
if route_args[arg] is None:
continue
route_args[arg] = int(route_args[arg])

@staticmethod
def _parse_float_reqarg(args, route_args):
for arg in args:
route_args[arg] = float(route_args[arg])

@staticmethod
def _parse_float_reqarg_opt(args, route_args):
for arg in args:
if route_args[arg] is None:
continue
route_args[arg] = float(route_args[arg])

@staticmethod
def _parse_date_reqarg(args, route_args):
try:
for arg in args:
route_args[arg] = datetime.strptime(
route_args[arg], "%Y-%m-%d")
except Exception:
raise HTTP(404)

@staticmethod
def _parse_date_reqarg_opt(args, route_args):
try:
for arg in args:
if route_args[arg] is None:
continue
route_args[arg] = datetime.strptime(
route_args[arg], "%Y-%m-%d")
except Exception:
raise HTTP(404)

@staticmethod
def _wrap_reqargs_parser(parser, args):
@wraps(parser)
def wrapped(route_args):
return parser(args, route_args)
return wrapped

@staticmethod
def _wrap_reqargs_pipeline(parsers):
def wrapped(match):
route_args = match.groupdict()
for parser in parsers:
parser(route_args)
return route_args
return wrapped


class ResponsePipe(Pipe):
def __init__(self, route):
self.route = route
Expand Down

0 comments on commit 6d1845c

Please sign in to comment.