Пример #1
0
    def testBlocks_multipleBlocks(self):
        data = self.join_lines(
        '<block one>',
        'def one():',
        '    return 1',
        '</block one>',
        'Intervening text',
        '<block two>',
        'def two():',
        '    return 2',
        '</block two>',
        '<block three>',
        'content',
        '</block three>')
        with self.mock_open({'some/path': data}):
            block, variables = link('some/path')

        self.assertEqual({}, variables)
        block_one = Block('some/path', 'one', [self.join_lines(
            'def one():',
            '    return 1')])
        block_two = Block('some/path', 'two', [self.join_lines(
            'def two():',
            '    return 2')])
        block_three = Block('some/path', 'three', ['content'])
        block_all = Block('some/path', 'all', [
            block_one,
            'Intervening text',
            block_two,
            block_three
        ])
        self.assertBlockEqual(block_all, block)
Пример #2
0
    def testNestedBlocks(self):
        block_inner = Block('some/path', 'inner', ['inner content'])
        block_outer = Block('some/path', 'outer', [
            'outer content',
            block_inner,
            'outer content'])
        block_all = Block('some/path', 'all', [
            'all content',
            block_outer,
            'all content'])

        block_dict = get_block_dict(block_all)
        self.assertEqual({
            'all': self.join_lines(
                'all content',
                'outer content',
                'inner content',
                'outer content',
                'all content'),
            'outer': self.join_lines(
                'outer content',
                'inner content',
                'outer content'),
            'inner': 'inner content',
        }, block_dict)
Пример #3
0
    def testBlocks_emptyBlock(self):
        data = self.join_lines(
        '<block foo>',
        '</block foo>')
        with self.mock_open({'some/path': data}):
            block, variables = link('some/path')

        self.assertEqual({}, variables)
        block_foo = Block('some/path', 'foo', [])  # Block should be empty.
        block_all = Block('some/path', 'all', [block_foo])
        self.assertBlockEqual(block_all, block)
Пример #4
0
    def testBlocks_stripLeadingCharacters(self):
        data = self.join_lines(
        '# <block foo>',
        'def foo():',
        '    return None',
        '# </block foo>')
        with self.mock_open({'some/path': data}):
            block, variables = link('some/path')

        self.assertEqual({}, variables)
        block_foo = Block('some/path', 'foo', [self.join_lines(
            'def foo():',
            '    return None')])
        block_all = Block('some/path', 'all', [block_foo])
        self.assertBlockEqual(block_all, block)
Пример #5
0
    def testNoBlocksAndIncludes(self):
        data = self.join_lines(
        'Hello world',
        'Second line')
        with self.mock_open({'some/path': data}):
            block, variables = link('some/path')

        self.assertEqual({}, variables)
        block_all = Block('some/path', 'all', [data])
        self.assertBlockEqual(block_all, block)
