Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(416)

Side by Side Diff: Tools/Scripts/webkitpy/thirdparty/wpt/wpt/tools/wptserve/wptserve/router.py

Issue 1154373005: Introduce WPTServe for running W3C Blink Layout tests (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Add executable bit to pass permchecks. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 import itertools
2 import re
3 import types
4
5 from logger import get_logger
6
7 any_method = object()
8
9 class RouteTokenizer(object):
10 def literal(self, scanner, token):
11 return ("literal", token)
12
13 def slash(self, scanner, token):
14 return ("slash", None)
15
16 def group(self, scanner, token):
17 return ("group", token[1:-1])
18
19 def star(self, scanner, token):
20 return ("star", token[1:-3])
21
22 def scan(self, input_str):
23 scanner = re.Scanner([(r"/", self.slash),
24 (r"{\w*}", self.group),
25 (r"\*", self.star),
26 (r"(?:\\.|[^{\*/])*", self.literal),])
27 return scanner.scan(input_str)
28
29 class RouteCompiler(object):
30 def __init__(self):
31 self.reset()
32
33 def reset(self):
34 self.star_seen = False
35
36 def compile(self, tokens):
37 self.reset()
38
39 func_map = {"slash":self.process_slash,
40 "literal":self.process_literal,
41 "group":self.process_group,
42 "star":self.process_star}
43
44 re_parts = ["^"]
45
46 if not tokens or tokens[0][0] != "slash":
47 tokens = itertools.chain([("slash", None)], tokens)
48
49 for token in tokens:
50 re_parts.append(func_map[token[0]](token))
51
52 if self.star_seen:
53 re_parts.append(")")
54 re_parts.append("$")
55
56 return re.compile("".join(re_parts))
57
58 def process_literal(self, token):
59 return re.escape(token[1])
60
61 def process_slash(self, token):
62 return "/"
63
64 def process_group(self, token):
65 if self.star_seen:
66 raise ValueError("Group seen after star in regexp")
67 return "(?P<%s>[^/]+)" % token[1]
68
69 def process_star(self, token):
70 if self.star_seen:
71 raise ValueError("Star seen after star in regexp")
72 self.star_seen = True
73 return "(.*"
74
75 def compile_path_match(route_pattern):
76 """tokens: / or literal or match or *"""
77
78 tokenizer = RouteTokenizer()
79 tokens, unmatched = tokenizer.scan(route_pattern)
80
81 assert unmatched is "", unmatched
82
83 compiler = RouteCompiler()
84
85 return compiler.compile(tokens)
86
87 class Router(object):
88 """Object for matching handler functions to requests.
89
90 :param doc_root: Absolute path of the filesystem location from
91 which to serve tests
92 :param routes: Initial routes to add; a list of three item tuples
93 (method, path_pattern, handler_function), defined
94 as for register()
95 """
96
97 def __init__(self, doc_root, routes):
98 self.doc_root = doc_root
99 self.routes = []
100 self.logger = get_logger()
101 for route in reversed(routes):
102 self.register(*route)
103
104 def register(self, methods, path, handler):
105 """Register a handler for a set of paths.
106
107 :param methods: Set of methods this should match. "*" is a
108 special value indicating that all methods should
109 be matched.
110
111 :param path_pattern: Match pattern that will be used to determine if
112 a request path matches this route. Match patterns
113 consist of either literal text, match groups,
114 denoted {name}, which match any character except /,
115 and, at most one \*, which matches and character an d
116 creates a match group to the end of the string.
117 If there is no leading "/" on the pattern, this is
118 automatically implied. For example::
119
120 api/{resource}/*.json
121
122 Would match `/api/test/data.json` or
123 `/api/test/test2/data.json`, but not `/api/test/data .py`.
124
125 The match groups are made available in the request o bject
126 as a dictionary through the route_match property. Fo r
127 example, given the route pattern above and the path
128 `/api/test/data.json`, the route_match property woul d
129 contain::
130
131 {"resource": "test", "*": "data.json"}
132
133 :param handler: Function that will be called to process matching
134 requests. This must take two parameters, the request
135 object and the response object.
136
137 """
138 if type(methods) in types.StringTypes or methods in (any_method, "*"):
139 methods = [methods]
140 for method in methods:
141 self.routes.append((method, compile_path_match(path), handler))
142 self.logger.debug("Route pattern: %s" % self.routes[-1][1].pattern)
143
144 def get_handler(self, request):
145 """Get a handler for a request or None if there is no handler.
146
147 :param request: Request to get a handler for.
148 :rtype: Callable or None
149 """
150 for method, regexp, handler in reversed(self.routes):
151 if (request.method == method or
152 method in (any_method, "*") or
153 (request.method == "HEAD" and method == "GET")):
154 m = regexp.match(request.url_parts.path)
155 if m:
156 if not hasattr(handler, "__class__"):
157 name = handler.__name__
158 else:
159 name = handler.__class__.__name__
160 self.logger.debug("Found handler %s" % name)
161
162 match_parts = m.groupdict().copy()
163 if len(match_parts) < len(m.groups()):
164 match_parts["*"] = m.groups()[-1]
165 request.route_match = match_parts
166
167 return handler
168 return None
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698