Mercurial > piecrust2
changeset 259:adb066ffb363
Merge docs.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 22 Feb 2015 22:03:54 -0800 |
parents | 7ec06ec14247 (diff) ef8e5f9efdbd (current diff) |
children | 07b4b8484c0a |
files | |
diffstat | 6 files changed, 108 insertions(+), 59 deletions(-) [+] |
line wrap: on
line diff
--- a/piecrust/app.py Sun Feb 22 21:53:53 2015 -0800 +++ b/piecrust/app.py Sun Feb 22 22:03:54 2015 -0800 @@ -108,9 +108,7 @@ 'default_template_engine': DEFAULT_TEMPLATE_ENGINE, 'enable_gzip': True, 'pretty_urls': False, - 'slugify': 'transliterate|lowercase', - 'timezone': False, - 'locale': False, + 'trailing_slash': False, 'date_format': DEFAULT_DATE_FORMAT, 'auto_formats': collections.OrderedDict([ ('html', ''), @@ -121,7 +119,6 @@ 'plugins_sources': [DEFAULT_PLUGIN_SOURCE], 'themes_sources': [DEFAULT_THEME_SOURCE], 'cache_time': 28800, - 'display_errors': True, 'enable_debug_info': True, 'show_debug_info': False, 'use_default_content': True @@ -139,20 +136,36 @@ # Cache auto-format regexes. if not isinstance(sitec['auto_formats'], dict): - raise ConfigurationError("The 'site/auto_formats' setting must be a dictionary.") + raise ConfigurationError("The 'site/auto_formats' setting must be " + "a dictionary.") html_auto_format = sitec['auto_formats'] if not html_auto_format: sitec['auto_formats']['html'] = sitec['default_format'] cachec['auto_formats_re'] = r"\.(%s)$" % ( '|'.join( - [re.escape(i) for i in list(sitec['auto_formats'].keys())])) + [re.escape(i) for i in + list(sitec['auto_formats'].keys())])) if sitec['default_auto_format'] not in sitec['auto_formats']: - raise ConfigurationError("Default auto-format '%s' is not declared." % sitec['default_auto_format']) + raise ConfigurationError("Default auto-format '%s' is not " + "declared." % + sitec['default_auto_format']) - # Cache pagination suffix regex. - pgn_suffix = re.escape(sitec['pagination_suffix']) - pgn_suffix = pgn_suffix.replace("\\%num\\%", "(?P<num>\\d+)") + '$' - cachec['pagination_suffix_re'] = pgn_suffix + # Cache pagination suffix regex and format. + pgn_suffix = sitec['pagination_suffix'] + if len(pgn_suffix) == 0 or pgn_suffix[0] != '/': + raise ConfigurationError("The 'site/pagination_suffix' setting " + "must start with a slash.") + if '%num%' not in pgn_suffix: + raise ConfigurationError("The 'site/pagination_suffix' setting " + "must contain the '%num%' placeholder.") + + pgn_suffix_fmt = pgn_suffix.replace('%num%', '%(num)d') + cachec['pagination_suffix_format'] = pgn_suffix_fmt + + pgn_suffix_re = re.escape(pgn_suffix) + pgn_suffix_re = (pgn_suffix_re.replace("\\%num\\%", "(?P<num>\\d+)") + + '$') + cachec['pagination_suffix_re'] = pgn_suffix_re # Make sure plugins and theme sources are lists. if not isinstance(sitec['plugins_sources'], list): @@ -283,9 +296,11 @@ if not routesc: raise ConfigurationError("There are no routes defined.") if not isinstance(sourcesc, dict): - raise ConfigurationError("The 'site/sources' setting must be a dictionary.") + raise ConfigurationError("The 'site/sources' setting must be a " + "dictionary.") if not isinstance(routesc, list): - raise ConfigurationError("The 'site/routes' setting must be a list.") + raise ConfigurationError("The 'site/routes' setting must be a " + "list.") # Add the theme page source if no sources were defined in the theme # configuration itself. @@ -310,7 +325,8 @@ # of other default values for other configuration stuff. for sn, sc in sourcesc.items(): if not isinstance(sc, dict): - raise ConfigurationError("All sources in 'site/sources' must be dictionaries.") + raise ConfigurationError("All sources in 'site/sources' must " + "be dictionaries.") sc.setdefault('type', 'default') sc.setdefault('fs_endpoint', sn) sc.setdefault('ignore_missing_dir', False) @@ -325,17 +341,19 @@ # values, etc. for rc in routesc: if not isinstance(rc, dict): - raise ConfigurationError("All routes in 'site/routes' must be dictionaries.") + raise ConfigurationError("All routes in 'site/routes' must be " + "dictionaries.") rc_url = rc.get('url') if not rc_url: - raise ConfigurationError("All routes in 'site/routes' must have an 'url'.") + raise ConfigurationError("All routes in 'site/routes' must " + "have an 'url'.") if rc_url[0] != '/': raise ConfigurationError("Route URLs must start with '/'.") if rc.get('source') is None: raise ConfigurationError("Routes must specify a source.") if rc['source'] not in list(sourcesc.keys()): - raise ConfigurationError("Route is referencing unknown source: %s" % - rc['source']) + raise ConfigurationError("Route is referencing unknown " + "source: %s" % rc['source']) rc.setdefault('taxonomy', None) rc.setdefault('page_suffix', '/%num%') @@ -358,7 +376,6 @@ "Source '%s' is using a reserved endpoint name: %s" % (name, endpoint)) - # Done validating! return values
--- a/piecrust/baking/single.py Sun Feb 22 21:53:53 2015 -0800 +++ b/piecrust/baking/single.py Sun Feb 22 22:03:54 2015 -0800 @@ -33,34 +33,6 @@ self.pretty_urls = app.config.get('site/pretty_urls') self.pagination_suffix = app.config.get('site/pagination_suffix') - def getOutputUri(self, uri, num): - suffix = self.pagination_suffix.replace('%num%', str(num)) - if self.pretty_urls: - # Output will be: - # - `uri/name` - # - `uri/name/2` - # - `uri/name.ext` - # - `uri/name.ext/2` - if num <= 1: - return uri - return uri + suffix - else: - # Output will be: - # - `uri/name.html` - # - `uri/name/2.html` - # - `uri/name.ext` - # - `uri/name/2.ext` - if uri == '/': - if num <= 1: - return '/' - return '/' + suffix.lstrip('/') - else: - if num <= 1: - return uri - #TODO: watch out for tags with dots in them. - base_uri, ext = os.path.splitext(uri) - return base_uri + suffix + ext - def getOutputPath(self, uri): bake_path = [self.out_dir] decoded_uri = urllib.parse.unquote(uri.lstrip('/')) @@ -109,7 +81,8 @@ # Generate the URL using the route. page = factory.buildPage() - uri = route.getUri(route_metadata, page, include_site_root=False) + uri = route.getUri(route_metadata, provider=page, + include_site_root=False) override = self.record.getOverrideEntry(factory, uri) if override is not None: @@ -161,7 +134,8 @@ invalidate_formatting = True while has_more_subs: - sub_uri = self.getOutputUri(uri, cur_sub) + sub_uri = route.getUri(route_metadata, sub_num=cur_sub, + provider=page, include_site_root=False) out_path = self.getOutputPath(sub_uri) # Check for up-to-date outputs.
--- a/piecrust/data/base.py Sun Feb 22 21:53:53 2015 -0800 +++ b/piecrust/data/base.py Sun Feb 22 22:03:54 2015 -0800 @@ -125,7 +125,7 @@ route = page.app.getRoute(page.source.name, page.source_metadata) if route is None: raise Exception("Can't get route for page: %s" % page.path) - return route.getUri(page.source_metadata, page) + return route.getUri(page.source_metadata, provider=page) def _loadCustom(self): page_url = self._get_uri()
--- a/piecrust/data/linker.py Sun Feb 22 21:53:53 2015 -0800 +++ b/piecrust/data/linker.py Sun Feb 22 22:03:54 2015 -0800 @@ -23,7 +23,6 @@ @property def children(self): self._linker._load() - print("GOT ", self._linker._items.keys()) if self._linker._self_item is None: return None return self._linker._self_item.config.get('__linker_child')
--- a/piecrust/routing.py Sun Feb 22 21:53:53 2015 -0800 +++ b/piecrust/routing.py Sun Feb 22 22:03:54 2015 -0800 @@ -1,4 +1,5 @@ import re +import os.path import logging @@ -8,6 +9,7 @@ route_re = re.compile(r'%((?P<qual>path):)?(?P<name>\w+)%') template_func_re = re.compile(r'^(?P<name>\w+)\((?P<first_arg>\w+)(?P<other_args>.*)\)\s*$') template_func_arg_re = re.compile(r',\s*(?P<arg>\w+)') +ugly_url_cleaner = re.compile(r'\.html$') class IRouteMetadataProvider(object): @@ -22,15 +24,22 @@ """ def __init__(self, app, cfg): self.app = app + + self.pretty_urls = app.config.get('site/pretty_urls') + self.trailing_slash = app.config.get('site/trailing_slash') + self.pagination_suffix_format = app.config.get( + '__cache/pagination_suffix_format') + self.uri_root = app.config.get('site/root').rstrip('/') + '/' + uri = cfg['url'] - self.uri_root = app.config.get('site/root').rstrip('/') + '/' self.uri_pattern = uri.lstrip('/') self.uri_format = route_re.sub(self._uriFormatRepl, self.uri_pattern) if app.config.get('site/show_debug_info'): self.uri_format += '?!debug' # Get the straight-forward regex for matching this URI pattern. - p = route_re.sub(self._uriPatternRepl, self.uri_pattern) + '$' + re_suffix = '$' + p = route_re.sub(self._uriPatternRepl, self.uri_pattern) + re_suffix self.uri_re = re.compile(p) # If the URI pattern has a 'path'-type component, we'll need to match @@ -76,6 +85,11 @@ return self.required_source_metadata.issubset(source_metadata.keys()) def matchUri(self, uri): + if not self.pretty_urls: + uri = ugly_url_cleaner.sub('', uri) + elif self.trailing_slash: + uri = uri.rstrip('/') + m = self.uri_re.match(uri) if m: return m.groupdict() @@ -85,17 +99,50 @@ return m.groupdict() return None - def getUri(self, source_metadata, provider=None, include_site_root=True): + def getUri(self, source_metadata, *, sub_num=1, provider=None, + include_site_root=True): if provider: source_metadata = dict(source_metadata) source_metadata.update(provider.getRouteMetadata()) + #TODO: fix this hard-coded shit for key in ['year', 'month', 'day']: if key in source_metadata and isinstance(source_metadata[key], str): source_metadata[key] = int(source_metadata[key]) + uri = self.uri_format % source_metadata + suffix = None + if sub_num > 1: + # Note that we know the pagination suffix starts with a slash. + suffix = self.pagination_suffix_format % sub_num + + if self.pretty_urls: + # Output will be: + # - `subdir/name` + # - `subdir/name/2` + # - `subdir/name.ext` + # - `subdir/name.ext/2` + if suffix: + uri = uri.rstrip('/') + suffix + if self.trailing_slash: + uri = uri.rstrip('/') + '/' + else: + # Output will be: + # - `subdir/name.html` + # - `subdir/name/2.html` + # - `subdir/name.ext` + # - `subdir/name/2.ext` + base_uri, ext = os.path.splitext(uri) + if not ext: + ext = '.html' + if suffix: + uri = base_uri + suffix + ext + else: + uri = base_uri + ext + if include_site_root: uri = self.uri_root + uri + return uri def _uriFormatRepl(self, m):
--- a/piecrust/serving.py Sun Feb 22 21:53:53 2015 -0800 +++ b/piecrust/serving.py Sun Feb 22 22:03:54 2015 -0800 @@ -8,6 +8,7 @@ import os.path import hashlib import logging +import datetime import threading from werkzeug.exceptions import ( NotFound, MethodNotAllowed, InternalServerError, HTTPException) @@ -130,7 +131,6 @@ # Create the app for this request. app = PieCrust(root_dir=self.root_dir, debug=self.debug) app.config.set('site/root', '/') - app.config.set('site/pretty_urls', True) app.config.set('server/is_serving', True) if (app.config.get('site/enable_debug_info') and '!debug' in request.args): @@ -170,7 +170,7 @@ full_path = os.path.join(mount, rel_req_path) try: response = self._make_wrapped_file_response( - environ, full_path) + environ, request, full_path) return response except OSError: pass @@ -203,7 +203,7 @@ try: response = self._make_wrapped_file_response( - environ, full_path) + environ, request, full_path) return response except OSError: pass @@ -217,7 +217,7 @@ if not os.path.isfile(full_path): return None - return self._make_wrapped_file_response(environ, full_path) + return self._make_wrapped_file_response(environ, request, full_path) def _try_serve_page(self, app, environ, request): # Try to find what matches the requested URL. @@ -353,11 +353,23 @@ return response - def _make_wrapped_file_response(self, environ, path): + def _make_wrapped_file_response(self, environ, request, path): logger.debug("Serving %s" % path) + + # Check if we can return a 304 status code. + mtime = os.path.getmtime(path) + etag_str = '%s$$%s' % (path, mtime) + etag = hashlib.md5(etag_str.encode('utf8')).hexdigest() + if etag in request.if_none_match: + response = Response() + response.status_code = 304 + return response + wrapper = wrap_file(environ, open(path, 'rb')) response = Response(wrapper) _, ext = os.path.splitext(path) + response.set_etag(etag) + response.last_modified = datetime.datetime.fromtimestamp(mtime) response.mimetype = self._mimetype_map.get( ext.lstrip('.'), 'text/plain') return response