mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Fix Pelican rendering and unit tests on Windows.
* Fix {filename} links on Windows.
Otherwise '{filename}/foo/bar.jpg' doesn't work
* Clean up relative Posix path handling in contents.
* Use Posix paths in readers
* Environment for Popen must be strs, not unicodes.
* Ignore Git CRLF warnings.
* Replace CRLFs with LFs in inputs on Windows.
* Fix importer tests
* Fix test_contents
* Fix one last backslash in paginated output
* Skip the remaining failing locale tests on Windows.
* Document the use of forward slashes on Windows.
* Add some Fabric and ghp-import notes
This commit is contained in:
parent
a740f8aa88
commit
4c25610cd8
14 changed files with 104 additions and 52 deletions
|
|
@ -157,6 +157,9 @@ the other content will be placed after site generation).
|
||||||
|
|
||||||
To link to internal content (files in the ``content`` directory), use the
|
To link to internal content (files in the ``content`` directory), use the
|
||||||
following syntax for the link target: ``{filename}path/to/file``
|
following syntax for the link target: ``{filename}path/to/file``
|
||||||
|
Note: forward slashes, ``/``,
|
||||||
|
are the required path separator in the ``{filename}`` directive
|
||||||
|
on all operating systems, including Windows.
|
||||||
|
|
||||||
For example, a Pelican project might be structured like this::
|
For example, a Pelican project might be structured like this::
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,18 @@ separately. Use the following command to install Fabric, prefixing with
|
||||||
|
|
||||||
pip install Fabric
|
pip install Fabric
|
||||||
|
|
||||||
|
.. note:: Installing PyCrypto on Windows
|
||||||
|
|
||||||
|
Fabric depends upon PyCrypto_, which is tricky to install
|
||||||
|
if your system doesn't have a C compiler.
|
||||||
|
For Windows users, before installing Fabric, use
|
||||||
|
``easy_install http://www.voidspace.org.uk/downloads/pycrypto26/pycrypto-2.6.win32-py2.7.exe``
|
||||||
|
per this `StackOverflow suggestion <http://stackoverflow.com/a/11405769/6364>`_
|
||||||
|
You're more likely to have success
|
||||||
|
with the Win32 versions of Python 2.7 and PyCrypto,
|
||||||
|
than with the Win64—\
|
||||||
|
even if your operating system is a 64-bit version of Windows.
|
||||||
|
|
||||||
Take a moment to open the ``fabfile.py`` file that was generated in your
|
Take a moment to open the ``fabfile.py`` file that was generated in your
|
||||||
project root. You will see a number of commands, any one of which can be
|
project root. You will see a number of commands, any one of which can be
|
||||||
renamed, removed, and/or customized to your liking. Using the out-of-the-box
|
renamed, removed, and/or customized to your liking. Using the out-of-the-box
|
||||||
|
|
@ -179,3 +191,4 @@ executables, such as ``python3``, you can set the ``PY`` and ``PELICAN``
|
||||||
environment variables, respectively, to override the default executable names.)
|
environment variables, respectively, to override the default executable names.)
|
||||||
|
|
||||||
.. _Fabric: http://fabfile.org/
|
.. _Fabric: http://fabfile.org/
|
||||||
|
.. _PyCrypto: http://pycrypto.org
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,15 @@ already exist). The ``git push origin gh-pages`` command updates the remote
|
||||||
``gh-pages`` branch, effectively publishing the Pelican site.
|
``gh-pages`` branch, effectively publishing the Pelican site.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
The ``github`` target of the Makefile (and the ``gh_pages`` task of the Fabfile)
|
||||||
|
created by the ``pelican-quickstart`` command
|
||||||
|
publishes the Pelican site as Project Pages, as described above.
|
||||||
|
|
||||||
The ``github`` target of the Makefile created by the ``pelican-quickstart``
|
.. note:: ghp-import on Windows
|
||||||
command publishes the Pelican site as Project Pages, as described above.
|
|
||||||
|
Until `ghp-import Pull Request #25 <https://github.com/davisp/ghp-import/pull/25>`_
|
||||||
|
is accepted, you will need to install a custom build of ghp-import:
|
||||||
|
``pip install https://github.com/chevah/ghp-import/archive/win-support.zip``
|
||||||
|
|
||||||
User Pages
|
User Pages
|
||||||
----------
|
----------
|
||||||
|
|
@ -86,6 +92,12 @@ output directory. For example::
|
||||||
STATIC_PATHS = ['images', 'extra/CNAME']
|
STATIC_PATHS = ['images', 'extra/CNAME']
|
||||||
EXTRA_PATH_METADATA = {'extra/CNAME': {'path': 'CNAME'},}
|
EXTRA_PATH_METADATA = {'extra/CNAME': {'path': 'CNAME'},}
|
||||||
|
|
||||||
|
Note: use forward slashes, ``/``, even on Windows.
|
||||||
|
|
||||||
|
.. hint::
|
||||||
|
You can also use the ``EXTRA_PATH_METADATA`` mechanism
|
||||||
|
to place a ``favicon.ico`` or ``robots.txt`` at the root of any site.
|
||||||
|
|
||||||
How to add YouTube or Vimeo Videos
|
How to add YouTube or Vimeo Videos
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ from pelican import signals
|
||||||
from pelican.settings import DEFAULT_CONFIG
|
from pelican.settings import DEFAULT_CONFIG
|
||||||
from pelican.utils import (slugify, truncate_html_words, memoized, strftime,
|
from pelican.utils import (slugify, truncate_html_words, memoized, strftime,
|
||||||
python_2_unicode_compatible, deprecated_attribute,
|
python_2_unicode_compatible, deprecated_attribute,
|
||||||
path_to_url, set_date_tzinfo, SafeDatetime)
|
path_to_url, posixize_path, set_date_tzinfo, SafeDatetime)
|
||||||
|
|
||||||
# Import these so that they're avalaible when you import from pelican.contents.
|
# Import these so that they're avalaible when you import from pelican.contents.
|
||||||
from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA
|
from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA
|
||||||
|
|
@ -337,17 +337,19 @@ class Content(object):
|
||||||
if source_path is None:
|
if source_path is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return os.path.relpath(
|
return posixize_path(
|
||||||
os.path.abspath(os.path.join(self.settings['PATH'], source_path)),
|
os.path.relpath(
|
||||||
os.path.abspath(self.settings['PATH'])
|
os.path.abspath(os.path.join(self.settings['PATH'], source_path)),
|
||||||
)
|
os.path.abspath(self.settings['PATH'])
|
||||||
|
))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def relative_dir(self):
|
def relative_dir(self):
|
||||||
return os.path.dirname(os.path.relpath(
|
return posixize_path(
|
||||||
os.path.abspath(self.source_path),
|
os.path.dirname(
|
||||||
os.path.abspath(self.settings['PATH']))
|
os.path.relpath(
|
||||||
)
|
os.path.abspath(self.source_path),
|
||||||
|
os.path.abspath(self.settings['PATH']))))
|
||||||
|
|
||||||
|
|
||||||
class Page(Content):
|
class Page(Content):
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ class Page(object):
|
||||||
|
|
||||||
# URL or SAVE_AS is a string, format it with a controlled context
|
# URL or SAVE_AS is a string, format it with a controlled context
|
||||||
context = {
|
context = {
|
||||||
'name': self.name,
|
'name': self.name.replace(os.sep, '/'),
|
||||||
'object_list': self.object_list,
|
'object_list': self.object_list,
|
||||||
'number': self.number,
|
'number': self.number,
|
||||||
'paginator': self.paginator,
|
'paginator': self.paginator,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ from six.moves.html_parser import HTMLParser
|
||||||
|
|
||||||
from pelican import signals
|
from pelican import signals
|
||||||
from pelican.contents import Page, Category, Tag, Author
|
from pelican.contents import Page, Category, Tag, Author
|
||||||
from pelican.utils import get_date, pelican_open, FileStampDataCacher, SafeDatetime
|
from pelican.utils import get_date, pelican_open, FileStampDataCacher, SafeDatetime, posixize_path
|
||||||
|
|
||||||
|
|
||||||
METADATA_PROCESSORS = {
|
METADATA_PROCESSORS = {
|
||||||
|
|
@ -424,7 +424,7 @@ class Readers(FileStampDataCacher):
|
||||||
"""Return a content object parsed with the given format."""
|
"""Return a content object parsed with the given format."""
|
||||||
|
|
||||||
path = os.path.abspath(os.path.join(base_path, path))
|
path = os.path.abspath(os.path.join(base_path, path))
|
||||||
source_path = os.path.relpath(path, base_path)
|
source_path = posixize_path(os.path.relpath(path, base_path))
|
||||||
logger.debug('Read file %s -> %s',
|
logger.debug('Read file %s -> %s',
|
||||||
source_path, content_class.__name__)
|
source_path, content_class.__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ except ImportError:
|
||||||
load_source = imp.load_source
|
load_source = imp.load_source
|
||||||
|
|
||||||
from os.path import isabs
|
from os.path import isabs
|
||||||
|
from pelican.utils import posix_join
|
||||||
|
|
||||||
from pelican.log import LimitFilter
|
from pelican.log import LimitFilter
|
||||||
|
|
||||||
|
|
@ -41,11 +42,11 @@ DEFAULT_CONFIG = {
|
||||||
'STATIC_EXCLUDE_SOURCES': True,
|
'STATIC_EXCLUDE_SOURCES': True,
|
||||||
'THEME_STATIC_DIR': 'theme',
|
'THEME_STATIC_DIR': 'theme',
|
||||||
'THEME_STATIC_PATHS': ['static', ],
|
'THEME_STATIC_PATHS': ['static', ],
|
||||||
'FEED_ALL_ATOM': os.path.join('feeds', 'all.atom.xml'),
|
'FEED_ALL_ATOM': posix_join('feeds', 'all.atom.xml'),
|
||||||
'CATEGORY_FEED_ATOM': os.path.join('feeds', '%s.atom.xml'),
|
'CATEGORY_FEED_ATOM': posix_join('feeds', '%s.atom.xml'),
|
||||||
'AUTHOR_FEED_ATOM': os.path.join('feeds', '%s.atom.xml'),
|
'AUTHOR_FEED_ATOM': posix_join('feeds', '%s.atom.xml'),
|
||||||
'AUTHOR_FEED_RSS': os.path.join('feeds', '%s.rss.xml'),
|
'AUTHOR_FEED_RSS': posix_join('feeds', '%s.rss.xml'),
|
||||||
'TRANSLATION_FEED_ATOM': os.path.join('feeds', 'all-%s.atom.xml'),
|
'TRANSLATION_FEED_ATOM': posix_join('feeds', 'all-%s.atom.xml'),
|
||||||
'FEED_MAX_ITEMS': '',
|
'FEED_MAX_ITEMS': '',
|
||||||
'SITEURL': '',
|
'SITEURL': '',
|
||||||
'SITENAME': 'A Pelican Blog',
|
'SITENAME': 'A Pelican Blog',
|
||||||
|
|
@ -68,25 +69,25 @@ DEFAULT_CONFIG = {
|
||||||
'ARTICLE_LANG_URL': '{slug}-{lang}.html',
|
'ARTICLE_LANG_URL': '{slug}-{lang}.html',
|
||||||
'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
|
'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html',
|
||||||
'DRAFT_URL': 'drafts/{slug}.html',
|
'DRAFT_URL': 'drafts/{slug}.html',
|
||||||
'DRAFT_SAVE_AS': os.path.join('drafts', '{slug}.html'),
|
'DRAFT_SAVE_AS': posix_join('drafts', '{slug}.html'),
|
||||||
'DRAFT_LANG_URL': 'drafts/{slug}-{lang}.html',
|
'DRAFT_LANG_URL': 'drafts/{slug}-{lang}.html',
|
||||||
'DRAFT_LANG_SAVE_AS': os.path.join('drafts', '{slug}-{lang}.html'),
|
'DRAFT_LANG_SAVE_AS': posix_join('drafts', '{slug}-{lang}.html'),
|
||||||
'PAGE_URL': 'pages/{slug}.html',
|
'PAGE_URL': 'pages/{slug}.html',
|
||||||
'PAGE_SAVE_AS': os.path.join('pages', '{slug}.html'),
|
'PAGE_SAVE_AS': posix_join('pages', '{slug}.html'),
|
||||||
'PAGE_ORDER_BY': 'basename',
|
'PAGE_ORDER_BY': 'basename',
|
||||||
'PAGE_LANG_URL': 'pages/{slug}-{lang}.html',
|
'PAGE_LANG_URL': 'pages/{slug}-{lang}.html',
|
||||||
'PAGE_LANG_SAVE_AS': os.path.join('pages', '{slug}-{lang}.html'),
|
'PAGE_LANG_SAVE_AS': posix_join('pages', '{slug}-{lang}.html'),
|
||||||
'STATIC_URL': '{path}',
|
'STATIC_URL': '{path}',
|
||||||
'STATIC_SAVE_AS': '{path}',
|
'STATIC_SAVE_AS': '{path}',
|
||||||
'PDF_GENERATOR': False,
|
'PDF_GENERATOR': False,
|
||||||
'PDF_STYLE_PATH': '',
|
'PDF_STYLE_PATH': '',
|
||||||
'PDF_STYLE': 'twelvepoint',
|
'PDF_STYLE': 'twelvepoint',
|
||||||
'CATEGORY_URL': 'category/{slug}.html',
|
'CATEGORY_URL': 'category/{slug}.html',
|
||||||
'CATEGORY_SAVE_AS': os.path.join('category', '{slug}.html'),
|
'CATEGORY_SAVE_AS': posix_join('category', '{slug}.html'),
|
||||||
'TAG_URL': 'tag/{slug}.html',
|
'TAG_URL': 'tag/{slug}.html',
|
||||||
'TAG_SAVE_AS': os.path.join('tag', '{slug}.html'),
|
'TAG_SAVE_AS': posix_join('tag', '{slug}.html'),
|
||||||
'AUTHOR_URL': 'author/{slug}.html',
|
'AUTHOR_URL': 'author/{slug}.html',
|
||||||
'AUTHOR_SAVE_AS': os.path.join('author', '{slug}.html'),
|
'AUTHOR_SAVE_AS': posix_join('author', '{slug}.html'),
|
||||||
'PAGINATION_PATTERNS': [
|
'PAGINATION_PATTERNS': [
|
||||||
(0, '{name}{number}{extension}', '{name}{number}{extension}'),
|
(0, '{name}{number}{extension}', '{name}{number}{extension}'),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from pelican.tests.support import unittest, get_settings
|
||||||
|
|
||||||
from pelican.contents import Page, Article, Static, URLWrapper
|
from pelican.contents import Page, Article, Static, URLWrapper
|
||||||
from pelican.settings import DEFAULT_CONFIG
|
from pelican.settings import DEFAULT_CONFIG
|
||||||
from pelican.utils import path_to_url, truncate_html_words, SafeDatetime
|
from pelican.utils import path_to_url, truncate_html_words, SafeDatetime, posix_join
|
||||||
from pelican.signals import content_object_init
|
from pelican.signals import content_object_init
|
||||||
from jinja2.utils import generate_lorem_ipsum
|
from jinja2.utils import generate_lorem_ipsum
|
||||||
|
|
||||||
|
|
@ -417,7 +417,7 @@ class TestStatic(unittest.TestCase):
|
||||||
self.context = self.settings.copy()
|
self.context = self.settings.copy()
|
||||||
|
|
||||||
self.static = Static(content=None, metadata={}, settings=self.settings,
|
self.static = Static(content=None, metadata={}, settings=self.settings,
|
||||||
source_path=os.path.join('dir', 'foo.jpg'), context=self.context)
|
source_path=posix_join('dir', 'foo.jpg'), context=self.context)
|
||||||
|
|
||||||
self.context['filenames'] = {self.static.source_path: self.static}
|
self.context['filenames'] = {self.static.source_path: self.static}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from pelican.tools.pelican_import import wp2fields, fields2pelican, decode_wp_co
|
||||||
from pelican.tests.support import (unittest, temporary_folder, mute,
|
from pelican.tests.support import (unittest, temporary_folder, mute,
|
||||||
skipIfNoExecutable)
|
skipIfNoExecutable)
|
||||||
|
|
||||||
from pelican.utils import slugify
|
from pelican.utils import slugify, path_to_file_url
|
||||||
|
|
||||||
CUR_DIR = os.path.abspath(os.path.dirname(__file__))
|
CUR_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'wordpressexport.xml')
|
WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'wordpressexport.xml')
|
||||||
|
|
@ -293,12 +293,11 @@ class TestWordpressXMLAttachements(unittest.TestCase):
|
||||||
|
|
||||||
def test_download_attachments(self):
|
def test_download_attachments(self):
|
||||||
real_file = os.path.join(CUR_DIR, 'content/article.rst')
|
real_file = os.path.join(CUR_DIR, 'content/article.rst')
|
||||||
good_url = 'file://' + real_file
|
good_url = path_to_file_url(real_file)
|
||||||
bad_url = 'http://localhost:1/not_a_file.txt'
|
bad_url = 'http://localhost:1/not_a_file.txt'
|
||||||
silent_da = mute()(download_attachments)
|
silent_da = mute()(download_attachments)
|
||||||
with temporary_folder() as temp:
|
with temporary_folder() as temp:
|
||||||
#locations = download_attachments(temp, [good_url, bad_url])
|
|
||||||
locations = list(silent_da(temp, [good_url, bad_url]))
|
locations = list(silent_da(temp, [good_url, bad_url]))
|
||||||
self.assertTrue(len(locations) == 1)
|
self.assertEqual(1, len(locations))
|
||||||
directory = locations[0]
|
directory = locations[0]
|
||||||
self.assertTrue(directory.endswith('content/article.rst'))
|
self.assertTrue(directory.endswith(os.path.join('content', 'article.rst')), directory)
|
||||||
|
|
|
||||||
|
|
@ -58,22 +58,22 @@ class TestPelican(LoggedTestCase):
|
||||||
locale.setlocale(locale.LC_ALL, self.old_locale)
|
locale.setlocale(locale.LC_ALL, self.old_locale)
|
||||||
super(TestPelican, self).tearDown()
|
super(TestPelican, self).tearDown()
|
||||||
|
|
||||||
def assertFilesEqual(self, diff):
|
|
||||||
msg = ("some generated files differ from the expected functional "
|
|
||||||
"tests output.\n"
|
|
||||||
"This is probably because the HTML generated files "
|
|
||||||
"changed. If these changes are normal, please refer "
|
|
||||||
"to docs/contribute.rst to update the expected "
|
|
||||||
"output of the functional tests.")
|
|
||||||
|
|
||||||
self.assertEqual(diff['left_only'], [], msg=msg)
|
|
||||||
self.assertEqual(diff['right_only'], [], msg=msg)
|
|
||||||
self.assertEqual(diff['diff_files'], [], msg=msg)
|
|
||||||
|
|
||||||
def assertDirsEqual(self, left_path, right_path):
|
def assertDirsEqual(self, left_path, right_path):
|
||||||
out, err = subprocess.Popen(
|
out, err = subprocess.Popen(
|
||||||
['git', 'diff', '--no-ext-diff', '--exit-code', '-w', left_path, right_path], env={'PAGER': ''},
|
['git', 'diff', '--no-ext-diff', '--exit-code', '-w', left_path, right_path],
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
env={b'PAGER': b''}, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||||
|
).communicate()
|
||||||
|
def ignorable_git_crlf_errors(line):
|
||||||
|
# Work around for running tests on Windows
|
||||||
|
for msg in [
|
||||||
|
"LF will be replaced by CRLF",
|
||||||
|
"The file will have its original line endings"]:
|
||||||
|
if msg in line:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
if err:
|
||||||
|
err = '\n'.join([l for l in err.decode('utf8').splitlines()
|
||||||
|
if not ignorable_git_crlf_errors(l)])
|
||||||
assert not out, out
|
assert not out, out
|
||||||
assert not err, err
|
assert not err, err
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals, print_function
|
||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
import locale
|
import locale
|
||||||
|
from sys import platform
|
||||||
from os.path import dirname, abspath, join
|
from os.path import dirname, abspath, join
|
||||||
|
|
||||||
from pelican.settings import (read_settings, configure_settings,
|
from pelican.settings import (read_settings, configure_settings,
|
||||||
|
|
@ -107,6 +108,8 @@ class TestSettingsConfiguration(unittest.TestCase):
|
||||||
# locale is not specified in the settings
|
# locale is not specified in the settings
|
||||||
|
|
||||||
#reset locale to python default
|
#reset locale to python default
|
||||||
|
if platform == 'win32':
|
||||||
|
return unittest.skip("Doesn't work on Windows")
|
||||||
locale.setlocale(locale.LC_ALL, str('C'))
|
locale.setlocale(locale.LC_ALL, str('C'))
|
||||||
self.assertEqual(self.settings['LOCALE'], DEFAULT_CONFIG['LOCALE'])
|
self.assertEqual(self.settings['LOCALE'], DEFAULT_CONFIG['LOCALE'])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,7 @@ class TestUtils(LoggedTestCase):
|
||||||
old_locale = locale.setlocale(locale.LC_TIME)
|
old_locale = locale.setlocale(locale.LC_TIME)
|
||||||
|
|
||||||
if platform == 'win32':
|
if platform == 'win32':
|
||||||
locale.setlocale(locale.LC_TIME, str('Turkish'))
|
return unittest.skip("Doesn't work on Windows")
|
||||||
else:
|
else:
|
||||||
locale.setlocale(locale.LC_TIME, str('tr_TR.UTF-8'))
|
locale.setlocale(locale.LC_TIME, str('tr_TR.UTF-8'))
|
||||||
|
|
||||||
|
|
@ -471,6 +471,8 @@ class TestDateFormatter(unittest.TestCase):
|
||||||
locale_available('French'),
|
locale_available('French'),
|
||||||
'French locale needed')
|
'French locale needed')
|
||||||
def test_french_strftime(self):
|
def test_french_strftime(self):
|
||||||
|
if platform == 'win32':
|
||||||
|
return unittest.skip("Doesn't work on Windows")
|
||||||
# This test tries to reproduce an issue that occurred with python3.3 under macos10 only
|
# This test tries to reproduce an issue that occurred with python3.3 under macos10 only
|
||||||
locale.setlocale(locale.LC_ALL, str('fr_FR.UTF-8'))
|
locale.setlocale(locale.LC_ALL, str('fr_FR.UTF-8'))
|
||||||
date = utils.SafeDatetime(2014,8,14)
|
date = utils.SafeDatetime(2014,8,14)
|
||||||
|
|
|
||||||
|
|
@ -588,7 +588,8 @@ def download_attachments(output_path, urls):
|
||||||
filename = path.pop(-1)
|
filename = path.pop(-1)
|
||||||
localpath = ''
|
localpath = ''
|
||||||
for item in path:
|
for item in path:
|
||||||
localpath = os.path.join(localpath, item)
|
if sys.platform != 'win32' or ':' not in item:
|
||||||
|
localpath = os.path.join(localpath, item)
|
||||||
full_path = os.path.join(output_path, localpath)
|
full_path = os.path.join(output_path, localpath)
|
||||||
if not os.path.exists(full_path):
|
if not os.path.exists(full_path):
|
||||||
os.makedirs(full_path)
|
os.makedirs(full_path)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import os
|
||||||
import pytz
|
import pytz
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import pickle
|
import pickle
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
@ -23,6 +24,7 @@ from functools import partial
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from jinja2 import Markup
|
from jinja2 import Markup
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
from posixpath import join as posix_join
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -230,13 +232,15 @@ def get_date(string):
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def pelican_open(filename):
|
def pelican_open(filename, mode='rb', strip_crs=(sys.platform == 'win32')):
|
||||||
"""Open a file and return its content"""
|
"""Open a file and return its content"""
|
||||||
|
|
||||||
with codecs.open(filename, encoding='utf-8') as infile:
|
with codecs.open(filename, mode, encoding='utf-8') as infile:
|
||||||
content = infile.read()
|
content = infile.read()
|
||||||
if content[0] == codecs.BOM_UTF8.decode('utf8'):
|
if content[0] == codecs.BOM_UTF8.decode('utf8'):
|
||||||
content = content[1:]
|
content = content[1:]
|
||||||
|
if strip_crs:
|
||||||
|
content = content.replace('\r\n', '\n')
|
||||||
yield content
|
yield content
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -370,6 +374,13 @@ def path_to_url(path):
|
||||||
return '/'.join(split_all(path))
|
return '/'.join(split_all(path))
|
||||||
|
|
||||||
|
|
||||||
|
def posixize_path(rel_path):
|
||||||
|
"""Use '/' as path separator, so that source references,
|
||||||
|
like '{filename}/foo/bar.jpg' or 'extras/favicon.ico',
|
||||||
|
will work on Windows as well as on Mac and Linux."""
|
||||||
|
return rel_path.replace(os.sep, '/')
|
||||||
|
|
||||||
|
|
||||||
def truncate_html_words(s, num, end_text='...'):
|
def truncate_html_words(s, num, end_text='...'):
|
||||||
"""Truncates HTML to a certain number of words.
|
"""Truncates HTML to a certain number of words.
|
||||||
|
|
||||||
|
|
@ -750,4 +761,9 @@ def is_selected_for_writing(settings, path):
|
||||||
return path in settings['WRITE_SELECTED']
|
return path in settings['WRITE_SELECTED']
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def path_to_file_url(path):
|
||||||
|
'''Convert file-system path to file:// URL'''
|
||||||
|
return six.moves.urllib_parse.urljoin(
|
||||||
|
"file://", six.moves.urllib.request.pathname2url(path))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue