changeset 11:617191dec18e

Fixes for Windows, make `findPagePath` return a ref path.
author Ludovic Chabant <ludovic@chabant.com>
date Mon, 18 Aug 2014 16:47:44 -0700
parents cd35d356ccce
children 30a42341cfa8
files piecrust/baking/baker.py piecrust/commands/builtin/util.py piecrust/processing/base.py piecrust/serving.py piecrust/sources/base.py piecrust/sources/posts.py tests/mockutil.py tests/test_baking_baker.py
diffstat 8 files changed, 81 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/baking/baker.py	Sun Aug 17 21:18:48 2014 -0700
+++ b/piecrust/baking/baker.py	Mon Aug 18 16:47:44 2014 -0700
@@ -74,7 +74,7 @@
             else:
                 bake_path.append(decoded_uri + '.html')
 
-        return os.path.join(*bake_path)
+        return os.path.normpath(os.path.join(*bake_path))
 
     def bake(self, factory, route, taxonomy_name=None, taxonomy_term=None):
         page = factory.buildPage()
@@ -155,9 +155,8 @@
                 ctx, rp = self._bakeSingle(page, sub_uri, cur_sub, out_path,
                         pagination_filter, custom_data)
             except Exception as ex:
-                logger.exception("Error baking page '%s' for URI '%s': %s" %
-                        (page.ref_spec, uri, ex))
-                raise
+                raise Exception("Error baking page '%s' for URI '%s'." %
+                        (page.ref_spec, uri)) from ex
 
             cur_record_entry.out_uris.append(sub_uri)
             cur_record_entry.out_paths.append(out_path)
--- a/piecrust/commands/builtin/util.py	Sun Aug 17 21:18:48 2014 -0700
+++ b/piecrust/commands/builtin/util.py	Mon Aug 18 16:47:44 2014 -0700
@@ -86,18 +86,19 @@
         app = ctx.app
         source = ctx.args.source
         metadata = source.buildMetadata(ctx.args)
-        page_path = source.findPagePath(metadata, MODE_CREATING)
-        name, ext = os.path.splitext(page_path)
+        rel_path, metadata = source.findPagePath(metadata, MODE_CREATING)
+        path = source.resolveRef(rel_path)
+        name, ext = os.path.splitext(path)
         if ext == '.*':
-            page_path = '%s.%s' % (name,
+            path = '%s.%s' % (name,
                     app.config.get('site/default_auto_format'))
-        if os.path.exists(page_path):
-            raise Exception("'%s' already exists." % page_path)
+        if os.path.exists(path):
+            raise Exception("'%s' already exists." % path)
 
-        logger.info("Creating page: %s" % os.path.relpath(page_path, app.root_dir))
-        if not os.path.exists(os.path.dirname(page_path)):
-            os.makedirs(os.path.dirname(page_path), 0o755)
-        with open(page_path, 'w') as f:
+        logger.info("Creating page: %s" % os.path.relpath(path, app.root_dir))
+        if not os.path.exists(os.path.dirname(path)):
+            os.makedirs(os.path.dirname(path), 0o755)
+        with open(path, 'w') as f:
             f.write('---\n')
             f.write('title: %s\n' % 'Unknown title')
             f.write('---\n')
--- a/piecrust/processing/base.py	Sun Aug 17 21:18:48 2014 -0700
+++ b/piecrust/processing/base.py	Mon Aug 18 16:47:44 2014 -0700
@@ -329,6 +329,8 @@
 
 
 def re_matchany(filename, patterns):
+    # skip patterns use a forward slash regardless of the platform.
+    filename = filename.replace('\\', '/')
     for pattern in patterns:
         if pattern.match(filename):
             return True
--- a/piecrust/serving.py	Sun Aug 17 21:18:48 2014 -0700
+++ b/piecrust/serving.py	Mon Aug 18 16:47:44 2014 -0700
@@ -1,6 +1,7 @@
 import re
 import gzip
 import time
+import os
 import os.path
 import hashlib
 import logging
@@ -17,7 +18,7 @@
 from piecrust.page import Page
 from piecrust.processing.base import ProcessorPipeline
 from piecrust.rendering import PageRenderingContext, render_page
-from piecrust.sources.base import MODE_PARSING
+from piecrust.sources.base import PageFactory, MODE_PARSING
 
 
 logger = logging.getLogger(__name__)
@@ -103,7 +104,7 @@
 
     def _try_serve_asset(self, app, environ, request):
         logger.debug("Searching for asset with path: %s" % request.path)
-        rel_req_path = request.path.lstrip('/')
+        rel_req_path = request.path.lstrip('/').replace('/', os.sep)
         entry = self._record.findEntry(rel_req_path)
         if entry is None:
             return None
@@ -161,16 +162,16 @@
         for route, route_metadata in routes:
             source = app.getSource(route.source_name)
             if route.taxonomy is None:
-                path, fac_metadata = source.findPagePath(
+                rel_path, fac_metadata = source.findPagePath(
                         route_metadata, MODE_PARSING)
-                if path is not None:
+                if rel_path is not None:
                     break
             else:
                 taxonomy = app.getTaxonomy(route.taxonomy)
                 term_value = route_metadata.get(taxonomy.term_name)
                 if term_value is not None:
                     tax_page_ref = taxonomy.getPageRef(source.name)
-                    path = tax_page_ref.path
+                    rel_path = tax_page_ref.rel_path
                     source = tax_page_ref.source
                     fac_metadata = {taxonomy.term_name: term_value}
                     break
@@ -180,7 +181,8 @@
                     (req_path, [r.source_name for r, _ in routes]))
 
         # Build the page and render it.
-        page = Page(source, fac_metadata, path)
+        fac = PageFactory(source, rel_path, fac_metadata)
+        page = fac.buildPage()
         render_ctx = PageRenderingContext(page, req_path, page_num)
         if taxonomy is not None:
             flt = PaginationFilter()
--- a/piecrust/sources/base.py	Sun Aug 17 21:18:48 2014 -0700
+++ b/piecrust/sources/base.py	Mon Aug 18 16:47:44 2014 -0700
@@ -294,6 +294,7 @@
         self.fs_endpoint = config.get('fs_endpoint', name)
         self.fs_endpoint_path = os.path.join(self.root_dir, CONTENT_DIR, self.fs_endpoint)
         self.supported_extensions = list(app.config.get('site/auto_formats').keys())
+        self.default_auto_format = app.config.get('site/default_auto_format')
 
     def buildPageFactories(self):
         logger.debug("Scanning for pages in: %s" % self.fs_endpoint_path)
@@ -313,22 +314,26 @@
                 fac_path = f
                 if rel_dirpath != '.':
                     fac_path = os.path.join(rel_dirpath, f)
+                fac_path = fac_path.replace('\\', '/')
                 yield PageFactory(self, fac_path, metadata)
 
     def resolveRef(self, ref_path):
-        return os.path.join(self.fs_endpoint_path, ref_path)
+        return os.path.normpath(
+                os.path.join(self.fs_endpoint_path, ref_path))
 
     def findPagePath(self, metadata, mode):
         uri_path = metadata['path']
         if uri_path == '':
             uri_path = '_index'
-        path = os.path.join(self.fs_endpoint_path, uri_path)
+        path = os.path.normpath(os.path.join(self.fs_endpoint_path, uri_path))
         _, ext = os.path.splitext(path)
 
         if mode == MODE_CREATING:
             if ext == '':
-                return '%s.*' % path
-            return path, metadata
+                path = '%s.%s' % (path, self.default_auto_format)
+            rel_path = os.path.relpath(path, self.fs_endpoint_path)
+            rel_path = rel_path.replace('\\', '/')
+            return rel_path, metadata
 
         if ext == '':
             paths_to_check = ['%s.%s' % (path, e)
@@ -337,7 +342,9 @@
             paths_to_check = [path]
         for path in paths_to_check:
             if os.path.isfile(path):
-                return path, metadata
+                rel_path = os.path.relpath(path, self.fs_endpoint_path)
+                rel_path = rel_path.replace('\\', '/')
+                return rel_path, metadata
 
         return None, None
 
@@ -393,5 +400,8 @@
 
     def __iter__(self):
         for page in self.it:
-            yield PaginationData(page)
+            if page is None:
+                yield None
+            else:
+                yield PaginationData(page)
 
--- a/piecrust/sources/posts.py	Sun Aug 17 21:18:48 2014 -0700
+++ b/piecrust/sources/posts.py	Mon Aug 18 16:47:44 2014 -0700
@@ -8,7 +8,7 @@
 from piecrust.sources.base import (PageSource, IPreparingSource,
         SimplePaginationSourceMixin,
         PageNotFoundError, InvalidFileSystemEndpointError,
-        PageFactory, MODE_CREATING)
+        PageFactory, MODE_CREATING, MODE_PARSING)
 
 
 logger = logging.getLogger(__name__)
@@ -22,13 +22,14 @@
         self.fs_endpoint = config.get('fs_endpoint', name)
         self.fs_endpoint_path = os.path.join(self.root_dir, CONTENT_DIR, self.fs_endpoint)
         self.supported_extensions = list(app.config.get('site/auto_formats').keys())
+        self.default_auto_format = app.config.get('site/default_auto_format')
 
     @property
     def path_format(self):
         return self.__class__.PATH_FORMAT
 
     def resolveRef(self, ref_path):
-        return os.path.join(self.fs_endpoint_path, ref_path)
+        return os.path.normpath(os.path.join(self.fs_endpoint_path, ref_path))
 
     def findPagePath(self, metadata, mode):
         year = metadata.get('year')
@@ -40,6 +41,8 @@
         if ext is None:
             if len(self.supported_extensions) == 1:
                 ext = self.supported_extensions[0]
+            elif mode == MODE_CREATING and self.default_auto_format:
+                ext = self.default_auto_format
 
         replacements = {
                 'year': year,
@@ -64,7 +67,8 @@
         if ext is None:
             needs_recapture = True
             replacements['ext'] = '*'
-        path = os.path.join(self.fs_endpoint_path, self.path_format % replacements)
+        path = os.path.normpath(os.path.join(
+                self.fs_endpoint_path, self.path_format % replacements))
 
         if needs_recapture:
             if mode == MODE_CREATING:
@@ -73,8 +77,8 @@
             if len(possible_paths) != 1:
                 raise PageNotFoundError()
             path = possible_paths[0]
-        elif not os.path.isfile(path):
-            raise PageNotFoundError()
+        elif mode == MODE_PARSING and not os.path.isfile(path):
+            raise PageNotFoundError(path)
 
         regex_repl = {
                 'year': '(?P<year>\d{4})',
@@ -83,8 +87,12 @@
                 'slug': '(?P<slug>.*)',
                 'ext': '(?P<ext>.*)'
                 }
-        pattern = os.path.join(self.fs_endpoint_path, self.path_format) % regex_repl
-        m = re.match(pattern, path)
+        #sanitized_fs_endpoint_path = (self.fs_endpoint_path.
+        #        replace('\\', '/').rstrip('/'))
+        #pattern = (re.escape(sanitized_fs_endpoint_path) + '/' +
+        #        self.path_format % regex_repl)
+        pattern = self.path_format % regex_repl + '$'
+        m = re.search(pattern, path.replace('\\', '/'))
         if not m:
             raise Exception("Expected to be able to match path with path "
                             "format: %s" % path)
@@ -94,8 +102,9 @@
                 'day': m.group('day'),
                 'slug': m.group('slug')
                 }
-
-        return path, fac_metadata
+        rel_path = os.path.relpath(path, self.fs_endpoint_path)
+        rel_path = rel_path.replace('\\', '/')
+        return rel_path, fac_metadata
 
     def setupPrepareParser(self, parser, app):
         parser.add_argument('-d', '--date', help="The date of the post, "
@@ -114,6 +123,7 @@
             raise InvalidFileSystemEndpointError(self.name, self.fs_endpoint_path)
 
     def _makeFactory(self, path, slug, year, month, day):
+        path = path.replace('\\', '/')
         timestamp = datetime.date(year, month, day)
         metadata = {
                 'slug': slug,
--- a/tests/mockutil.py	Sun Aug 17 21:18:48 2014 -0700
+++ b/tests/mockutil.py	Mon Aug 18 16:47:44 2014 -0700
@@ -40,6 +40,7 @@
                     "site:\n  title: Mock Website\n")
 
     def path(self, p):
+        p = p.replace('\\', '/')
         if p in ['/', '', None]:
             return '/%s' % self._root
         return '/%s/%s' % (self._root, p.lstrip('/'))
@@ -49,6 +50,7 @@
         return PieCrust(root_dir, cache=False)
 
     def withDir(self, path):
+        path = path.replace('\\', '/')
         cur = self._fs[self._root]
         for b in path.split('/'):
             if b not in cur:
@@ -57,6 +59,7 @@
         return self
 
     def withFile(self, path, contents):
+        path = path.replace('\\', '/')
         cur = self._fs[self._root]
         bits = path.split('/')
         for b in bits[:-1]:
@@ -73,11 +76,13 @@
         return self.withDir('kitchen/' + path)
 
     def withConfig(self, config):
-        return self.withFile('kitchen/_content/config.yml',
+        return self.withFile(
+                'kitchen/_content/config.yml',
                 yaml.dump(config))
 
     def withThemeConfig(self, config):
-        return self.withFile('kitchen/_content/theme/_content/theme_config.yml',
+        return self.withFile(
+                'kitchen/_content/theme/_content/theme_config.yml',
                 yaml.dump(config))
 
     def withPage(self, url, config=None, contents=None):
@@ -92,7 +97,7 @@
         if not ext:
             url += '.md'
         url = url.lstrip('/')
-        return self.withAsset('_content/pages/' + url, text)
+        return self.withAsset('_content/' + url, text)
 
     def withPageAsset(self, page_url, name, contents=None):
         contents = contents or "A test asset."
@@ -138,25 +143,31 @@
         self._patchers.append(mock.patch(name, func, **kwargs))
 
     def _open(self, path, *args, **kwargs):
-        path = os.path.abspath(path)
+        path = os.path.normpath(path)
         if path.startswith(resources_path):
             return self._originals['__main__.open'](path, **kwargs)
         e = self._getFsEntry(path)
+        if e is None:
+            raise OSError("No such file: %s" % path)
         return io.StringIO(e[0])
 
     def _codecsOpen(self, path, *args, **kwargs):
-        path = os.path.abspath(path)
+        path = os.path.normpath(path)
         if path.startswith(resources_path):
             return self._originals['codecs.open'](path, *args, **kwargs)
         e = self._getFsEntry(path)
+        if e is None:
+            raise OSError("No such file: %s" % path)
         return io.StringIO(e[0])
 
     def _listdir(self, path):
         if not path.startswith('/' + self._root):
             return self._originals['os.listdir'](path)
         e = self._getFsEntry(path)
+        if e is None:
+            raise OSError("No such directory: %s" % path)
         if not isinstance(e, dict):
-            raise Exception("'%s' is not a directory." % path)
+            raise OSError("'%s' is not a directory." % path)
         return list(e.keys())
 
     def _isdir(self, path):
@@ -175,12 +186,13 @@
             return self._originals['os.path.getmtime'](path)
         e = self._getFsEntry(path)
         if e is None:
-            raise OSError()
+            raise OSError("No such file: %s" % path)
         return e[1]['mtime']
 
     def _getFsEntry(self, path):
         cur = self._fs
-        bits = path.lstrip('/').split('/')
+        path = path.replace('\\', '/').lstrip('/')
+        bits = path.split('/')
         for p in bits:
             try:
                 cur = cur[p]
--- a/tests/test_baking_baker.py	Sun Aug 17 21:18:48 2014 -0700
+++ b/tests/test_baking_baker.py	Mon Aug 18 16:47:44 2014 -0700
@@ -1,3 +1,4 @@
+import os.path
 import pytest
 from piecrust.baking.baker import PageBaker
 from .mockutil import get_mock_app
@@ -36,6 +37,7 @@
     baker = PageBaker(app, '/destination')
     sub_uri = baker.getOutputUri(uri, page_num)
     path = baker.getOutputPath(sub_uri)
-    expected = '/destination/' + expected
+    expected = os.path.normpath(
+            os.path.join('/destination', expected))
     assert expected == path