Mercurial > piecrust2
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 |