Skip to content
Permalink
Browse files

Merge branch 'master' into generalization-of-taxonomies

  • Loading branch information
felixfontein committed Nov 20, 2016
2 parents b71a818 + d6dde70 commit 37c3832d37da56e0fd0744950e992766bcddb3df
@@ -4,12 +4,16 @@ New in master
Bugfixes
--------

* Make "data" from global context available to templated shortcodes (Issue #2488)
* Make ``data`` from global context available to templated shortcodes
as ``global_data`` (Issue #2488)
* Don't crash if plugins is a file (Issue #2539)
* Don't mangle bare ``#`` links (Issue #2553)

Features
--------

* Improving handling of .dep files, and allowing compilers to specify
additional targets for the render_posts task (Issue #2536)
* ``render_template`` and ``generic_renderer`` can now create HTML
fragments.
* Allow posts to set custom ``URL_TYPE`` by using the ``url_type``
@@ -48,8 +48,9 @@ configuration file:
# Some things in the middle you don't really need to change...
#

POSTS = ()
# you can also keep the current content of POSTS if you want a blog with your site
POSTS = ()
# remove destination directory to generate pages in the root directory
PAGES = (
("pages/*.rst", "", "story.tmpl"),
("pages/*.txt", "", "story.tmpl"),
@@ -1086,9 +1086,10 @@ In that case, the template engine used will be your theme's and the arguments yo
as well as the global context from your ``conf.py``, are available to the template you
are creating.

You can use anything defined in your confguration's ``GLOBAL_CONTEXT`` as variables in your
shortcode template, with a caveat: Because of an unfortunate implementation detail, data is called
"global_data" when used in a shortcode.
You can use anything defined in your confguration's ``GLOBAL_CONTEXT`` as
variables in your shortcode template, with a caveat: Because of an unfortunate
implementation detail (a name conflict), ``data`` is called ``global_data``
when used in a shortcode.

The Global Context and Data files
---------------------------------
@@ -1119,6 +1120,8 @@ JSON/YAML/TOML files and Nikola generates a large page with data from all data
files. (This is especially useful with some automatic rebuild feature, like
those documented in `Deployment`_)

Data files are also available as ``global_data``, to avoid name conflicts in
shortcodes. (``global_data`` works everywhere.)

Redirections
------------
@@ -247,6 +247,7 @@ def clean_tasks(self, tasks, dryrun):
shutil.rmtree(cache_folder)
return super(Clean, self).clean_tasks(tasks, dryrun)


# Nikola has its own "auto" commands that uses livereload.
# Expose original doit "auto" command as "doit_auto".
DoitAuto.name = 'doit_auto'
@@ -7,5 +7,6 @@ def hello(name='world'):
greeting = "hello " + name
print(greeting)


if __name__ == "__main__":
hello(*sys.argv[1:])
@@ -1168,6 +1168,8 @@ def _set_global_context_from_data(self):
data = utils.load_data(fname)
key = os.path.splitext(fname.split(os.sep, 1)[1])[0]
self._GLOBAL_CONTEXT['data'][key] = data
# Offer global_data as an alias for data (Issue #2488)
self._GLOBAL_CONTEXT['global_data'] = self._GLOBAL_CONTEXT['data']

def _activate_plugins_of_category(self, category):
"""Activate all the plugins of a given category and return them."""
@@ -1382,6 +1384,10 @@ def url_replacer(self, src, dst, lang=None, url_type=None):
lang is used for language-sensitive URLs in link://
url_type is used to determine final link appearance, defaulting to URL_TYPE from config
"""
# Avoid mangling links within the page
if dst.startswith('#'):
return dst

parsed_src = urlsplit(src)
src_elems = parsed_src.path.split('/')[1:]
dst_url = urlparse(dst)
@@ -1495,13 +1501,12 @@ def _make_renderfunc(self, t_data, fname=None):
Global context keys are made available as part of the context,
respecting locale.
As a special quirk, the "data" key from global_context is made
available as "global_data" because of name clobbering.
As a special quirk, the "data" key from global_context is
available only as "global_data" because of name clobbering.
"""
def render_shortcode(*args, **kw):
context = self.GLOBAL_CONTEXT.copy()
context['global_data'] = context['data']
context.update(kw)
context['_args'] = args
context['lang'] = utils.LocaleBorg().current_lang
@@ -155,6 +155,7 @@ def help(self):
text.append(self.doc_description)
return "\n".join(text)


DoitCommand.help = help


@@ -251,6 +252,7 @@ class PageCompiler(BasePlugin):
friendly_name = ''
demote_headers = False
supports_onefile = True
use_dep_file = True # If set to false, the .dep file is never written and not automatically added as a target
default_metadata = {
'title': '',
'slug': '',
@@ -263,9 +265,13 @@ class PageCompiler(BasePlugin):
}
config_dependencies = []

def _read_extra_deps(self, post):
def get_dep_filename(self, post, lang):
"""Return the .dep file's name for the given post and language."""
return post.translated_base_path(lang) + '.dep'

def _read_extra_deps(self, post, lang):
"""Read contents of .dep file and return them as a list."""
dep_path = post.base_path + '.dep'
dep_path = self.get_dep_filename(post, lang)
if os.path.isfile(dep_path):
with io.open(dep_path, 'r+', encoding='utf8') as depf:
deps = [l.strip() for l in depf.readlines()]
@@ -274,7 +280,21 @@ def _read_extra_deps(self, post):

def register_extra_dependencies(self, post):
"""Add dependency to post object to check .dep file."""
post.add_dependency(lambda: self._read_extra_deps(post), 'fragment')
def create_lambda(lang):
# We create a lambda like this so we can pass `lang` to it, because if we didn’t
# add that function, `lang` would always be the last language in TRANSLATIONS.
# (See http://docs.python-guide.org/en/latest/writing/gotchas/#late-binding-closures)
return lambda: self._read_extra_deps(post, lang)

for lang in self.site.config['TRANSLATIONS']:
post.add_dependency(create_lambda(lang), 'fragment', lang=lang)

def get_extra_targets(self, post, lang, dest):
"""Return a list of extra targets for the render_posts task when compiling the post for the specified language."""
if self.use_dep_file:
return [self.get_dep_filename(post, lang)]
else:
return []

def compile(self, source, dest, is_two_file=True, post=None, lang=None):
"""Compile the source file into HTML and save as dest."""
@@ -262,7 +262,7 @@ def _execute(self, options, args):
self.site.config['post_pages'])

if content_format not in compiler_names:
LOGGER.error("Unknown {0} format {1}, maybe you need to install a plugin?".format(content_type, content_format))
LOGGER.error("Unknown {0} format {1}, maybe you need to install a plugin or enable an existing one?".format(content_type, content_format))
self.print_compilers()
return
compiler_plugin = self.site.plugin_manager.getPluginByName(
@@ -203,6 +203,7 @@ def makeExtension(configs=None): # pragma: no cover
"""Make Markdown extension."""
return GistExtension(configs)


if __name__ == '__main__':
import doctest

@@ -92,6 +92,7 @@ def makeExtension(configs=None): # pragma: no cover
"""Make Markdown extension."""
return PodcastExtension(configs)


if __name__ == '__main__':
import doctest
doctest.testmod(optionflags=(doctest.NORMALIZE_WHITESPACE +
@@ -206,6 +206,7 @@ def shortcode_role(name, rawtext, text, lineno, inliner,
"""A shortcode role that passes through raw inline HTML."""
return [docutils.nodes.raw('', text, format='html')], []


roles.register_canonical_role('raw-html', shortcode_role)
roles.register_canonical_role('html', shortcode_role)
roles.register_canonical_role('sc', shortcode_role)
@@ -297,6 +298,7 @@ def rst2html(source, source_path=None, source_class=docutils.io.StringInput,

return pub.writer.parts['docinfo'] + pub.writer.parts['fragment'], pub.document.reporter.max_level, pub.settings.record_dependencies


# Alignment helpers for extensions
_align_options_base = ('left', 'center', 'right')

@@ -119,6 +119,7 @@ def run(self):

return [node]


# Monkey-patch: replace insane docutils CodeBlock with our implementation.
docutils.parsers.rst.directives.body.CodeBlock = CodeBlock
docutils.parsers.rst.directives.misc.CodeBlock = CodeBlock
@@ -307,5 +307,6 @@ def _do_post_list(start=None, stop=None, reverse=False, tags=None, categories=No
template, None, template_data)
return output, template_deps


# Request file name from shortcode (Issue #2412)
_do_post_list.nikola_shortcode_pass_filename = True
@@ -85,11 +85,12 @@ def tl_ch():
deps_dict[k] = self.site.config.get(k)
dest = post.translated_base_path(lang)
file_dep = [p for p in post.fragment_deps(lang) if not p.startswith("####MAGIC####")]
extra_targets = post.compiler.get_extra_targets(post, lang, dest)
task = {
'basename': self.name,
'name': dest,
'file_dep': file_dep,
'targets': [dest],
'targets': [dest] + extra_targets,
'actions': [(post.compile, (lang, )),
(update_deps, (post, lang, )),
],
@@ -36,12 +36,12 @@
from nikola.utils import get_logger, STDERR_HANDLER
from nikola.plugin_categories import LateTask

PY2_AND_NO_PY3_WARNING = """Nikola is going to deprecate Python 2 support in 2016. Your current
PY2_AND_NO_PY3_WARNING = """Nikola is going to deprecate Python 2 support in 2016 (or 2017). Your current
version will continue to work, but please consider upgrading to Python 3.
Please check http://bit.ly/1FKEsiX for details.
"""
PY2_WARNING = """Nikola is going to deprecate Python 2 support in 2016. You already have Python 3
PY2_WARNING = """Nikola is going to deprecate Python 2 support in 2016 (or 2017). You already have Python 3
available in your system. Why not switch?
Please check http://bit.ly/1FKEsiX for details.
@@ -322,6 +322,7 @@ def get_lastmod(self, p):
lastmod = datetime.datetime.utcfromtimestamp(os.stat(p).st_mtime).replace(tzinfo=dateutil.tz.gettz('UTC'), second=0, microsecond=0).isoformat().replace('+00:00', 'Z')
return lastmod


if __name__ == '__main__':
import doctest
doctest.testmod()
@@ -461,10 +461,13 @@ def register_depfile(self, dep, dest=None, lang=None):
self._depfile[dest].append(dep)

@staticmethod
def write_depfile(dest, deps_list):
def write_depfile(dest, deps_list, post=None, lang=None):
"""Write a depfile for a given language."""
deps_path = dest + '.dep'
if deps_list:
if post is None or lang is None:
deps_path = dest + '.dep'
else:
deps_path = post.compiler.get_dep_filename(post, lang)
if deps_list or (post.compiler.use_dep_file if post else False):
deps_list = [p for p in deps_list if p != dest] # Don't depend on yourself (#1671)
with io.open(deps_path, "w+", encoding="utf8") as deps_file:
deps_file.write('\n'.join(deps_list))
@@ -543,7 +546,7 @@ def wrap_encrypt(path, password):
self.is_two_file,
self,
lang)
Post.write_depfile(dest, self._depfile[dest])
Post.write_depfile(dest, self._depfile[dest], post=self, lang=lang)

signal('compiled').send({
'source': self.translated_source_path(lang),
@@ -165,6 +165,7 @@ def showwarning(message, category, filename, lineno, file=None, line=None):
n = str(category)
get_logger(n, STDERR_HANDLER).warn('{0}:{1}: {2}'.format(filename, lineno, message))


warnings.showwarning = showwarning


@@ -761,6 +762,7 @@ def remove_file(source):
elif os.path.isfile(source) or os.path.islink(source):
os.remove(source)


# slugify is adopted from
# http://code.activestate.com/recipes/
# 577257-slugify-make-a-string-usable-in-a-url-or-filename/
@@ -1,8 +1,8 @@
-r requirements-extras.txt
mock==2.0.0
coverage==4.2
pytest==3.0.3
pytest==3.0.4
pytest-cov==2.4.0
freezegun==0.3.7
freezegun==0.3.8
python-coveralls==2.9.0
colorama>=0.3.4

0 comments on commit 37c3832

Please sign in to comment.
You can’t perform that action at this time.