diff --git a/.moban.cd/changelog.yml b/.moban.cd/changelog.yml index 861496bf..7d81a89d 100644 --- a/.moban.cd/changelog.yml +++ b/.moban.cd/changelog.yml @@ -1,6 +1,12 @@ name: moban organisation: moremoban releases: + - changes: + - action: Updated + details: + - "`#390`: single render action will print to stdout by defafult" + date: 06.08.2020 + version: 0.7.9 - changes: - action: Added details: diff --git a/.moban.cd/moban.yml b/.moban.cd/moban.yml index 1857d5c5..fc36b34f 100644 --- a/.moban.cd/moban.yml +++ b/.moban.cd/moban.yml @@ -4,9 +4,9 @@ organisation: moremoban author: C. W. contact: wangc_2011@hotmail.com license: MIT -version: 0.7.8 -current_version: 0.7.8 -release: 0.7.8 +version: 0.7.9 +current_version: 0.7.9 +release: 0.7.9 branch: master master: index command_line_interface: "moban" diff --git a/.moban.d/moban_readme.jj2 b/.moban.d/moban_readme.jj2 index 30d880eb..45d22d7c 100644 --- a/.moban.d/moban_readme.jj2 +++ b/.moban.d/moban_readme.jj2 @@ -44,16 +44,11 @@ versions lower than 0.7.0 if you are still using python 2. Quick start ================================================================================ -.. image:: https://github.com/moremoban/moban/raw/dev/docs/images/moban-in-intro.gif - {% raw %} .. code-block:: bash $ export HELLO="world" $ moban "{{HELLO}}" - Templating {{HELLO}}... to moban.output - Templated 1 file. - $ cat moban.output world Or @@ -76,7 +71,7 @@ A bit formal example: .. code-block:: bash $ moban -c data.yml -t my.template - $ cat moban.output + world Given data.yml as: @@ -94,11 +89,6 @@ and my.template as: {% endraw %} -moban.output will contain: - -.. code-block:: bash - - world Please note that data.yml will take precedence over environment variables. @@ -182,6 +172,13 @@ Assume that the custom example was saved in `custom-jj2-plugin` Moban will then load your custom jinja2 functions +Slim template syntax for jinja2 +--------------------------------- + +with `moban-slim `_ installed, + +{% include "slim_example.rst.jj2" %} + Handlebars.js template ---------------------------- diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a828b279..15c92b78 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,14 @@ Change log ================================================================================ +0.7.9 - 06.08.2020 +-------------------------------------------------------------------------------- + +**Updated** + +#. `#390 `_: single render action + will print to stdout by defafult + 0.7.8 - 09.06.2020 -------------------------------------------------------------------------------- diff --git a/Makefile b/Makefile index 5d9ead41..c578aaae 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ lint: yamllint -d "{extends: default, ignore: .moban.cd/changelog.yml}" . format: - isort -y $(find moban -name "*.py"|xargs echo) $(find tests -name "*.py"|xargs echo) + isort $(find moban -name "*.py"|xargs echo) $(find tests -name "*.py"|xargs echo) git diff black -l 79 moban git diff diff --git a/README.rst b/README.rst index 7b5f9615..5e5d2093 100644 --- a/README.rst +++ b/README.rst @@ -44,16 +44,11 @@ versions lower than 0.7.0 if you are still using python 2. Quick start ================================================================================ -.. image:: https://github.com/moremoban/moban/raw/dev/docs/images/moban-in-intro.gif - .. code-block:: bash $ export HELLO="world" $ moban "{{HELLO}}" - Templating {{HELLO}}... to moban.output - Templated 1 file. - $ cat moban.output world Or @@ -75,7 +70,7 @@ A bit formal example: .. code-block:: bash $ moban -c data.yml -t my.template - $ cat moban.output + world Given data.yml as: @@ -92,11 +87,6 @@ and my.template as: {{hello}} -moban.output will contain: - -.. code-block:: bash - - world Please note that data.yml will take precedence over environment variables. @@ -179,6 +169,31 @@ Assume that the custom example was saved in `custom-jj2-plugin` Moban will then load your custom jinja2 functions +Slim template syntax for jinja2 +--------------------------------- + +with `moban-slim `_ installed, + +Given a data.json file with the following content + +.. code-block:: + + { + "person": { + "firstname": "Smith", + "lastname": "Jones", + }, + } + +.. code-block:: bash + + + $ moban --template-type slim -c data.json "{{person.firstname}} {{person.lastname}}" + Slimming

