From d9e98b8598776f9c429e9a4a76ca98efda31edde Mon Sep 17 00:00:00 2001 From: CLiu13 Date: Tue, 1 Jan 2019 00:52:18 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20low-setup=20usage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the ability to use moban in an ad-hoc manner without a config file. This also adds environment variables as a fallback data source if the default/specified data files do not exist. Closes https://github.com/moremoban/moban/issues/133 --- .moban.cd/changelog.yml | 6 +++ .moban.cd/moban.yml | 6 +-- CHANGELOG.rst | 9 ++++ README.rst | 6 ++- docs/conf.py | 4 +- docs/level-1-jinja2-cli/README.rst | 3 +- moban/_version.py | 2 +- moban/mobanfile.py | 28 +++++++++--- moban/plugins.py | 30 +++++++++++-- moban/reporter.py | 11 +++++ setup.py | 8 ++-- .../environ_vars_as_data/test.template | 1 + .../test_command_line_options.py | 45 ++++++++++++++++--- tests/test_engine.py | 14 ++++++ 14 files changed, 146 insertions(+), 27 deletions(-) create mode 100644 tests/fixtures/environ_vars_as_data/test.template diff --git a/.moban.cd/changelog.yml b/.moban.cd/changelog.yml index 708cc62a..569c8631 100644 --- a/.moban.cd/changelog.yml +++ b/.moban.cd/changelog.yml @@ -1,6 +1,12 @@ name: moban organisation: moremoban releases: +- changes: + - action: Updated + details: + - "`#146`: added a low-setup usage mode via environment variables to moban" + date: 3-1-2019 + version: 0.3.7 - changes: - action: Updated details: diff --git a/.moban.cd/moban.yml b/.moban.cd/moban.yml index c7ae2e2b..96a4c863 100644 --- a/.moban.cd/moban.yml +++ b/.moban.cd/moban.yml @@ -3,9 +3,9 @@ organisation: moremoban author: C. W. contact: wangc_2011@hotmail.com license: MIT -version: 0.3.6 -current_version: 0.3.6 -release: 0.3.6 +version: 0.3.7 +current_version: 0.3.7 +release: 0.3.7 branch: master command_line_interface: "moban" entry_point: "moban.main:main" diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 80320369..4e25ef4a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,15 @@ Change log ================================================================================ +0.3.7 - 3-1-2019 +-------------------------------------------------------------------------------- + +Updated +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +#. `#146 `_: added a low-setup + usage mode via environment variables to moban + 0.3.6 - 30-12-2018 -------------------------------------------------------------------------------- diff --git a/README.rst b/README.rst index 44b0d0ed..e699b4f4 100644 --- a/README.rst +++ b/README.rst @@ -91,11 +91,13 @@ Usage -cd CONFIGURATION_DIR, --configuration_dir CONFIGURATION_DIR the directory for configuration file lookup -c CONFIGURATION, --configuration CONFIGURATION - the dictionary file + the dictionary file. if not present, moban + will try to use environment vars as data -td [TEMPLATE_DIR [TEMPLATE_DIR ...]], --template_dir [TEMPLATE_DIR [TEMPLATE_DIR ...]] the directories for template file lookup -t TEMPLATE, --template TEMPLATE - the template file + the template file. this overrides any targets + defined in a custom moban file -o OUTPUT, --output OUTPUT the output file --template_type TEMPLATE_TYPE diff --git a/docs/conf.py b/docs/conf.py index 731a5619..b8bd3a7b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,9 +28,9 @@ author = u'C. W.' # The short X.Y version -version = u'0.3.6' +version = u'0.3.7' # The full version, including alpha/beta/rc tags -release = u'0.3.6' +release = u'0.3.7' # -- General configuration --------------------------------------------------- diff --git a/docs/level-1-jinja2-cli/README.rst b/docs/level-1-jinja2-cli/README.rst index 78d29cde..59bb9b1e 100644 --- a/docs/level-1-jinja2-cli/README.rst +++ b/docs/level-1-jinja2-cli/README.rst @@ -2,7 +2,8 @@ Level 1 Jinja2 on command line ================================================================================ `moban` reads data in yaml format, renders a template file in jinja2 format and -outputs it to `moban.output`. By default, it looks for `data.yml` as its data file +outputs it to `moban.output`. By default, it looks for `data.yml` as its data file, +but it will fallback to environment variables if a data file cannot be found Evaluation -------------------------------------------------------------------------------- diff --git a/moban/_version.py b/moban/_version.py index 64fcab11..2c02a9fe 100644 --- a/moban/_version.py +++ b/moban/_version.py @@ -1,2 +1,2 @@ -__version__ = "0.3.6" +__version__ = "0.3.7" __author__ = "C. W." diff --git a/moban/mobanfile.py b/moban/mobanfile.py index ffff7277..1223db29 100644 --- a/moban/mobanfile.py +++ b/moban/mobanfile.py @@ -37,7 +37,23 @@ def find_default_moban_file(): def handle_moban_file_v1(moban_file_configurations, command_line_options): merged_options = None - target = extract_target(command_line_options) + + targets = moban_file_configurations.get(constants.LABEL_TARGETS) + try: + target = extract_target(command_line_options) + except Exception as exception: + if targets: + template = command_line_options.get(constants.LABEL_TEMPLATE) + for t in targets: + found_template = template in t.values() + if found_template: + target = [dict(t)] + if not found_template: + # Warn user if template not defined under targets in moban file + reporter.report_template_not_in_moban_file(template) + else: + raise exception + if constants.LABEL_CONFIG in moban_file_configurations: merged_options = merge( command_line_options, @@ -52,12 +68,14 @@ def handle_moban_file_v1(moban_file_configurations, command_line_options): if requires: handle_requires(requires) - targets = moban_file_configurations.get(constants.LABEL_TARGETS) if targets: + # If template specified via CLI flag `-t: + # 1. Only update the specified template + # 2. Do not copy if target: - # if command line option exists, append its template to targets - # issue 30 - targets += target + targets = target + if constants.LABEL_COPY in moban_file_configurations: + del moban_file_configurations[constants.LABEL_COPY] number_of_templated_files = handle_targets(merged_options, targets) else: number_of_templated_files = 0 diff --git a/moban/plugins.py b/moban/plugins.py index 544dc2a4..fb152886 100644 --- a/moban/plugins.py +++ b/moban/plugins.py @@ -46,7 +46,15 @@ def number_of_templated_files(self): return self.templated_count def render_to_file(self, template_file, data_file, output_file): - data = self.context.get_data(data_file) + try: + data = self.context.get_data(data_file) + except Exception as exception: + # If data file doesn't exist: + # 1. Alert the user of their (potential) mistake + # 2. Attempt to use environment vars as data + reporter.report_error_message(str(exception)) + reporter.report_using_env_vars() + data = os.environ template = self.engine.get_template(template_file) template_abs_path = utils.get_template_path( self.template_dirs, template_file @@ -89,7 +97,15 @@ def _render_with_finding_template_first(self, template_file_index): self.template_dirs, template_file ) for (data_file, output) in data_output_pairs: - data = self.context.get_data(data_file) + try: + data = self.context.get_data(data_file) + except Exception as exception: + # If data file doesn't exist: + # 1. Alert the user of their (potential) mistake + # 2. Attempt to use environment vars as data + reporter.report_error_message(exception) + reporter.report_using_env_vars() + data = os.environ flag = self.apply_template( template_abs_path, template, data, output ) @@ -100,7 +116,15 @@ def _render_with_finding_template_first(self, template_file_index): def _render_with_finding_data_first(self, data_file_index): for (data_file, template_output_pairs) in data_file_index.items(): - data = self.context.get_data(data_file) + try: + data = self.context.get_data(data_file) + except Exception as exception: + # If data file doesn't exist: + # 1. Alert the user of their (potential) mistake + # 2. Attempt to use environment vars as data + reporter.report_error_message(exception) + reporter.report_using_env_vars() + data = os.environ for (template_file, output) in template_output_pairs: template = self.engine.get_template(template_file) template_abs_path = utils.get_template_path( diff --git a/moban/reporter.py b/moban/reporter.py index bdb5bc42..fac242de 100644 --- a/moban/reporter.py +++ b/moban/reporter.py @@ -12,6 +12,8 @@ MESSAGE_COPIED_ALL = "Copied {0} files." MESSAGE_PULLING_REPO = "Updating {0}..." MESSAGE_CLONING_REPO = "Cloning {0}..." +MESSAGE_USING_ENV_VARS = "Attempting to use environment vars as data..." +MESSAGE_TEMPLATE_NOT_IN_MOBAN_FILE = "{0} is not defined in your moban file!" def report_templating(source_file, destination_file): @@ -89,6 +91,15 @@ def report_git_clone(repo): print(MESSAGE_CLONING_REPO.format(colored_repo)) +def report_using_env_vars(): + print(crayons.yellow(MESSAGE_USING_ENV_VARS, bold=True)) + + +def report_template_not_in_moban_file(template): + message = MESSAGE_TEMPLATE_NOT_IN_MOBAN_FILE.format(template) + print(crayons.yellow(message, bold=True)) + + def _format_single(message, count): if count == 1: return message.replace("files", "file") diff --git a/setup.py b/setup.py index 27f65c2e..dbaca9af 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ NAME = 'moban' AUTHOR = 'C. W.' -VERSION = '0.3.6' +VERSION = '0.3.7' EMAIL = 'wangc_2011@hotmail.com' LICENSE = 'MIT' ENTRY_POINTS = { @@ -25,7 +25,7 @@ 'Yet another jinja2 cli command for static text generation' ) URL = 'https://github.com/moremoban/moban' -DOWNLOAD_URL = '%s/archive/0.3.6.tar.gz' % URL +DOWNLOAD_URL = '%s/archive/0.3.7.tar.gz' % URL FILES = ['README.rst', 'CONTRIBUTORS.rst', 'CHANGELOG.rst'] KEYWORDS = [ 'python', @@ -60,8 +60,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.3.6 ' + - "Find 0.3.6 in changelog for more details") +GS_COMMAND = ('gs moban v0.3.7 ' + + "Find 0.3.7 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/fixtures/environ_vars_as_data/test.template b/tests/fixtures/environ_vars_as_data/test.template new file mode 100644 index 00000000..b9590f82 --- /dev/null +++ b/tests/fixtures/environ_vars_as_data/test.template @@ -0,0 +1 @@ +{{ TEST_ENVIRONMENT_VARIABLE }} \ No newline at end of file diff --git a/tests/integration_tests/test_command_line_options.py b/tests/integration_tests/test_command_line_options.py index e0ee5e9c..2d9647c3 100644 --- a/tests/integration_tests/test_command_line_options.py +++ b/tests/integration_tests/test_command_line_options.py @@ -95,7 +95,7 @@ def tearDown(self): os.unlink(self.config_file) -@raises(IOError) +@raises(Exception) def test_missing_configuration(): test_args = ["moban", "-t", "a.jj2"] with patch.object(sys, "argv", test_args): @@ -146,8 +146,6 @@ def test_single_command_with_a_few_options(self, fake_template_doer): eq_( call_args, [ - ("README.rst.jj2", "data.yaml", "README.rst"), - ("setup.py.jj2", "data.yaml", "setup.py"), ("abc.jj2", "data.yaml", "xyz.output"), ], ) @@ -171,14 +169,11 @@ def test_single_command_with_options(self, fake_template_doer): eq_( call_args, [ - ("README.rst.jj2", "new.yml", "README.rst"), - ("setup.py.jj2", "new.yml", "setup.py"), ("abc.jj2", "new.yml", "xyz.output"), ], ) @raises(Exception) - @patch("moban.plugins.BaseEngine.render_to_files") def test_single_command_without_output_option(self, fake_template_doer): test_args = ["moban", "-t", "abc.jj2"] with patch.object(sys, "argv", test_args): @@ -265,6 +260,44 @@ def tearDown(self): os.unlink(self.data_file) +class TestTemplateOption: + def setUp(self): + self.config_file = "custom-moban.txt" + copyfile( + os.path.join("tests", "fixtures", ".moban.yml"), self.config_file + ) + self.patcher1 = patch( + "moban.plugins.verify_the_existence_of_directories" + ) + self.patcher1.start() + + @patch("moban.plugins.BaseEngine.render_to_file") + def test_template_option_override_moban_file(self, fake_template_doer): + test_args = ["moban", "-t", "setup.py.jj2"] + with patch.object(sys, "argv", test_args): + from moban.main import main + + main() + fake_template_doer.assert_called_with( + "setup.py.jj2", "data.yml", "moban.output" + ) + + @patch("moban.plugins.BaseEngine.render_to_file") + def test_template_option_not_in_moban_file(self, fake_template_doer): + test_args = ["moban", "-t", "foo.jj2"] + with patch.object(sys, "argv", test_args): + from moban.main import main + + main() + fake_template_doer.assert_called_with( + "foo.jj2", "data.yml", "moban.output" + ) + + def tearDown(self): + self.patcher1.stop() + os.unlink(self.config_file) + + @patch("moban.plugins.verify_the_existence_of_directories") def test_duplicated_targets_in_moban_file(fake_verify): config_file = "duplicated.moban.yml" diff --git a/tests/test_engine.py b/tests/test_engine.py index 9d8d46eb..521be93d 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -99,3 +99,17 @@ def test_nested_global_template_variables(): content = output_file.read() eq_(content, "template: nested.template\ntarget: test.txt\nhere") os.unlink(output) + + +def test_environ_variables_as_data(): + test_var = "TEST_ENVIRONMENT_VARIABLE" + test_value = "foo" + os.environ[test_var] = test_value + output = "test.txt" + path = os.path.join("tests", "fixtures", "environ_vars_as_data") + engine = BaseEngine([path], path, Engine) + engine.render_to_file("test.template", "this_does_not_exist.yml", output) + with open(output, "r") as output_file: + content = output_file.read() + eq_(content, "foo") + os.unlink(output)