got first directive workign

This commit is contained in:
Dave Cowden 2015-12-08 22:36:19 -05:00
parent 4e2168cad6
commit 190980d4a1
5 changed files with 110 additions and 58 deletions

View File

@ -13,7 +13,9 @@ from .CQ import *
__all__ = [ __all__ = [
'CQ','Workplane','plugins','selectors','Plane','BoundBox','Matrix','Vector','sortWiresByBuildOrder', 'CQ','Workplane','plugins','selectors','Plane','BoundBox','Matrix','Vector','sortWiresByBuildOrder',
'Shape','Vertex','Edge','Wire','Solid','Shell','Compound','exporters', 'importers', 'NearestToPointSelector','ParallelDirSelector','DirectionSelector','PerpendicularDirSelector','TypeSelector','DirectionMinMaxSelector','StringSyntaxSelector','Selector','plugins' 'Shape','Vertex','Edge','Wire','Solid','Shell','Compound','exporters', 'importers',
'NearestToPointSelector','ParallelDirSelector','DirectionSelector','PerpendicularDirSelector',
'TypeSelector','DirectionMinMaxSelector','StringSyntaxSelector','Selector','plugins'
] ]
__version__ = "0.3.0" __version__ = "0.3.0"

View File

@ -3,19 +3,18 @@ A special directive for including a cq object.
""" """
import sys, os, shutil, imp, warnings, cStringIO, re,traceback import traceback
from cadquery import * from cadquery import *
from cadquery import cqgi
import StringIO import StringIO
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
template = """ template = """
.. raw:: html .. raw:: html
<div class="cq" style="text-align:%(txtAlign)s;float:left;"> <div class="cq" style="text-align:%(txt_align)s;float:left;">
%(outSVG)s %(out_svg)s
</div> </div>
<div style="clear:both;"> <div style="clear:both;">
</div> </div>
@ -25,37 +24,39 @@ template_content_indent = ' '
def cq_directive(name, arguments, options, content, lineno, def cq_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine): content_offset, block_text, state, state_machine):
# only consider inline snippets
#only consider inline snippets
plot_code = '\n'.join(content) plot_code = '\n'.join(content)
# Since we don't have a filename, use a hash based on the content # Since we don't have a filename, use a hash based on the content
#the script must define a variable called 'out', which is expected to # the script must define a variable called 'out', which is expected to
#be a CQ object # be a CQ object
outSVG = "Your Script Did not assign the 'result' variable!" out_svg = "Your Script Did not assign call build_output() function!"
try: try:
_s = StringIO.StringIO() _s = StringIO.StringIO()
exec(plot_code) result = cqgi.execute(plot_code)
exporters.exportShape(result,"SVG",_s) if result.success:
outSVG = _s.getvalue() exporters.exportShape(result.first_result, "SVG", _s)
except: out_svg = _s.getvalue()
else:
raise result.exception
except Exception:
traceback.print_exc() traceback.print_exc()
outSVG = traceback.format_exc() out_svg = traceback.format_exc()
#now out # now out
# Now start generating the lines of output # Now start generating the lines of output
lines = [] lines = []
#get rid of new lines # get rid of new lines
outSVG = outSVG.replace('\n','') out_svg = out_svg.replace('\n', '')
txtAlign = "left" txt_align = "left"
if options.has_key("align"): if "align" in options:
txtAlign = options['align'] txt_align = options['align']
lines.extend((template % locals()).split('\n')) lines.extend((template % locals()).split('\n'))
@ -70,6 +71,7 @@ def cq_directive(name, arguments, options, content, lineno,
return [] return []
def setup(app): def setup(app):
setup.app = app setup.app = app
setup.config = app.config setup.config = app.config
@ -78,8 +80,6 @@ def setup(app):
options = {'height': directives.length_or_unitless, options = {'height': directives.length_or_unitless,
'width': directives.length_or_percentage_or_unitless, 'width': directives.length_or_percentage_or_unitless,
'align': directives.unchanged 'align': directives.unchanged
} }
app.add_directive('cq_plot', cq_directive, True, (0, 2, 0), **options) app.add_directive('cq_plot', cq_directive, True, (0, 2, 0), **options)

View File

@ -7,10 +7,30 @@ import ast
import traceback import traceback
import re import re
import time import time
import cadquery
CQSCRIPT = "<cqscript>" CQSCRIPT = "<cqscript>"
def execute(script_source, build_parameters=None):
"""
Executes the provided model, using the specified variables.
If you would prefer to access the underlying model without building it,
for example, to inspect its available parameters, construct a CQModel object.
:param script_source: the script to run. Must be a valid cadquery script
:param build_parameters: a dictionary of variables. The variables must be
assignable to the underlying variable type.
:raises: Nothing. If there is an exception, it will be on the exception property of the result.
This is the interface so that we can return other information onthe result, such as the build time
:return: a BuildResult object, which includes the status of the result, and either
a resulting shape or an exception
"""
model = CQModel(script_source)
return model.build(build_parameters)
class CQModel(object): class CQModel(object):
""" """
Object that provides a nice interface to a cq script that Object that provides a nice interface to a cq script that
@ -19,9 +39,9 @@ class CQModel(object):
def __init__(self, script_source): def __init__(self, script_source):
self.metadata = ScriptMetadata() self.metadata = ScriptMetadata()
self.astTree = ast.parse(script_source, CQSCRIPT) self.ast_tree = ast.parse(script_source, CQSCRIPT)
ConstantAssignmentFinder(self.metadata).visit(self.astTree) ConstantAssignmentFinder(self.metadata).visit(self.ast_tree)
# TODO: pick up other scirpt metadata: # TODO: pick up other scirpt metadata:
# describe # describe
@ -39,21 +59,21 @@ class CQModel(object):
if not params: if not params:
params = {} params = {}
self.set_param_values(params)
collector = BuildObjectCollector()
env = EnvironmentBuilder().with_real_builtins() \
.add_entry("build_object", collector.build_object).build()
start = time.clock() start = time.clock()
result = BuildResult() result = BuildResult()
try: try:
c = compile(self.astTree, CQSCRIPT, 'exec') self.set_param_values(params)
collector = BuildObjectCollector()
env = EnvironmentBuilder().with_real_builtins().with_cadquery_objects() \
.add_entry("build_object", collector.build_object).build()
c = compile(self.ast_tree, CQSCRIPT, 'exec')
exec (c, env) exec (c, env)
if collector.hasResults(): if collector.has_results():
result.set_success_result(collector.outputObjects) result.set_success_result(collector.outputObjects)
else: else:
raise ValueError("Script did not call build_object-- no output available.") raise NoOutputError("Script did not call build_object-- no output available.")
except Exception, ex: except Exception, ex:
result.set_failure_result(ex) result.set_failure_result(ex)
@ -76,6 +96,7 @@ class BuildResult(object):
def __init__(self): def __init__(self):
self.buildTime = None self.buildTime = None
self.results = [] self.results = []
self.first_result = None
self.success = False self.success = False
self.exception = None self.exception = None
@ -85,6 +106,7 @@ class BuildResult(object):
def set_success_result(self, results): def set_success_result(self, results):
self.results = results self.results = results
self.first_result = self.results[0]
self.success = True self.success = True
@ -117,7 +139,7 @@ class InputParameter:
self.name = None self.name = None
self.shortDesc = None self.shortDesc = None
self.varType = None self.varType = None
self.validValues = [] self.valid_values = []
self.default_value = None self.default_value = None
self.ast_node = None self.ast_node = None
@ -136,15 +158,15 @@ class InputParameter:
else: else:
p.shortDesc = short_desc p.shortDesc = short_desc
p.varType = var_type p.varType = var_type
p.validValues = valid_values p.valid_values = valid_values
return p return p
def set_value(self, new_value): def set_value(self, new_value):
if len(self.validValues) > 0 and not new_value in self.validValues: if len(self.valid_values) > 0 and new_value not in self.valid_values:
raise InvalidParameterError( raise InvalidParameterError(
"Cannot set value '{0:s}' for parameter '{1:s}': not a valid value. Valid values are {2:s} " "Cannot set value '{0:s}' for parameter '{1:s}': not a valid value. Valid values are {2:s} "
.format( str(new_value), self.name, str(self.validValues))) .format(str(new_value), self.name, str(self.valid_values)))
if self.varType == NumberParameterType: if self.varType == NumberParameterType:
try: try:
@ -181,7 +203,7 @@ class BuildObjectCollector(object):
def build_object(self, shape): def build_object(self, shape):
self.outputObjects.append(shape) self.outputObjects.append(shape)
def hasResults(self): def has_results(self):
return len(self.outputObjects) > 0 return len(self.outputObjects) > 0
@ -190,10 +212,10 @@ class ScriptExecutor(object):
executes a script in a given environment. executes a script in a given environment.
""" """
def __init__(self, environment, astTree): def __init__(self, environment, ast_tree):
try: try:
exec (astTree) in environment exec ast_tree in environment
except Exception, ex: except Exception, ex:
# an error here means there was a problem compiling the script # an error here means there was a problem compiling the script
@ -219,6 +241,10 @@ class InvalidParameterError(Exception):
pass pass
class NoOutputError(Exception):
pass
class ScriptExecutionError(Exception): class ScriptExecutionError(Exception):
""" """
Represents a script syntax error. Represents a script syntax error.
@ -258,6 +284,10 @@ class EnvironmentBuilder(object):
self.env['__builtins__'] = env_dict self.env['__builtins__'] = env_dict
return self return self
def with_cadquery_objects(self):
self.env['cadquery'] = cadquery
return self
def add_entry(self, name, value): def add_entry(self, name, value):
self.env[name] = value self.env[name] = value
return self return self

View File

@ -43,7 +43,8 @@ Just about the simplest possible example, a rectangular box
.. cq_plot:: .. cq_plot::
result = Workplane("front").box(2.0,2.0,0.5) result = cadquery.Workplane("front").box(2.0,2.0,0.5)
build_object(result)
.. topic:: Api References .. topic:: Api References
@ -64,7 +65,7 @@ of a working plane is at the center of the face. The default hole depth is thro
.. cq_plot:: .. cq_plot::
result = Workplane("front").box(2.0,2.0,0.5).faces(">Z").hole(0.5) result = Workplane("front").box(2.0,2.0,0.5).faces(">Z").hole(0.5)
build_output(result)
.. topic:: Api References .. topic:: Api References
@ -533,6 +534,8 @@ with just a few lines of code.
.rect(length-padding,height-padding,forConstruction=True) \ .rect(length-padding,height-padding,forConstruction=True) \
.vertices().cboreHole(2.4,4.4,2.1) .vertices().cboreHole(2.4,4.4,2.1)
build_output(result)
Splitting an Object Splitting an Object
--------------------- ---------------------

View File

@ -92,7 +92,7 @@ class TestCQGI(BaseTest):
build_object(h) build_object(h)
""" """
) )
result = self._executeScriptWithParams(script, {'h': 33.33}) result = cqgi.execute(script, {'h': 33.33})
self.assertEquals(result.results[0], "33.33") self.assertEquals(result.results[0], "33.33")
def test_that_assigning_string_to_number_fails(self): def test_that_assigning_string_to_number_fails(self):
@ -102,20 +102,37 @@ class TestCQGI(BaseTest):
build_object(h) build_object(h)
""" """
) )
with self.assertRaises(Exception): result = cqgi.execute(script, {'h': "a string"})
result = self._executeScriptWithParams(script, {'h': "a string"}) self.assertTrue(isinstance(result.exception, cqgi.InvalidParameterError))
def test_that_assigning_unknown_var_fails(self): def test_that_assigning_unknown_var_fails(self):
script = textwrap.dedent( script = textwrap.dedent(
""" """
h = 20.0 h = 20.0
build_object(h) build_object(h)
""" """
) )
with self.assertRaises(cqgi.InvalidParameterError):
result = self._executeScriptWithParams(script, {'w': "var is not there"})
def _executeScriptWithParams(self, script, params): result = cqgi.execute(script, {'w': "var is not there"})
model = cqgi.CQModel(script) self.assertTrue(isinstance(result.exception, cqgi.InvalidParameterError))
return model.build(params)
def test_that_not_calling_build_object_raises_error(self):
script = textwrap.dedent(
"""
h = 20.0
"""
)
result = cqgi.execute(script)
self.assertTrue(isinstance(result.exception, cqgi.NoOutputError))
def test_that_cq_objects_are_visible(self):
script = textwrap.dedent(
"""
r = cadquery.Workplane('XY').box(1,2,3)
build_object(r)
"""
)
result = cqgi.execute(script)
self.assertTrue(result.success)
self.assertIsNotNone(result.first_result)