Пример #6
0
    def testIncludes_leadingWhitespace(self):
        mock_open = self.mock_open({
            'docA.md': '    <include docB.md>',  # Indented four spaces
            'docB.md': 'content',
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({}, variables)
        block_all = Block('docA.md', 'all', ['    content'])
        self.assertBlockEqual(block_all, block)
Пример #7
0
    def testIncludes_relativeToSource(self):
        mock_open = self.mock_open({
            'path/to/docA.md': '<include docB.md>',
            'path/to/docB.md': 'content',
        })
        with mock_open:
            block, variables = link('path/to/docA.md')

        self.assertEqual({}, variables)
        block_all = Block('path/to/docA.md', 'all', ['content'])
        self.assertBlockEqual(block_all, block)
Пример #8
0
    def testBlocks_nested(self):
        data = self.join_lines(
        '<block outer>',
        'outer content',
        '<block inner>',
        'inner content',
        '</block inner>',
        'outer content',
        '</block outer>')
        with self.mock_open({'some/path': data}):
            block, variables = link('some/path')

        self.assertEqual({}, variables)
        block_inner = Block('some/path', 'inner', ['inner content'])
        block_outer = Block('some/path', 'outer', [
            'outer content',
            block_inner,
            'outer content'])
        block_all = Block('some/path', 'all', [block_outer])
        self.assertBlockEqual(block_all, block)
Пример #9
0
    def testVariables_handleWhitespace(self):
        mock_open = self.mock_open({
            'docA.md': '~     title   :     foo bar ',
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({
            'title': 'foo bar',
        }, variables)
        block_all = Block('docA.md', 'all', [])
        self.assertBlockEqual(block_all, block)
Пример #10
0
    def testVariables_noIncludes(self):
        mock_open = self.mock_open({
            'docA.md': self.join_lines(
                '~ title: foo',
                '~ author: bar')
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({
            'title': 'foo',
            'author': 'bar',
        }, variables)
        block_all = Block('docA.md', 'all', [])
        self.assertBlockEqual(block_all, block)
Пример #11
0
    def testIncludes_stripWhiteSpaceInTag(self):
        mock_open = self.mock_open({
            'docA.md': '< include docB.md : foo >',
            'docB.md': self.join_lines(
                'content not in block',
                '<block foo>',
                'content in block',
                '</block foo>')
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({}, variables)
        block_all = Block('docA.md', 'all', ['content in block'])
        self.assertBlockEqual(block_all, block)
Пример #12
0
    def testVariables_includeVariablesFromOtherFiles(self):
        mock_open = self.mock_open({
            'docA.md': self.join_lines(
                '~ title: foo ',
                '<include docB.md>'),
            'docB.md': '~ author: bar '
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({
            'title': 'foo',
            'author': 'bar',
        }, variables)
        block_all = Block('docA.md', 'all', [])
        self.assertBlockEqual(block_all, block)
Пример #13
0
    def testIncludes_omitBlockName(self):
        mock_open = self.mock_open({
            'path/docA.md': self.join_lines(
                'content',
                '<include path/to/docB.md>',
                'more content'),
            'path/to/docB.md': 'docB content',
        })
        with mock_open:
            block, variables = link('path/docA.md')

        self.assertEqual({}, variables)
        block_all = Block('path/docA.md', 'all', [self.join_lines(
            'content',
            'docB content',
            'more content')])
        self.assertBlockEqual(block_all, block)
Пример #14
0
    def testVariables_variablesInAnyLocation(self):
        mock_open = self.mock_open({
            'docA.md': self.join_lines(
                '~ title: foo ',
                'Some text',
                '~ author: bar ',
                'More text')
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({
            'title': 'foo',
            'author': 'bar',
        }, variables)
        block_all = Block('docA.md', 'all', [self.join_lines(
            'Some text',
            'More text')])
        self.assertBlockEqual(block_all, block)
Пример #15
0
def publish(config, source=None, template=None, destination=None, jinja_env=None, no_write=False):
    """Given a config, performs an end-to-end publishing pipeline and returns the result:

        linking -> compiling -> templating -> writing

    NOTE: at most one of source and template can be None. If both are None, the publisher
    effectively has nothing to do; an exception is raised.

    PARAMETERS:
    config      -- Config; a context that includes variables, compiler options, and templater
                   information.
    source      -- str; path to a source file, relative to the current working directory. If None,
                   the publisher effectively becomes a templating engine.
    template    -- str; path to a Jinja template file. Templar treats the path as relative to the
                   list of template directories in config. If the template cannot be found relative
                   to those directories, Templar finally tries the path relative to the current
                   directory.

                   If template is None, the publisher effectively becomes a linker and compiler.
    destination -- str; path for the destination file.
    jinja_env   -- jinja2.Environment; if None, a Jinja2 Environment is created with a
                   FileSystemLoader that is configured with config.template_dirs. Otherwise, the
                   given Jinja2 Environment is used to retrieve and render the template.
    no_write    -- bool; if True, the result is not written to a file or printed. If False and
                   destination is provided, the result is written to the provided destination file.

    RETURNS:
    str; the result of the publishing pipeline.
    """
    if not isinstance(config, Config):
        raise PublishError(
                "config must be a Config object, "
                "but instead was type '{}'".format(type(config).__name__))

    if source is None and template is None:
        raise PublishError('When publishing, source and template cannot both be omitted.')

    variables = config.variables
    if source:
        # Linking stage.
        all_block, extracted_variables = linker.link(source)
        variables.update(extracted_variables)

        # Compiling stage.
        block_variables = {}
        for rule in config.rules:
            if rule.applies(source, destination):
                if isinstance(rule, VariableRule):
                    variables.update(rule.apply_with_destination(str(all_block), destination))
                elif rule.is_global():
                    result_content = rule.apply_with_destination(str(all_block), destination=destination)
                    all_block = Block(all_block.source_path, all_block.name, [result_content])
                else:
                    all_block.apply_rule(rule, destination=destination)
        block_variables.update(linker.get_block_dict(all_block))
        variables['blocks'] = block_variables   # Blocks are namespaced with 'blocks'.

    # Templating stage.
    if template:
        if not jinja_env:
            jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(config.template_dirs))
        jinja_template = jinja_env.get_template(template)
        result = jinja_template.render(variables)

        # Handle recursive evaluation of Jinja expressions.
        iterations = 0
        while config.recursively_evaluate_jinja_expressions \
                and iterations < _MAX_JINJA_RECURSIVE_DEPTH + 1 \
                and  _jinja_expression_re.search(result):
            if iterations == _MAX_JINJA_RECURSIVE_DEPTH:
                raise PublishError('\n'.join([
                    'Recursive Jinja expression evaluation exceeded the allowed '
                        'number of iterations. Last state of template:',
                    result]))
            jinja_env = jinja2.Environment(loader=jinja2.DictLoader({'intermediate': result}))
            jinja_template = jinja_env.get_template('intermediate')
            result = jinja_template.render(variables)
            iterations += 1
    else:
        # template is None implies source is not None, so variables['blocks'] must exist.
        result = variables['blocks']['all']

    # Writing stage.
    if not no_write and destination:
        destination_dir = os.path.dirname(destination)
        if destination_dir != '' and not os.path.isdir(destination_dir):
            try:
                os.makedirs(destination_dir)
            except FileExistsError:
                pass
        with open(destination, 'w') as f:
            f.write(result)
    return result
Пример #16
0
 def testNoNestedBlocks(self):
     block_all = Block('some/path', 'all', ['all content'])
     block_dict = get_block_dict(block_all)
     self.assertEqual({
         'all': 'all content',
     }, block_dict)