From 2bc35598485c0c8e806934332335bbf811f235ec Mon Sep 17 00:00:00 2001 From: Justin Buchanan Date: Wed, 15 Nov 2023 17:33:53 -0800 Subject: [PATCH 1/7] add --entrypoint flag --- src/cq_cli/main.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/cq_cli/main.py b/src/cq_cli/main.py index 914fbd8..7fad4ac 100755 --- a/src/cq_cli/main.py +++ b/src/cq_cli/main.py @@ -20,13 +20,15 @@ from cq_cli.cqcodecs import loader -def build_and_parse(script_str, params, errfile): +def build_and_parse(script_str, params, errfile, entrypoint): """ Uses CQGI to parse and build a script, substituting in parameters if any were supplied. """ # We need to do a broad try/catch to let the user know if something higher-level fails try: # Do the CQGI handling of the script here and, if successful, pass the build result to the codec + if entrypoint != None: + script_str += "\n" + entrypoint cqModel = cqgi.parse(script_str) build_result = cqModel.build(params) @@ -181,6 +183,10 @@ def main(): "--validate", help="Setting to true forces the CLI to only parse and validate the script and not produce converted output.", ) + parser.add_argument( + "--entrypoint", + help="A snipped of python code to append to the input file before rendering. This allows rendering different models/parts from the same python file.", + ) args = parser.parse_args() @@ -223,7 +229,7 @@ def main(): # Set the PYTHONPATH variable to the current directory to allow module loading set_pythonpath_for_infile(args.infile) - build_result = build_and_parse(script_str, params, errfile) + build_result = build_and_parse(script_str, params, errfile, args.entrypoint) # Double-check that the build was a success if build_result != None and build_result.success: @@ -424,7 +430,7 @@ def main(): # build_result = None try: - build_result = build_and_parse(script_str, params, errfile) + build_result = build_and_parse(script_str, params, errfile, args.entrypoint) # If None was returned, it means the build failed and the exception has already been reported if build_result == None: From 950c72898a6ae133628f82a953d4d75d36639d0a Mon Sep 17 00:00:00 2001 From: Justin Buchanan Date: Wed, 15 Nov 2023 17:47:42 -0800 Subject: [PATCH 2/7] typo --- src/cq_cli/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cq_cli/main.py b/src/cq_cli/main.py index 7fad4ac..475be33 100755 --- a/src/cq_cli/main.py +++ b/src/cq_cli/main.py @@ -185,7 +185,7 @@ def main(): ) parser.add_argument( "--entrypoint", - help="A snipped of python code to append to the input file before rendering. This allows rendering different models/parts from the same python file.", + help="A snippet of python code to append to the input file before rendering. This allows rendering different models/parts from the same python file.", ) args = parser.parse_args() From 8af49987e30a1e068b612ba56e969ab945d278fe Mon Sep 17 00:00:00 2001 From: Justin Buchanan Date: Mon, 20 Nov 2023 21:39:03 -0800 Subject: [PATCH 3/7] don't require entrypoint to contain show_object() --entrypoint is now just the name of a python function to call and show --- src/cq_cli/main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cq_cli/main.py b/src/cq_cli/main.py index 475be33..9c22a09 100755 --- a/src/cq_cli/main.py +++ b/src/cq_cli/main.py @@ -28,7 +28,9 @@ def build_and_parse(script_str, params, errfile, entrypoint): try: # Do the CQGI handling of the script here and, if successful, pass the build result to the codec if entrypoint != None: - script_str += "\n" + entrypoint + if not entrypoint.isidentifier(): + raise ValueError("Entrypoint is not a valid python function name") + script_str += "\nshow_object({f}())".format(f=entrypoint) cqModel = cqgi.parse(script_str) build_result = cqModel.build(params) @@ -185,7 +187,7 @@ def main(): ) parser.add_argument( "--entrypoint", - help="A snippet of python code to append to the input file before rendering. This allows rendering different models/parts from the same python file.", + help="The name of a python function that returns a cadquery model object. cq-cli will call the function and render the resulting object. This allows rendering different models/parts from the same python file.", ) args = parser.parse_args() From 3f74867a90c3149b14366096e536d3389f51f557 Mon Sep 17 00:00:00 2001 From: Justin Buchanan Date: Tue, 21 Nov 2023 22:07:27 -0800 Subject: [PATCH 4/7] change from --entrypoint to --expression --- src/cq_cli/main.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cq_cli/main.py b/src/cq_cli/main.py index 9c22a09..64377a3 100755 --- a/src/cq_cli/main.py +++ b/src/cq_cli/main.py @@ -20,17 +20,15 @@ from cq_cli.cqcodecs import loader -def build_and_parse(script_str, params, errfile, entrypoint): +def build_and_parse(script_str, params, errfile, expression): """ Uses CQGI to parse and build a script, substituting in parameters if any were supplied. """ # We need to do a broad try/catch to let the user know if something higher-level fails try: # Do the CQGI handling of the script here and, if successful, pass the build result to the codec - if entrypoint != None: - if not entrypoint.isidentifier(): - raise ValueError("Entrypoint is not a valid python function name") - script_str += "\nshow_object({f}())".format(f=entrypoint) + if expression != None: + script_str += "\nshow_object({expr})".format(expr=expression) cqModel = cqgi.parse(script_str) build_result = cqModel.build(params) @@ -186,8 +184,8 @@ def main(): help="Setting to true forces the CLI to only parse and validate the script and not produce converted output.", ) parser.add_argument( - "--entrypoint", - help="The name of a python function that returns a cadquery model object. cq-cli will call the function and render the resulting object. This allows rendering different models/parts from the same python file.", + "--expression", + help="A python expression (such as `my_shape(x=5)`) to evaluate and render. This allows rendering different models/parts from the same python file.", ) args = parser.parse_args() @@ -231,7 +229,7 @@ def main(): # Set the PYTHONPATH variable to the current directory to allow module loading set_pythonpath_for_infile(args.infile) - build_result = build_and_parse(script_str, params, errfile, args.entrypoint) + build_result = build_and_parse(script_str, params, errfile, args.expression) # Double-check that the build was a success if build_result != None and build_result.success: @@ -432,7 +430,7 @@ def main(): # build_result = None try: - build_result = build_and_parse(script_str, params, errfile, args.entrypoint) + build_result = build_and_parse(script_str, params, errfile, args.expression) # If None was returned, it means the build failed and the exception has already been reported if build_result == None: From a98b9a7988a77b79dca4fd9582a26eceef86a77f Mon Sep 17 00:00:00 2001 From: Justin Buchanan Date: Wed, 22 Nov 2023 19:51:06 -0800 Subject: [PATCH 5/7] add test for --expression argument --- tests/test_cli.py | 50 +++++++++++++++++++++++++++ tests/testdata/no_toplevel_objects.py | 7 ++++ 2 files changed, 57 insertions(+) create mode 100644 tests/testdata/no_toplevel_objects.py diff --git a/tests/test_cli.py b/tests/test_cli.py index 75c1a6d..b8ce14f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -407,3 +407,53 @@ def test_exit_codes(): # Make sure that we got exit code 100 for a failed model build assert exitcode == 100 + + +def test_expression_argument(): + """ + Tests the CLI with the the expression argument. + """ + test_file = helpers.get_test_file_location("no_toplevel_objects.py") + + # Get a temporary output file location + temp_dir = tempfile.gettempdir() + temp_file = os.path.join(temp_dir, "temp_test_10.step") + + command = [ + "python", + "src/cq_cli/main.py", + "--codec", + "step", + "--infile", + test_file, + "--outfile", + temp_file, + "--expression", + "cube()", + ] + out, err, exitcode = helpers.cli_call(command) + + # Read the STEP output back from the outfile + with open(temp_file, "r") as file: + step_str = file.read() + + assert step_str.startswith("ISO-10303-21;") + + # Run cq-cli on the same model file, but don't specify an --expression. This + # should fail because the file contains no top-level show_object() calls. + command = [ + "python", + "src/cq_cli/main.py", + "--codec", + "step", + "--infile", + test_file, + "--outfile", + temp_file, + ] + out, err, exitcode = helpers.cli_call(command) + + print("err: %s" % err.decode()) + + # cq-cli invocation should fail + assert exitcode == 200 diff --git a/tests/testdata/no_toplevel_objects.py b/tests/testdata/no_toplevel_objects.py new file mode 100644 index 0000000..b45f5c8 --- /dev/null +++ b/tests/testdata/no_toplevel_objects.py @@ -0,0 +1,7 @@ +import cadquery as cq + +# This test file contains a method for creating a shape, but does not contain +# any top-level `show_object()` calls. + +def cube(): + return cq.Workplane().box(1, 1, 1) From 70282b8fd45e72f2650f2e65b6f4b28338dd31eb Mon Sep 17 00:00:00 2001 From: Justin Buchanan Date: Wed, 22 Nov 2023 19:55:11 -0800 Subject: [PATCH 6/7] fix black formatting error --- tests/testdata/no_toplevel_objects.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/testdata/no_toplevel_objects.py b/tests/testdata/no_toplevel_objects.py index b45f5c8..531e1fd 100644 --- a/tests/testdata/no_toplevel_objects.py +++ b/tests/testdata/no_toplevel_objects.py @@ -3,5 +3,6 @@ # This test file contains a method for creating a shape, but does not contain # any top-level `show_object()` calls. + def cube(): return cq.Workplane().box(1, 1, 1) From de4fbdf91b635589d64ebc9068b32de88c4ab2e1 Mon Sep 17 00:00:00 2001 From: Justin Buchanan Date: Wed, 22 Nov 2023 20:32:50 -0800 Subject: [PATCH 7/7] comment --- tests/test_cli.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index b8ce14f..fe77199 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -419,6 +419,7 @@ def test_expression_argument(): temp_dir = tempfile.gettempdir() temp_file = os.path.join(temp_dir, "temp_test_10.step") + # Run cq-cli with --expression "cube()" command = [ "python", "src/cq_cli/main.py", @@ -453,7 +454,5 @@ def test_expression_argument(): ] out, err, exitcode = helpers.cli_call(command) - print("err: %s" % err.decode()) - # cq-cli invocation should fail assert exitcode == 200