added describe_parameter

This commit is contained in:
Dave Cowden 2016-04-05 21:04:09 -04:00
parent 7c3bf01779
commit b22409d88c
4 changed files with 83 additions and 17 deletions

View File

@ -44,9 +44,11 @@ class CQModel(object):
self.ast_tree = ast.parse(script_source, CQSCRIPT)
self.script_source = script_source
self._find_vars()
# TODO: pick up other scirpt metadata:
# describe
# pick up validation methods
self._find_descriptions()
def _find_vars(self):
"""
@ -65,6 +67,9 @@ class CQModel(object):
if isinstance(node, ast.Assign):
assignment_finder.visit_Assign(node)
def _find_descriptions(self):
description_finder = ParameterDescriptionFinder(self.metadata)
description_finder.visit(self.ast_tree)
def validate(self, params):
"""
@ -99,6 +104,7 @@ class CQModel(object):
env = EnvironmentBuilder().with_real_builtins().with_cadquery_objects() \
.add_entry("build_object", collector.build_object) \
.add_entry("debug", collector.debug) \
.add_entry("describe_parameter",collector.describe_parameter) \
.build()
c = compile(self.ast_tree, CQSCRIPT, 'exec')
@ -173,6 +179,11 @@ class ScriptMetadata(object):
def add_script_parameter(self, p):
self.parameters[p.name] = p
def add_parameter_description(self,name,description):
print 'Adding Parameter name=%s, desc=%s' % ( name, description )
p = self.parameters[name]
p.desc = description
class ParameterType(object):
pass
@ -213,19 +224,15 @@ class InputParameter:
self.varType = None
#: help text describing the variable. Only available if the script used describe_parameter()
self.shortDesc = None
self.desc = None
#: valid values for the variable. Only available if the script used describe_parameter()
self.valid_values = []
self.ast_node = None
@staticmethod
def create(ast_node, var_name, var_type, default_value, valid_values=None, short_desc=None):
def create(ast_node, var_name, var_type, default_value, valid_values=None, desc=None):
if valid_values is None:
valid_values = []
@ -234,10 +241,7 @@ class InputParameter:
p.ast_node = ast_node
p.default_value = default_value
p.name = var_name
if short_desc is None:
p.shortDesc = var_name
else:
p.shortDesc = short_desc
p.desc = desc
p.varType = var_type
p.valid_values = valid_values
return p
@ -296,10 +300,9 @@ class ScriptCallback(object):
"""
self.debugObjects.append(DebugObject(obj,args))
def describe_parameter(self,var, valid_values, short_desc):
def describe_parameter(self,var_data ):
"""
Not yet implemented: allows a script to document
extra metadata about the parameters
Do Nothing-- we parsed the ast ahead of exection to get what we need.
"""
pass
@ -394,6 +397,30 @@ class EnvironmentBuilder(object):
def build(self):
return self.env
class ParameterDescriptionFinder(ast.NodeTransformer):
"""
Visits a parse tree, looking for function calls to describe_parameter(var, description )
"""
def __init__(self, cq_model):
self.cqModel = cq_model
def visit_Call(self,node):
"""
Called when we see a function call. Is it describe_parameter?
"""
try:
if node.func.id == 'describe_parameter':
#looks like we have a call to our function.
#first parameter is the variable,
#second is the description
varname = node.args[0].id
desc = node.args[1].s
self.cqModel.add_parameter_description(varname,desc)
except:
print "Unable to handle function call"
pass
return node
class ConstantAssignmentFinder(ast.NodeTransformer):
"""
@ -404,9 +431,6 @@ class ConstantAssignmentFinder(ast.NodeTransformer):
self.cqModel = cq_model
def handle_assignment(self, var_name, value_node):
try:
if type(value_node) == ast.Num:

View File

@ -70,6 +70,24 @@ run code like this::
The :py:meth:`cadquery.cqgi.parse()` method returns a :py:class:`cadquery.cqgi.CQModel` object.
The `metadata`p property of the object contains a `cadquery.cqgi.ScriptMetaData` object, which can be used to discover the
user parameters available. This is useful if the execution environment would like to present a GUI to allow the user to change the
model parameters. Typically, after collecting new values, the environment will supply them in the build() method.
This code will return a dictionary of parameter values in the model text SCRIPT::
parameters = cqgi.parse(SCRIPT).metadata.parameters
The dictionary you get back is a map where key is the parameter name, and value is an InputParameter object,
which has a name, type, and default value.
The type is an object which extends ParameterType-- you can use this to determine what kind of widget to render ( checkbox for boolean, for example ).
The parameter object also has a description, valid values, minimum, and maximum values, if the user has provided them using the
describe_parameter() method.
Calling :py:meth:`cadquery.cqgi.CQModel.build()` returns a :py:class:`cadquery.cqgi.BuildResult` object,
,which includes the script execution time, and a success flag.

View File

@ -8,11 +8,11 @@ import unittest
#on py 2.7.x on win
suite = unittest.TestSuite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQGI.TestCQGI))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadObjects.TestCadObjects))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestWorkplanes.TestWorkplanes))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQSelectors.TestCQSelectors))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadQuery.TestCadQuery))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestExporters.TestExporters))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestImporters.TestImporters))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQGI.TestCQGI))
unittest.TextTestRunner().run(suite)

View File

@ -66,6 +66,30 @@ class TestCQGI(BaseTest):
result = model.build({'height': 3.0})
self.assertTrue(result.results[0] == "3.0|3.0|bar|1.0")
def test_describe_parameters(self):
script = textwrap.dedent(
"""
a = 2.0
describe_parameter(a,'FirstLetter')
"""
)
model = cqgi.CQModel(script)
a_param = model.metadata.parameters['a']
self.assertTrue(a_param.default_value == 2.0)
self.assertTrue(a_param.desc == 'FirstLetter')
self.assertTrue(a_param.varType == cqgi.NumberParameterType )
def test_describe_parameter_invalid_doesnt_fail_script(self):
script = textwrap.dedent(
"""
a = 2.0
describe_parameter(a, 2 - 1 )
"""
)
model = cqgi.CQModel(script)
a_param = model.metadata.parameters['a']
self.assertTrue(a_param.name == 'a' )
def test_build_with_exception(self):
badscript = textwrap.dedent(
"""