Mercurial > piecrust2
comparison piecrust/routing.py @ 790:4cbe057a8b6a
routing: Add some backwards compatibility support for parameter types.
This makes it so old routes are still correctly formatting years/months/days
which were previously hard-coded to int4/int2 types.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 05 Sep 2016 22:12:37 -0700 |
parents | f6f9a284a5f3 |
children | 504d6817352d |
comparison
equal
deleted
inserted
replaced
789:b8e760b3413e | 790:4cbe057a8b6a |
---|---|
64 | 64 |
65 # Get the straight-forward regex for matching this URI pattern. | 65 # Get the straight-forward regex for matching this URI pattern. |
66 p = route_esc_re.sub(self._uriPatternRepl, | 66 p = route_esc_re.sub(self._uriPatternRepl, |
67 re.escape(self.uri_pattern)) + '$' | 67 re.escape(self.uri_pattern)) + '$' |
68 self.uri_re = re.compile(p) | 68 self.uri_re = re.compile(p) |
69 | |
70 # Get the types of the route parameters. | |
71 self.param_types = {} | |
72 for m in route_re.finditer(self.uri_pattern): | |
73 qual = m.group('qual') | |
74 if qual: | |
75 self.param_types[str(m.group('name'))] = qual | |
76 | 69 |
77 # If the URI pattern has a 'path'-type component, we'll need to match | 70 # If the URI pattern has a 'path'-type component, we'll need to match |
78 # the versions for which that component is empty. So for instance if | 71 # the versions for which that component is empty. So for instance if |
79 # we have `/foo/%path:bar%`, we may need to match `/foo` (note the | 72 # we have `/foo/%path:bar%`, we may need to match `/foo` (note the |
80 # lack of a trailing slash). We have to build a special pattern (in | 73 # lack of a trailing slash). We have to build a special pattern (in |
94 | 87 |
95 # Determine the parameters for the route function. | 88 # Determine the parameters for the route function. |
96 self.func_name = self._validateFuncName(cfg.get('func')) | 89 self.func_name = self._validateFuncName(cfg.get('func')) |
97 self.func_parameters = [] | 90 self.func_parameters = [] |
98 self.func_has_variadic_parameter = False | 91 self.func_has_variadic_parameter = False |
92 self.param_types = {} | |
99 variadic_param_idx = -1 | 93 variadic_param_idx = -1 |
100 for m in route_re.finditer(self.uri_pattern): | 94 for m in route_re.finditer(self.uri_pattern): |
101 name = m.group('name') | 95 name = m.group('name') |
96 self.func_parameters.append(name) | |
97 | |
98 qual = m.group('qual') | |
99 if not qual: | |
100 qual = self._getBackwardCompatibleParamType(name) | |
101 if qual: | |
102 self.param_types[name] = qual | |
103 | |
102 if m.group('var'): | 104 if m.group('var'): |
103 self.func_has_variadic_parameter = True | 105 self.func_has_variadic_parameter = True |
104 variadic_param_idx = len(self.func_parameters) | 106 variadic_param_idx = len(self.func_parameters) |
105 | |
106 self.func_parameters.append(name) | |
107 | 107 |
108 if (variadic_param_idx >= 0 and | 108 if (variadic_param_idx >= 0 and |
109 variadic_param_idx != len(self.func_parameters) - 1): | 109 variadic_param_idx != len(self.func_parameters) - 1): |
110 raise Exception( | 110 raise Exception( |
111 "Only the last route URL parameter can be variadic. " | 111 "Only the last route URL parameter can be variadic. " |
268 return self.getUri(metadata) | 268 return self.getUri(metadata) |
269 | 269 |
270 def _uriFormatRepl(self, m): | 270 def _uriFormatRepl(self, m): |
271 qual = m.group('qual') | 271 qual = m.group('qual') |
272 name = m.group('name') | 272 name = m.group('name') |
273 | |
274 # Backwards compatibility... this will print a warning later. | |
275 if qual is None: | |
276 if name == 'year': | |
277 qual = 'int4' | |
278 elif name in ['month', 'day']: | |
279 qual = 'int2' | |
280 | |
273 if qual == 'int4': | 281 if qual == 'int4': |
274 return '%%(%s)04d' % name | 282 return '%%(%s)04d' % name |
275 elif qual == 'int2': | 283 elif qual == 'int2': |
276 return '%%(%s)02d' % name | 284 return '%%(%s)02d' % name |
285 elif qual and qual != 'path': | |
286 raise Exception("Unknown route parameter type: %s" % qual) | |
277 return '%%(%s)s' % name | 287 return '%%(%s)s' % name |
278 | 288 |
279 def _uriPatternRepl(self, m): | 289 def _uriPatternRepl(self, m): |
280 name = m.group('name') | 290 name = m.group('name') |
281 qual = m.group('qual') | 291 qual = m.group('qual') |
292 | |
293 # Backwards compatibility... this will print a warning later. | |
294 if qual is None: | |
295 if name == 'year': | |
296 qual = 'int4' | |
297 elif name in ['month', 'day']: | |
298 qual = 'int2' | |
299 | |
282 if qual == 'path' or m.group('var'): | 300 if qual == 'path' or m.group('var'): |
283 return r'(?P<%s>[^\?]*)' % name | 301 return r'(?P<%s>[^\?]*)' % name |
284 elif qual == 'int4': | 302 elif qual == 'int4': |
285 return r'(?P<%s>\d{4})' % name | 303 return r'(?P<%s>\d{4})' % name |
286 elif qual == 'int2': | 304 elif qual == 'int2': |
287 return r'(?P<%s>\d{2})' % name | 305 return r'(?P<%s>\d{2})' % name |
306 elif qual and qual != 'path': | |
307 raise Exception("Unknown route parameter type: %s" % qual) | |
288 return r'(?P<%s>[^/\?]+)' % name | 308 return r'(?P<%s>[^/\?]+)' % name |
289 | 309 |
290 def _uriNoPathRepl(self, m): | 310 def _uriNoPathRepl(self, m): |
291 name = m.group('name') | 311 name = m.group('name') |
292 qualifier = m.group('qual') | 312 qualifier = m.group('qual') |
301 if param_type in ['int', 'int2', 'int4']: | 321 if param_type in ['int', 'int2', 'int4']: |
302 try: | 322 try: |
303 return int(val) | 323 return int(val) |
304 except ValueError: | 324 except ValueError: |
305 raise Exception( | 325 raise Exception( |
306 "Expected route parameter '%s' to be of type " | 326 "Expected route parameter '%s' to be of type " |
307 "'%s', but was: %s" % | 327 "'%s', but was: %s" % |
308 (name, param_type, val)) | 328 (name, param_type, val)) |
309 if param_type == 'path': | 329 if param_type == 'path': |
310 return val | 330 return val |
311 raise Exception("Unknown route parameter type: %s" % param_type) | 331 raise Exception("Unknown route parameter type: %s" % param_type) |
332 | |
333 def _getBackwardCompatibleParamType(self, name): | |
334 # Print a warning only if we're not in a worker process. | |
335 print_warning = not self.app.config.has('baker/worker_id') | |
336 | |
337 if name in ['year']: | |
338 if print_warning: | |
339 logger.warning( | |
340 "Route parameter '%%%s%%' has no type qualifier. " | |
341 "You probably meant '%%int4:%s%%' so we'll use that." % | |
342 (name, name)) | |
343 return 'int4' | |
344 if name in ['month', 'day']: | |
345 if print_warning: | |
346 logger.warning( | |
347 "Route parameter '%%%s%%' has no type qualifier. " | |
348 "You probably meant '%%int2:%s%%' so we'll use that." % | |
349 (name, name)) | |
350 return 'int2' | |
351 return None | |
312 | 352 |
313 def _validateFuncName(self, name): | 353 def _validateFuncName(self, name): |
314 if not name: | 354 if not name: |
315 return None | 355 return None |
316 i = name.find('(') | 356 i = name.find('(') |