diff --git a/docs/source/standardlib/jinja.rst b/docs/source/standardlib/jinja.rst index 94a3cc55..79fcfbde 100644 --- a/docs/source/standardlib/jinja.rst +++ b/docs/source/standardlib/jinja.rst @@ -28,7 +28,9 @@ Renders a single file from JINJA2. Renders all files recursively in given directory to other directory. Can remove source files after rendering them to the output files. -*Pattern is a regexp pattern that matches whole path, not only file name* +*Note: Pattern is a regexp pattern that matches whole path, not only file name* + +*Note: Exclude pattern is matching on SOURCE files, not on target files* **Example usage:** diff --git a/src/rkd/standardlib/jinja.py b/src/rkd/standardlib/jinja.py index b13221c1..c6a716b4 100644 --- a/src/rkd/standardlib/jinja.py +++ b/src/rkd/standardlib/jinja.py @@ -74,6 +74,7 @@ def execute(self, context: ExecutionContext) -> bool: pattern = re.compile(context.get_arg('--pattern')) exclude_pattern = re.compile(context.get_arg('--exclude-pattern')) if context.get_arg('--exclude-pattern') else None copy_not_matched = context.get_arg('--copy-not-matching-files') + template_filenames = context.get_arg('--template-filenames') self.io().info_msg('Pattern is `%s`' % context.get_arg('--pattern')) @@ -85,6 +86,9 @@ def execute(self, context: ExecutionContext) -> bool: if target_full_path.endswith('.j2'): target_full_path = target_full_path[:-3] + if template_filenames: + target_full_path = self.replace_vars_in_filename(context.env, target_full_path) + if exclude_pattern and self._is_file_matching_filter(exclude_pattern, source_full_path): self.io().info_msg('Skipping file "%s" - (filtered out by --exclude-pattern)' % source_full_path) continue @@ -110,6 +114,13 @@ def execute(self, context: ExecutionContext) -> bool: return True + @staticmethod + def replace_vars_in_filename(env_vars: dict, filename: str) -> str: + for name, value in env_vars.items(): + filename = filename.replace('--%s--' % name, value) + + return filename + def _copy_file(self, source_full_path: str, target_full_path: str): self.sh('mkdir -p "%s"' % os.path.dirname(target_full_path)) self.sh('cp -p "%s" "%s"' % (source_full_path, target_full_path)) @@ -140,6 +151,9 @@ def configure_argparse(self, parser: ArgumentParser): parser.add_argument('--exclude-pattern', '-xp', help='Optional regexp for a pattern exclude, to exclude files') parser.add_argument('--copy-not-matching-files', '-c', help='Copy all files that are not matching the pattern' + ' instead of skipping them', action='store_true') + parser.add_argument('--template-filenames', '-tf', + help='Replace variables in filename eg. --VAR--, ' + + 'where VAR is a name of environment variable', action='store_true') def imports(): diff --git a/test/internal-samples/jinja2-filename-templating/lyrics---song_name--.txt.j2 b/test/internal-samples/jinja2-filename-templating/lyrics---song_name--.txt.j2 new file mode 100644 index 00000000..7d3ef458 --- /dev/null +++ b/test/internal-samples/jinja2-filename-templating/lyrics---song_name--.txt.j2 @@ -0,0 +1,17 @@ +Hijo del pueblo, te oprimen cadenas, +y esa injusticia no puede seguir; +si tu existencia es un mundo de penas +antes que esclavo prefiere morir. +En la batalla, la hiena fascista. +por nuestro esfuerzo sucumbirá; +y el pueblo entero, con los anarquistas, +hará que triunfe la libertad. + +Trabajador, no más sufrir, +el opresor ha de sucumbir. +Levántate, pueblo leal, +al grito de revolución social. +Fuerte unidad de fe y de acción +producirá la revolución. +Nuestro pendón uno ha de ser: +sólo en la unión está el vencer. diff --git a/test/internal-samples/jinja2-filename-templating/lyrics2---song2_name--.txt b/test/internal-samples/jinja2-filename-templating/lyrics2---song2_name--.txt new file mode 100644 index 00000000..d760d221 --- /dev/null +++ b/test/internal-samples/jinja2-filename-templating/lyrics2---song2_name--.txt @@ -0,0 +1,16 @@ +Negras tormentas agitan los aires, +nubes oscuras nos impiden ver. +Aunque nos espere el dolor y la muerte, +contra el enemigo nos llama el deber. + +El bien más preciado +es la libertad, +hay que defenderla con fe y con valor. +Alta la bandera revolucionaria, +que llevará al pueblo a la emancipación + +En pie el pueblo obrero a la batalla, +hay que derrocar a la reacción. +¡A las Barricadas! +¡A las Barricadas! +por el triunfo de la Confederación. diff --git a/test/test_standardlib_jinja_render_directory.py b/test/test_standardlib_jinja_render_directory.py index 53c6c2f0..4d4aa226 100644 --- a/test/test_standardlib_jinja_render_directory.py +++ b/test/test_standardlib_jinja_render_directory.py @@ -1,16 +1,19 @@ #!/usr/bin/env python3 +import os import unittest from rkd.standardlib.jinja import RenderDirectoryTask from rkd.test import mock_task, mock_execution_context +TESTS_DIR = os.path.dirname(os.path.realpath(__file__)) + class TestRenderDirectoryTask(unittest.TestCase): """Tests for a task that should render JINJA2 files from DIRECTORY "A" to DIRECTORY "B" """ @staticmethod - def _execute_mocked_task(params: dict) -> tuple: + def _execute_mocked_task(params: dict, env: dict = {}) -> tuple: task: RenderDirectoryTask = RenderDirectoryTask() mock_task(task) @@ -30,7 +33,7 @@ def mock__sh(*args, **kwargs): task._delete_file = lambda file: deletions.append(file) # run task - task.execute(mock_execution_context(task, params)) + task.execute(mock_execution_context(task, params, env)) return calls, deletions @@ -46,7 +49,8 @@ def test_iterates_over_subdirectories_including_depth_and_pattern(self): 'delete_source_files': False, 'pattern': '(.*)(src|test)/(.*).py$', '--exclude-pattern': '', - '--copy-not-matching-files': False + '--copy-not-matching-files': False, + '--template-filenames': False }) # example files (please correct if changed in filesystem) @@ -71,7 +75,8 @@ def test_files_are_copied_when_not_matching_pattern_but_switch_was_used(self): 'delete_source_files': False, 'pattern': '(.*).j2', '--exclude-pattern': '(.*).pyc', - '--copy-not-matching-files': True + '--copy-not-matching-files': True, + '--template-filenames': False }) flatten_list_as_str = ' '.join(renderings) @@ -94,7 +99,8 @@ def test_without_pattern(self): 'delete_source_files': False, 'pattern': '', '--exclude-pattern': '', - '--copy-not-matching-files': False + '--copy-not-matching-files': False, + '--template-filenames': False }) # example files (please correct if changed in filesystem) @@ -109,7 +115,8 @@ def test_no_files_deleted_when_option_disabled(self): 'delete_source_files': False, 'pattern': '', '--exclude-pattern': '', - '--copy-not-matching-files': False + '--copy-not-matching-files': False, + '--template-filenames': False }) self.assertEqual([], deletions) @@ -121,7 +128,61 @@ def test_files_are_called_to_be_deleted(self): 'delete_source_files': True, 'pattern': '(.*)test_standardlib_jinja_render_directory.py$', '--exclude-pattern': '', - '--copy-not-matching-files': False + '--copy-not-matching-files': False, + '--template-filenames': False }) self.assertEqual(['../test/test_standardlib_jinja_render_directory.py'], deletions) + + def test_replace_vars_in_filename(self): + name = RenderDirectoryTask().replace_vars_in_filename({'Word': 'triumph'}, 'that-agony-is-your---Word--.txt') + + self.assertEqual('that-agony-is-your-triumph.txt', name) + + def test_replace_vars_in_filename_multiple_occurrences(self): + name = RenderDirectoryTask()\ + .replace_vars_in_filename({'word': 'pueblo', 'word2': 'hijos'}, '--word2--_del_--word--(--word--_version)') + + self.assertEqual('hijos_del_pueblo(pueblo_version)', name) + + def test_filename_templating_when_switch_is_on(self): + """Assert that variables in filenames are also replaced, not only in the content + + Condition: When the "--template-filenames" is used + """ + + renderings, deletions = self._execute_mocked_task({ + 'source': TESTS_DIR + '/internal-samples/jinja2-filename-templating', + 'target': '/tmp', + 'delete_source_files': True, + 'pattern': '(.*).j2', + '--exclude-pattern': '', + '--copy-not-matching-files': True, + '--template-filenames': True + }, env={ + 'song_name': 'hijos-del-pueblo', + 'song2_name': 'a-las-barricadas' + }) + + self.assertIn('-> "/tmp//lyrics-hijos-del-pueblo.txt"', ' '.join(renderings), + msg='Expected that the matching .j2 file would have changed name') + self.assertIn('"/tmp//lyrics2-a-las-barricadas.txt"', ' '.join(renderings), + msg='Expected that the non-j2 file would have also changed name') + + def test_filename_templating_is_not_replacing_vars_when_switch_is_not_used(self): + """Checks if the variable replacing in filenames can be turned off by not using + "--template-filenames" switch""" + + renderings, deletions = self._execute_mocked_task({ + 'source': TESTS_DIR + '/internal-samples/jinja2-filename-templating', + 'target': '/tmp', + 'delete_source_files': True, + 'pattern': '(.*).j2', + '--exclude-pattern': '', + '--copy-not-matching-files': False, + '--template-filenames': False + }, env={ + 'song_name': 'hijos-del-pueblo' + }) + + self.assertIn('-> "/tmp//lyrics---song_name--.txt"', ' '.join(renderings))