added describe_parameter
This commit is contained in:
parent
7c3bf01779
commit
b22409d88c
|
@ -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:
|
||||
|
|
18
doc/cqgi.rst
18
doc/cqgi.rst
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue
Block a user