comparison piecrust/routing.py @ 329:422052d2e978

internal: Try handling URLs in a consistent way. * Now URLs passed to, and returned from, routes will always be absolute URLs, i.e. URLs including the site root. * Validate the site root at config loading time to make sure it starts and ends with a slash. * Get rid of unused stuff. * Add tests.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 31 Mar 2015 23:03:28 -0700
parents 61145dcd56e0
children b034f6f15e22
comparison
equal deleted inserted replaced
328:2a5996e0d3ec 329:422052d2e978
5 5
6 logger = logging.getLogger(__name__) 6 logger = logging.getLogger(__name__)
7 7
8 8
9 route_re = re.compile(r'%((?P<qual>path):)?(?P<name>\w+)%') 9 route_re = re.compile(r'%((?P<qual>path):)?(?P<name>\w+)%')
10 template_func_re = re.compile(r'^(?P<name>\w+)\((?P<first_arg>\w+)(?P<other_args>.*)\)\s*$') 10 route_esc_re = re.compile(r'\\%((?P<qual>path)\\:)?(?P<name>\w+)\\%')
11 template_func_re = re.compile(r'^(?P<name>\w+)\((?P<first_arg>\w+)'
12 r'(?P<other_args>.*)\)\s*$')
11 template_func_arg_re = re.compile(r',\s*(?P<arg>\w+)') 13 template_func_arg_re = re.compile(r',\s*(?P<arg>\w+)')
12 ugly_url_cleaner = re.compile(r'\.html$') 14 ugly_url_cleaner = re.compile(r'\.html$')
13 15
14 16
15 class IRouteMetadataProvider(object): 17 class IRouteMetadataProvider(object):
27 29
28 self.pretty_urls = app.config.get('site/pretty_urls') 30 self.pretty_urls = app.config.get('site/pretty_urls')
29 self.trailing_slash = app.config.get('site/trailing_slash') 31 self.trailing_slash = app.config.get('site/trailing_slash')
30 self.pagination_suffix_format = app.config.get( 32 self.pagination_suffix_format = app.config.get(
31 '__cache/pagination_suffix_format') 33 '__cache/pagination_suffix_format')
32 self.uri_root = app.config.get('site/root').rstrip('/') + '/' 34 self.uri_root = app.config.get('site/root')
33 35
34 uri = cfg['url'] 36 uri = cfg['url']
35 self.uri_pattern = uri.lstrip('/') 37 self.uri_pattern = uri.lstrip('/')
36 self.uri_format = route_re.sub(self._uriFormatRepl, self.uri_pattern) 38 self.uri_format = route_re.sub(self._uriFormatRepl, self.uri_pattern)
37 if app.config.get('site/show_debug_info'): 39 if app.config.get('site/show_debug_info'):
38 self.uri_format += '?!debug' 40 self.uri_format += '?!debug'
39 41
40 # Get the straight-forward regex for matching this URI pattern. 42 # Get the straight-forward regex for matching this URI pattern.
41 re_suffix = '$' 43 p = route_esc_re.sub(self._uriPatternRepl,
42 p = route_re.sub(self._uriPatternRepl, self.uri_pattern) + re_suffix 44 re.escape(self.uri_pattern)) + '$'
43 self.uri_re = re.compile(p) 45 self.uri_re = re.compile(p)
44 46
45 # If the URI pattern has a 'path'-type component, we'll need to match 47 # If the URI pattern has a 'path'-type component, we'll need to match
46 # the versions for which that component is empty. So for instance if 48 # the versions for which that component is empty. So for instance if
47 # we have `/foo/%path:bar%`, we may need to match `/foo` (note the 49 # we have `/foo/%path:bar%`, we may need to match `/foo` (note the
52 uri_pattern_no_path = ( 54 uri_pattern_no_path = (
53 route_re.sub(self._uriNoPathRepl, self.uri_pattern) 55 route_re.sub(self._uriNoPathRepl, self.uri_pattern)
54 .replace('//', '/') 56 .replace('//', '/')
55 .rstrip('/')) 57 .rstrip('/'))
56 if uri_pattern_no_path != self.uri_pattern: 58 if uri_pattern_no_path != self.uri_pattern:
57 p = route_re.sub(self._uriPatternRepl, uri_pattern_no_path) + '$' 59 p = route_esc_re.sub(self._uriPatternRepl,
60 re.escape(uri_pattern_no_path)) + '$'
58 self.uri_re_no_path = re.compile(p) 61 self.uri_re_no_path = re.compile(p)
59 else: 62 else:
60 self.uri_re_no_path = None 63 self.uri_re_no_path = None
61 64
65 self.required_source_metadata = set()
66 for m in route_re.finditer(self.uri_pattern):
67 self.required_source_metadata.add(m.group('name'))
68
62 self.source_name = cfg['source'] 69 self.source_name = cfg['source']
63 self.taxonomy = cfg.get('taxonomy') 70 self.taxonomy = cfg.get('taxonomy')
64 self.required_source_metadata = set()
65 for m in route_re.finditer(uri):
66 self.required_source_metadata.add(m.group('name'))
67 self.template_func = None 71 self.template_func = None
68 self.template_func_name = None 72 self.template_func_name = None
69 self.template_func_args = [] 73 self.template_func_args = []
70 self._createTemplateFunc(cfg.get('func')) 74 self._createTemplateFunc(cfg.get('func'))
71 75
83 87
84 def matchesMetadata(self, source_metadata): 88 def matchesMetadata(self, source_metadata):
85 return self.required_source_metadata.issubset(source_metadata.keys()) 89 return self.required_source_metadata.issubset(source_metadata.keys())
86 90
87 def matchUri(self, uri): 91 def matchUri(self, uri):
92 if not uri.startswith(self.uri_root):
93 raise Exception("The given URI is not absolute: %s" % uri)
94 uri = uri[len(self.uri_root):]
95
88 if not self.pretty_urls: 96 if not self.pretty_urls:
89 uri = ugly_url_cleaner.sub('', uri) 97 uri = ugly_url_cleaner.sub('', uri)
90 elif self.trailing_slash: 98 elif self.trailing_slash:
91 uri = uri.rstrip('/') 99 uri = uri.rstrip('/')
92 100
97 m = self.uri_re_no_path.match(uri) 105 m = self.uri_re_no_path.match(uri)
98 if m: 106 if m:
99 return m.groupdict() 107 return m.groupdict()
100 return None 108 return None
101 109
102 def getUri(self, source_metadata, *, sub_num=1, provider=None, 110 def getUri(self, source_metadata, *, sub_num=1, provider=None):
103 include_site_root=True):
104 if provider: 111 if provider:
105 source_metadata = dict(source_metadata) 112 source_metadata = dict(source_metadata)
106 source_metadata.update(provider.getRouteMetadata()) 113 source_metadata.update(provider.getRouteMetadata())
107 114
108 #TODO: fix this hard-coded shit 115 #TODO: fix this hard-coded shit
145 if suffix: 152 if suffix:
146 uri = base_uri + suffix + ext 153 uri = base_uri + suffix + ext
147 else: 154 else:
148 uri = base_uri + ext 155 uri = base_uri + ext
149 156
150 if include_site_root: 157 uri = self.uri_root + uri
151 uri = self.uri_root + uri
152
153 return uri 158 return uri
154 159
155 def _uriFormatRepl(self, m): 160 def _uriFormatRepl(self, m):
156 name = m.group('name') 161 name = m.group('name')
157 #TODO: fix this hard-coded shit 162 #TODO: fix this hard-coded shit