{{first... to moban.output + Slimmed 1 file. + $ cat moban.output + Smith Jones + Handlebars.js template ---------------------------- diff --git a/docs/conf.py b/docs/conf.py index d39b148d..cccb501c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,9 +25,9 @@ copyright = '2017-2020 Onni Software Ltd.' author = 'C. W.' # The short X.Y version -version = '0.7.8' +version = '0.7.9' # The full version, including alpha/beta/rc tags -release = '0.7.8' +release = '0.7.9' # -- General configuration --------------------------------------------------- diff --git a/moban/_version.py b/moban/_version.py index 6e277821..7c098b93 100644 --- a/moban/_version.py +++ b/moban/_version.py @@ -1,2 +1,2 @@ -__version__ = "0.7.8" +__version__ = "0.7.9" __author__ = "C. W." diff --git a/moban/constants.py b/moban/constants.py index c4a96f90..7522785b 100644 --- a/moban/constants.py +++ b/moban/constants.py @@ -56,7 +56,7 @@ file_system.path_join(".", DEFAULT_TEMPLATE_DIRNAME), ], # moban.output, default output file name - LABEL_OUTPUT: "%s.output" % PROGRAM_NAME, + LABEL_OUTPUT: "-", # data.yml, default data input file LABEL_CONFIG: "data%s" % DEFAULT_YAML_SUFFIX, LABEL_TEMPLATE_TYPE: DEFAULT_TEMPLATE_TYPE, diff --git a/moban/externals/buffered_writer.py b/moban/externals/buffered_writer.py index 42b56b3f..db954a01 100644 --- a/moban/externals/buffered_writer.py +++ b/moban/externals/buffered_writer.py @@ -11,7 +11,9 @@ def __init__(self): self.fs_list = {} def write_file_out(self, filename, content): - if file_system.is_zip_alike_url(filename): + if filename == "-": + print(content.decode()) + elif file_system.is_zip_alike_url(filename): self.write_file_out_to_zip(filename, content) else: write_file_out(filename, content) diff --git a/moban/externals/reporter.py b/moban/externals/reporter.py index aef91d89..dcab3e5d 100644 --- a/moban/externals/reporter.py +++ b/moban/externals/reporter.py @@ -1,3 +1,5 @@ +import sys + import crayons import moban.constants as constants @@ -13,6 +15,8 @@ MESSAGE_FILE_EXTENSION_NOT_NEEDED = "File extension is not required for ad-hoc\ type" +GLOBAL = {"PRINT": True} + def report_templating( action_in_present_continuous_tense, source_file, destination_file @@ -49,11 +53,13 @@ def report_partial_run(action_in_past_tense, file_count, total): def report_error_message(message): - do_print(crayons.white("Error: ", bold=True) + crayons.red(message)) + error_print(crayons.white("Error: ", bold=True) + crayons.red(message)) def report_warning_message(message): - do_print(crayons.white("Warning: ", bold=True) + crayons.yellow(message)) + error_print( + crayons.white("Warning: ", bold=True) + crayons.yellow(message) + ) def report_info_message(message): @@ -98,4 +104,9 @@ def report_file_extension_not_needed(): def do_print(message): - print(message) + if GLOBAL["PRINT"]: + print(message) + + +def error_print(message): + sys.stderr.write(message + "\n") diff --git a/moban/main.py b/moban/main.py index 701de86f..be2ef2d4 100644 --- a/moban/main.py +++ b/moban/main.py @@ -238,6 +238,7 @@ def handle_command_line(options): """ act upon command options """ + reporter.GLOBAL["PRINT"] = False options = data_loader.merge(options, constants.DEFAULT_OPTIONS) engine = ENGINES.get_engine( options[constants.LABEL_TEMPLATE_TYPE], diff --git a/mobanfile b/mobanfile index b1d3265b..55f67431 100644 --- a/mobanfile +++ b/mobanfile @@ -5,6 +5,7 @@ configuration: - "git://github.com/moremoban/moban-anyconfig.git?branch=master!/.moban.d/" - "git://github.com/moremoban/moban-handlebars.git?branch=dev!/.moban.d/" - "git://github.com/moremoban/moban-velocity.git?branch=dev!/.moban.d/" + - "git://github.com/moremoban/moban-slim.git?branch=dev!/.moban.d/" - "git://github.com/moremoban/httpfs.git?branch=master!/.moban.d/" - "git://github.com/moremoban/gitfs2.git?branch=dev!/.moban.d/" - "git://github.com/moremoban/pypifs.git?branch=master!/.moban.d/" diff --git a/setup.py b/setup.py index 1dfbf5ce..cc041626 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ NAME = "moban" AUTHOR = "C. W." -VERSION = "0.7.8" +VERSION = "0.7.9" EMAIL = "wangc_2011@hotmail.com" LICENSE = "MIT" ENTRY_POINTS = { @@ -53,7 +53,7 @@ "General purpose static text generator" ) URL = "https://github.com/moremoban/moban" -DOWNLOAD_URL = "%s/archive/0.7.8.tar.gz" % URL +DOWNLOAD_URL = "%s/archive/0.7.9.tar.gz" % URL FILES = ["README.rst", "CONTRIBUTORS.rst", "CHANGELOG.rst"] KEYWORDS = [ "python", @@ -96,8 +96,8 @@ } # You do not need to read beyond this line PUBLISH_COMMAND = "{0} setup.py sdist bdist_wheel upload -r pypi".format(sys.executable) -GS_COMMAND = ("gs moban v0.7.8 " + - "Find 0.7.8 in changelog for more details") +GS_COMMAND = ("gs moban v0.7.9 " + + "Find 0.7.9 in changelog for more details") NO_GS_MESSAGE = ("Automatic github release is disabled. " + "Please install gease to enable it.") UPLOAD_FAILED_MSG = ( diff --git a/tests/integration_tests/test_command_line_options.py b/tests/integration_tests/test_command_line_options.py index 7244202d..4ad67d70 100644 --- a/tests/integration_tests/test_command_line_options.py +++ b/tests/integration_tests/test_command_line_options.py @@ -42,9 +42,7 @@ def test_custom_options(self, fake_template_doer, fake_abspath): from moban.main import main main() - fake_template_doer.assert_called_with( - "a.jj2", "config.yaml", "moban.output" - ) + fake_template_doer.assert_called_with("a.jj2", "config.yaml", "-") @patch("moban.core.moban_factory.MobanEngine.render_to_file") def test_minimal_options(self, fake_template_doer): @@ -53,9 +51,7 @@ def test_minimal_options(self, fake_template_doer): from moban.main import main main() - fake_template_doer.assert_called_with( - "a.jj2", "config.yaml", "moban.output" - ) + fake_template_doer.assert_called_with("a.jj2", "config.yaml", "-") @raises(SystemExit) def test_missing_template(self): @@ -89,9 +85,7 @@ def test_default_options(self, fake_template_doer): from moban.main import main main() - fake_template_doer.assert_called_with( - "a.jj2", "data.yml", "moban.output" - ) + fake_template_doer.assert_called_with("a.jj2", "data.yml", "-") @patch("moban.core.moban_factory.MobanEngine.render_string_to_file") def test_string_template(self, fake_template_doer): @@ -102,7 +96,7 @@ def test_string_template(self, fake_template_doer): main() fake_template_doer.assert_called_with( - string_template, "data.yml", "moban.output" + string_template, "data.yml", "-" ) @raises(SystemExit) @@ -307,7 +301,7 @@ def test_template_option_override_moban_file(self, fake_template_doer): main() fake_template_doer.assert_called_with( - "setup.py.jj2", "data.yml", "moban.output" + "setup.py.jj2", "data.yml", "-" ) @patch("moban.core.moban_factory.MobanEngine.render_to_file") @@ -317,9 +311,7 @@ def test_template_option_not_in_moban_file(self, fake_template_doer): from moban.main import main main() - fake_template_doer.assert_called_with( - "foo.jj2", "data.yml", "moban.output" - ) + fake_template_doer.assert_called_with("foo.jj2", "data.yml", "-") def tearDown(self): self.patcher1.stop() @@ -431,9 +423,7 @@ def test_mako_option(self, fake_template_doer): from moban.main import main main() - fake_template_doer.assert_called_with( - "a.mako", "data.yml", "moban.output" - ) + fake_template_doer.assert_called_with("a.mako", "data.yml", "-") def tearDown(self): os.unlink(self.config_file) @@ -532,6 +522,8 @@ def test_add_extension(): "{{ python_version }}", "-e", "jinja2=jinja2_python_version.PythonVersionExtension", + "-o", + "moban.output", ], [ "moban", @@ -539,6 +531,8 @@ def test_add_extension(): "{{ python_version }}", "-e", "jj2=jinja2_python_version.PythonVersionExtension", + "-o", + "moban.output", ], ] for test_args in test_commands: @@ -558,7 +552,7 @@ def test_add_extension(): def test_stdin_input(): if sys.platform == "win32": raise SkipTest("windows test fails with this pipe test 2") - test_args = ["moban", "-d", "hello=world"] + test_args = ["moban", "-d", "hello=world", "-o", "moban.output"] with patch.object(sys, "stdin", StringIO("{{hello}}")): with patch.object(sys, "argv", test_args): from moban.main import main @@ -568,3 +562,13 @@ def test_stdin_input(): content = f.read() eq_(content, "world") os.unlink("moban.output") + + +def test_stdout(): + test_args = ["moban", "-d", "hello=world", "-t", "{{hello}}"] + with patch.object(sys, "argv", test_args): + with patch("sys.stdout", new_callable=StringIO) as fake_stdout: + from moban.main import main + + main() + eq_(fake_stdout.getvalue(), "world\n") diff --git a/tests/test_docs.py b/tests/test_docs.py index d40587cf..92053e20 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -14,7 +14,15 @@ def test_level_1(self): def test_level_1_custom_define(self): expected = "maailman" folder = "level-1-jinja2-cli" - args = ["moban", "-d", "hello=maailman", "-t", "a.template"] + args = [ + "moban", + "-d", + "hello=maailman", + "-t", + "a.template", + "-o", + "moban.output", + ] self.run_moban(args, folder, [("moban.output", expected)]) def test_level_2(self): @@ -187,7 +195,15 @@ def test_level_13_json(self): """ expected = custom_dedent(expected) folder = "level-13-any-data-override-any-data" - commands = ["moban", "-c", "child.json", "-t", "a.template"] + commands = [ + "moban", + "-c", + "child.json", + "-t", + "a.template", + "-o", + "moban.output", + ] self.run_moban(commands, folder, [("moban.output", expected)]) def test_level_13_yaml(self): @@ -202,7 +218,15 @@ def test_level_13_yaml(self): """ expected = custom_dedent(expected) folder = "level-13-any-data-override-any-data" - commands = ["moban", "-c", "child.yaml", "-t", "a.template"] + commands = [ + "moban", + "-c", + "child.yaml", + "-t", + "a.template", + "-o", + "moban.output", + ] self.run_moban(commands, folder, [("moban.output", expected)]) def test_level_14_custom(self): @@ -354,5 +378,13 @@ def test_misc_1(self): self.run_moban(["moban"], folder, [("simple.file", expected)]) def _moban(self, folder, expected): - args = ["moban", "-c", "data.yml", "-t", "a.template"] + args = [ + "moban", + "-c", + "data.yml", + "-t", + "a.template", + "-o", + "moban.output", + ] self.run_moban(args, folder, [("moban.output", expected)]) diff --git a/tests/test_regression.py b/tests/test_regression.py index b2c3771d..e1b337fd 100644 --- a/tests/test_regression.py +++ b/tests/test_regression.py @@ -47,6 +47,8 @@ def test_level_7(self): "filter.jj2", "-pd", "custom-jj2-plugin", + "-o", + "moban.output", ], folder, [("moban.output", expected)], @@ -65,6 +67,8 @@ def test_level_7_b(self): "custom-plugin", "-t", "duplicated_content.txt", + "-o", + "moban.output", ], folder, [("moban.output", expected)], diff --git a/tests/test_reporter.py b/tests/test_reporter.py index ad578a4f..386c0512 100644 --- a/tests/test_reporter.py +++ b/tests/test_reporter.py @@ -12,85 +12,80 @@ from io import StringIO -def test_partial_run(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_partial_run("Actioned", 1, 20) - patcher.stop() - eq_(fake_stdout.getvalue(), "Actioned 1 out of 20 files.\n") - - -def test_full_run(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_full_run("Worked on", 20) - patcher.stop() - eq_(fake_stdout.getvalue(), "Worked on 20 files.\n") - - -def test_error_message(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_error_message("something wrong") - patcher.stop() - eq_(fake_stdout.getvalue(), "Error: something wrong\n") - - -def test_info_message(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_info_message("for your information") - patcher.stop() - eq_(fake_stdout.getvalue(), "Info: for your information\n") - - -def test_warning_message(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_warning_message("Maybe you wanna know") - patcher.stop() - eq_(fake_stdout.getvalue(), "Warning: Maybe you wanna know\n") - - -def test_report_templating(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_templating("Transforming", "a", "b") - patcher.stop() - eq_(fake_stdout.getvalue(), "Transforming a to b\n") - - -def test_no_action(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_no_action() - patcher.stop() - eq_(fake_stdout.getvalue(), "No actions performed\n") - - -def test_format_single(): - message = "1 files" - ret = reporter._format_single(message, 1) - eq_(ret, "1 file") - - -def test_report_template_not_in_moban_file(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_template_not_in_moban_file("test.jj2") - patcher.stop() - eq_( - fake_stdout.getvalue(), - "Warning: test.jj2 is not defined in your moban file!\n", - ) - - -def test_report_file_extension_not_needed(): - patcher = patch("sys.stdout", new_callable=StringIO) - fake_stdout = patcher.start() - reporter.report_file_extension_not_needed() - patcher.stop() - eq_( - fake_stdout.getvalue(), - "Info: File extension is not required for ad-hoc type\n", - ) +class TestReporter: + def setUp(self): + reporter.GLOBAL["PRINT"] = True + + def test_partial_run(self): + patcher = patch("sys.stdout", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_partial_run("Actioned", 1, 20) + patcher.stop() + eq_(fake_stdout.getvalue(), "Actioned 1 out of 20 files.\n") + + def test_full_run(self): + patcher = patch("sys.stdout", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_full_run("Worked on", 20) + patcher.stop() + eq_(fake_stdout.getvalue(), "Worked on 20 files.\n") + + def test_error_message(self): + patcher = patch("sys.stderr", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_error_message("something wrong") + patcher.stop() + eq_(fake_stdout.getvalue(), "Error: something wrong\n") + + def test_info_message(self): + patcher = patch("sys.stdout", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_info_message("for your information") + patcher.stop() + eq_(fake_stdout.getvalue(), "Info: for your information\n") + + def test_warning_message(self): + patcher = patch("sys.stderr", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_warning_message("Maybe you wanna know") + patcher.stop() + eq_(fake_stdout.getvalue(), "Warning: Maybe you wanna know\n") + + def test_report_templating(self): + patcher = patch("sys.stdout", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_templating("Transforming", "a", "b") + patcher.stop() + eq_(fake_stdout.getvalue(), "Transforming a to b\n") + + def test_no_action(self): + patcher = patch("sys.stdout", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_no_action() + patcher.stop() + eq_(fake_stdout.getvalue(), "No actions performed\n") + + def test_format_single(self): + message = "1 files" + ret = reporter._format_single(message, 1) + eq_(ret, "1 file") + + def test_report_template_not_in_moban_file(self): + patcher = patch("sys.stderr", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_template_not_in_moban_file("test.jj2") + patcher.stop() + eq_( + fake_stdout.getvalue(), + "Warning: test.jj2 is not defined in your moban file!\n", + ) + + def test_report_file_extension_not_needed(self): + patcher = patch("sys.stdout", new_callable=StringIO) + fake_stdout = patcher.start() + reporter.report_file_extension_not_needed() + patcher.stop() + eq_( + fake_stdout.getvalue(), + "Info: File extension is not required for ad-hoc type\n", + )