diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 721376016c..acd25eb166 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -133,6 +133,7 @@ def reset_perms(context): def execute(context, method, args=None, kwargs=None, profile=False): "Execute a function" for site in context.sites: + ret = "" try: frappe.init(site=site) frappe.connect() @@ -154,7 +155,10 @@ def execute(context, method, args=None, kwargs=None, profile=False): pr = cProfile.Profile() pr.enable() - ret = frappe.get_attr(method)(*args, **kwargs) + try: + ret = frappe.get_attr(method)(*args, **kwargs) + except Exception: + ret = frappe.safe_eval(method + "(*args, **kwargs)", eval_globals=globals(), eval_locals=locals()) if profile: pr.disable() diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py new file mode 100644 index 0000000000..82c0cdce5c --- /dev/null +++ b/frappe/tests/test_commands.py @@ -0,0 +1,46 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors + +# imports - standard imports +import shlex +import subprocess +import unittest + +# imports - module imports +import frappe + + +def clean(value): + if isinstance(value, (bytes, str)): + value = value.decode().strip() + return value + + +class BaseTestCommands: + def execute(self, command): + command = command.format(**{"site": frappe.local.site}) + command = shlex.split(command) + self._proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.stdout = clean(self._proc.stdout) + self.stderr = clean(self._proc.stderr) + self.returncode = clean(self._proc.returncode) + + +class TestCommands(BaseTestCommands, unittest.TestCase): + def test_execute(self): + # test 1: execute a command expecting a numeric output + self.execute("bench --site {site} execute frappe.db.get_database_size") + self.assertEquals(self.returncode, 0) + self.assertIsInstance(float(self.stdout), float) + + # test 2: execute a command expecting an errored output as local won't exist + self.execute("bench --site {site} execute frappe.local.site") + self.assertEquals(self.returncode, 1) + self.assertIsNotNone(self.stderr) + + # test 3: execute a command with kwargs + # Note: + # terminal command has been escaped to avoid .format string replacement + # The returned value has quotes which have been trimmed for the test + self.execute("""bench --site {site} execute frappe.bold --kwargs '{{"text": "DocType"}}'""") + self.assertEquals(self.returncode, 0) + self.assertEquals(self.stdout[1:-1], frappe.bold(text='DocType'))