comparison piecrust/routing.py @ 256:da5e6e00fb41

bake/serve: Make previewed and baked URLs consistent. The preview server now handles the `pretty_urls` setting correctly instead of forcing it. The `trailing_slash` and `pagination_suffix` setting are also doing the same between the 2 systems.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 22 Feb 2015 22:01:58 -0800
parents 55087da9a72e
children 61145dcd56e0
comparison
equal deleted inserted replaced
250:311447fe3dd0 256:da5e6e00fb41
1 import re 1 import re
2 import os.path
2 import logging 3 import logging
3 4
4 5
5 logger = logging.getLogger(__name__) 6 logger = logging.getLogger(__name__)
6 7
7 8
8 route_re = re.compile(r'%((?P<qual>path):)?(?P<name>\w+)%') 9 route_re = re.compile(r'%((?P<qual>path):)?(?P<name>\w+)%')
9 template_func_re = re.compile(r'^(?P<name>\w+)\((?P<first_arg>\w+)(?P<other_args>.*)\)\s*$') 10 template_func_re = re.compile(r'^(?P<name>\w+)\((?P<first_arg>\w+)(?P<other_args>.*)\)\s*$')
10 template_func_arg_re = re.compile(r',\s*(?P<arg>\w+)') 11 template_func_arg_re = re.compile(r',\s*(?P<arg>\w+)')
12 ugly_url_cleaner = re.compile(r'\.html$')
11 13
12 14
13 class IRouteMetadataProvider(object): 15 class IRouteMetadataProvider(object):
14 def getRouteMetadata(self): 16 def getRouteMetadata(self):
15 raise NotImplementedError() 17 raise NotImplementedError()
20 Each route defines the "shape" of an URL and how it maps to 22 Each route defines the "shape" of an URL and how it maps to
21 sources and taxonomies. 23 sources and taxonomies.
22 """ 24 """
23 def __init__(self, app, cfg): 25 def __init__(self, app, cfg):
24 self.app = app 26 self.app = app
27
28 self.pretty_urls = app.config.get('site/pretty_urls')
29 self.trailing_slash = app.config.get('site/trailing_slash')
30 self.pagination_suffix_format = app.config.get(
31 '__cache/pagination_suffix_format')
32 self.uri_root = app.config.get('site/root').rstrip('/') + '/'
33
25 uri = cfg['url'] 34 uri = cfg['url']
26 self.uri_root = app.config.get('site/root').rstrip('/') + '/'
27 self.uri_pattern = uri.lstrip('/') 35 self.uri_pattern = uri.lstrip('/')
28 self.uri_format = route_re.sub(self._uriFormatRepl, self.uri_pattern) 36 self.uri_format = route_re.sub(self._uriFormatRepl, self.uri_pattern)
29 if app.config.get('site/show_debug_info'): 37 if app.config.get('site/show_debug_info'):
30 self.uri_format += '?!debug' 38 self.uri_format += '?!debug'
31 39
32 # Get the straight-forward regex for matching this URI pattern. 40 # Get the straight-forward regex for matching this URI pattern.
33 p = route_re.sub(self._uriPatternRepl, self.uri_pattern) + '$' 41 re_suffix = '$'
42 p = route_re.sub(self._uriPatternRepl, self.uri_pattern) + re_suffix
34 self.uri_re = re.compile(p) 43 self.uri_re = re.compile(p)
35 44
36 # If the URI pattern has a 'path'-type component, we'll need to match 45 # If the URI pattern has a 'path'-type component, we'll need to match
37 # the versions for which that component is empty. So for instance if 46 # the versions for which that component is empty. So for instance if
38 # we have `/foo/%path:bar%`, we may need to match `/foo` (note the 47 # we have `/foo/%path:bar%`, we may need to match `/foo` (note the
74 83
75 def matchesMetadata(self, source_metadata): 84 def matchesMetadata(self, source_metadata):
76 return self.required_source_metadata.issubset(source_metadata.keys()) 85 return self.required_source_metadata.issubset(source_metadata.keys())
77 86
78 def matchUri(self, uri): 87 def matchUri(self, uri):
88 if not self.pretty_urls:
89 uri = ugly_url_cleaner.sub('', uri)
90 elif self.trailing_slash:
91 uri = uri.rstrip('/')
92
79 m = self.uri_re.match(uri) 93 m = self.uri_re.match(uri)
80 if m: 94 if m:
81 return m.groupdict() 95 return m.groupdict()
82 if self.uri_re_no_path: 96 if self.uri_re_no_path:
83 m = self.uri_re_no_path.match(uri) 97 m = self.uri_re_no_path.match(uri)
84 if m: 98 if m:
85 return m.groupdict() 99 return m.groupdict()
86 return None 100 return None
87 101
88 def getUri(self, source_metadata, provider=None, include_site_root=True): 102 def getUri(self, source_metadata, *, sub_num=1, provider=None,
103 include_site_root=True):
89 if provider: 104 if provider:
90 source_metadata = dict(source_metadata) 105 source_metadata = dict(source_metadata)
91 source_metadata.update(provider.getRouteMetadata()) 106 source_metadata.update(provider.getRouteMetadata())
107
92 #TODO: fix this hard-coded shit 108 #TODO: fix this hard-coded shit
93 for key in ['year', 'month', 'day']: 109 for key in ['year', 'month', 'day']:
94 if key in source_metadata and isinstance(source_metadata[key], str): 110 if key in source_metadata and isinstance(source_metadata[key], str):
95 source_metadata[key] = int(source_metadata[key]) 111 source_metadata[key] = int(source_metadata[key])
112
96 uri = self.uri_format % source_metadata 113 uri = self.uri_format % source_metadata
114 suffix = None
115 if sub_num > 1:
116 # Note that we know the pagination suffix starts with a slash.
117 suffix = self.pagination_suffix_format % sub_num
118
119 if self.pretty_urls:
120 # Output will be:
121 # - `subdir/name`
122 # - `subdir/name/2`
123 # - `subdir/name.ext`
124 # - `subdir/name.ext/2`
125 if suffix:
126 uri = uri.rstrip('/') + suffix
127 if self.trailing_slash:
128 uri = uri.rstrip('/') + '/'
129 else:
130 # Output will be:
131 # - `subdir/name.html`
132 # - `subdir/name/2.html`
133 # - `subdir/name.ext`
134 # - `subdir/name/2.ext`
135 base_uri, ext = os.path.splitext(uri)
136 if not ext:
137 ext = '.html'
138 if suffix:
139 uri = base_uri + suffix + ext
140 else:
141 uri = base_uri + ext
142
97 if include_site_root: 143 if include_site_root:
98 uri = self.uri_root + uri 144 uri = self.uri_root + uri
145
99 return uri 146 return uri
100 147
101 def _uriFormatRepl(self, m): 148 def _uriFormatRepl(self, m):
102 name = m.group('name') 149 name = m.group('name')
103 #TODO: fix this hard-coded shit 150 #TODO: fix this hard-coded shit