Skip to content

Commit 8a9e5a0

Browse files
authored
Add Tuple support to CQGI (#1373)
* Initial draft of Tuple support in CQGI * Fixed default value for Tuple * Handle multiple length tuples * Added a test case for nested tuples * Added test for invalid parameter type error
1 parent 8b7189d commit 8a9e5a0

File tree

2 files changed

+58
-10
lines changed

2 files changed

+58
-10
lines changed

cadquery/cqgi.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ class BooleanParameterType(ParameterType):
219219
pass
220220

221221

222+
class TupleParameterType(ParameterType):
223+
pass
224+
225+
222226
class InputParameter:
223227
"""
224228
Defines a parameter that can be supplied when the model is executed.
@@ -304,6 +308,15 @@ def set_value(self, new_value):
304308
self.ast_node.value = False
305309
else:
306310
self.ast_node.id = "False"
311+
elif self.varType == TupleParameterType:
312+
self.ast_node.n = new_value
313+
314+
# Build the list of constants to set as the tuple value
315+
constants = []
316+
for nv in new_value:
317+
constants.append(ast.Constant(value=nv))
318+
self.ast_node.elts = constants
319+
ast.fix_missing_locations(self.ast_node)
307320
else:
308321
raise ValueError("Unknown Type of var: ", str(self.varType))
309322

@@ -478,7 +491,6 @@ def __init__(self, cq_model):
478491

479492
def handle_assignment(self, var_name, value_node):
480493
try:
481-
482494
if type(value_node) == ast.Num:
483495
self.cqModel.add_script_parameter(
484496
InputParameter.create(
@@ -504,6 +516,17 @@ def handle_assignment(self, var_name, value_node):
504516
value_node, var_name, BooleanParameterType, False
505517
)
506518
)
519+
elif type(value_node) == ast.Tuple:
520+
# Handle multi-length tuples
521+
tup = ()
522+
for entry in value_node.elts:
523+
tup = tup + (entry.value,)
524+
525+
self.cqModel.add_script_parameter(
526+
InputParameter.create(
527+
value_node, var_name, TupleParameterType, tup,
528+
)
529+
)
507530
elif hasattr(ast, "NameConstant") and type(value_node) == ast.NameConstant:
508531
if value_node.value == True:
509532
self.cqModel.add_script_parameter(
@@ -525,6 +548,7 @@ def handle_assignment(self, var_name, value_node):
525548
str: StringParameterType,
526549
float: NumberParameterType,
527550
int: NumberParameterType,
551+
tuple: TupleParameterType,
528552
}
529553

530554
self.cqModel.add_script_parameter(
@@ -561,8 +585,7 @@ def visit_Assign(self, node):
561585
self.handle_assignment(left_side.id, node.value)
562586
elif type(node.value) == ast.Tuple:
563587
if isinstance(left_side, ast.Name):
564-
# skip unsupported parameter type
565-
pass
588+
self.handle_assignment(left_side.id, node.value)
566589
else:
567590
# we have a multi-value assignment
568591
for n, v in zip(left_side.elts, node.value.elts):

tests/test_cqgi.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
defining a build_object function to return results
88
"""
99

10+
import pytest
1011
from cadquery import cqgi
1112
from tests import BaseTest
1213
import textwrap
@@ -16,9 +17,10 @@
1617
height=2.0
1718
width=3.0
1819
(a,b) = (1.0,1.0)
20+
o = (2, 2, 0)
1921
foo="bar"
2022
21-
result = "%s|%s|%s|%s" % ( str(height) , str(width) , foo , str(a) )
23+
result = "%s|%s|%s|%s|%s" % ( str(height) , str(width) , foo , str(a) , str(o) )
2224
show_object(result)
2325
"""
2426
)
@@ -28,9 +30,10 @@
2830
height=2.0
2931
width=3.0
3032
(a,b) = (1.0,1.0)
33+
o = (2, 2, 0)
3134
foo="bar"
3235
debug(foo, { "color": 'yellow' } )
33-
result = "%s|%s|%s|%s" % ( str(height) , str(width) , foo , str(a) )
36+
result = "%s|%s|%s|%s|%s" % ( str(height) , str(width) , foo , str(a) , str(o) )
3437
show_object(result)
3538
debug(height )
3639
"""
@@ -41,9 +44,8 @@ class TestCQGI(BaseTest):
4144
def test_parser(self):
4245
model = cqgi.CQModel(TESTSCRIPT)
4346
metadata = model.metadata
44-
4547
self.assertEqual(
46-
set(metadata.parameters.keys()), {"height", "width", "a", "b", "foo"}
48+
set(metadata.parameters.keys()), {"height", "width", "a", "b", "foo", "o"}
4749
)
4850

4951
def test_build_with_debug(self):
@@ -62,12 +64,19 @@ def test_build_with_empty_params(self):
6264

6365
self.assertTrue(result.success)
6466
self.assertTrue(len(result.results) == 1)
65-
self.assertTrue(result.results[0].shape == "2.0|3.0|bar|1.0")
67+
self.assertTrue(result.results[0].shape == "2.0|3.0|bar|1.0|(2, 2, 0)")
6668

6769
def test_build_with_different_params(self):
6870
model = cqgi.CQModel(TESTSCRIPT)
69-
result = model.build({"height": 3.0})
70-
self.assertTrue(result.results[0].shape == "3.0|3.0|bar|1.0")
71+
result = model.build({"height": 3.0, "o": (3, 3)})
72+
print(result.results[0].shape)
73+
self.assertTrue(result.results[0].shape == "3.0|3.0|bar|1.0|(3, 3)")
74+
75+
def test_build_with_nested_tuple_params(self):
76+
model = cqgi.CQModel(TESTSCRIPT)
77+
result = model.build({"height": 3.0, "o": ((2, 2), (3, 3))})
78+
print(result.results[0].shape)
79+
self.assertTrue(result.results[0].shape == "3.0|3.0|bar|1.0|((2, 2), (3, 3))")
7180

7281
def test_describe_parameters(self):
7382
script = textwrap.dedent(
@@ -222,3 +231,19 @@ def do_stuff():
222231
model = cqgi.parse(script)
223232

224233
self.assertEqual(2, len(model.metadata.parameters))
234+
235+
def test_invalid_parameter_type(self):
236+
"""Contrived test in case a parameter type that is not valid for InputParameter sneaks through."""
237+
238+
# Made up parameter class
239+
class UnknowParameter:
240+
def __init__():
241+
return 1
242+
243+
# Set up the most basic InputParameter object that is possible
244+
p = cqgi.InputParameter()
245+
p.varType = UnknowParameter
246+
247+
# Setting the parameter should throw an unknown parameter type error
248+
with pytest.raises(ValueError) as info:
249+
p.set_value(2)

0 commit comments

Comments
 (0)