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__ = [
'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"

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

View File

@ -7,10 +7,30 @@ import ast
import traceback
import re
import time
import cadquery
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):
"""
Object that provides a nice interface to a cq script that
@ -19,9 +39,9 @@ class CQModel(object):
def __init__(self, script_source):
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:
# describe
@ -39,21 +59,21 @@ class CQModel(object):
if not 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()
result = BuildResult()
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)
if collector.hasResults():
if collector.has_results():
result.set_success_result(collector.outputObjects)
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:
result.set_failure_result(ex)
@ -76,6 +96,7 @@ class BuildResult(object):
def __init__(self):
self.buildTime = None
self.results = []
self.first_result = None
self.success = False
self.exception = None
@ -85,6 +106,7 @@ class BuildResult(object):
def set_success_result(self, results):
self.results = results
self.first_result = self.results[0]
self.success = True
@ -117,7 +139,7 @@ class InputParameter:
self.name = None
self.shortDesc = None
self.varType = None
self.validValues = []
self.valid_values = []
self.default_value = None
self.ast_node = None
@ -136,15 +158,15 @@ class InputParameter:
else:
p.shortDesc = short_desc
p.varType = var_type
p.validValues = valid_values
p.valid_values = valid_values
return p
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(
"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:
try:
@ -181,7 +203,7 @@ class BuildObjectCollector(object):
def build_object(self, shape):
self.outputObjects.append(shape)
def hasResults(self):
def has_results(self):
return len(self.outputObjects) > 0
@ -190,10 +212,10 @@ class ScriptExecutor(object):
executes a script in a given environment.
"""
def __init__(self, environment, astTree):
def __init__(self, environment, ast_tree):
try:
exec (astTree) in environment
exec ast_tree in environment
except Exception, ex:
# an error here means there was a problem compiling the script
@ -219,6 +241,10 @@ class InvalidParameterError(Exception):
pass
class NoOutputError(Exception):
pass
class ScriptExecutionError(Exception):
"""
Represents a script syntax error.
@ -258,6 +284,10 @@ class EnvironmentBuilder(object):
self.env['__builtins__'] = env_dict
return self
def with_cadquery_objects(self):
self.env['cadquery'] = cadquery
return self
def add_entry(self, name, value):
self.env[name] = value
return self

View File

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

View File

@ -92,7 +92,7 @@ class TestCQGI(BaseTest):
build_object(h)
"""
)
result = self._executeScriptWithParams(script, {'h': 33.33})
result = cqgi.execute(script, {'h': 33.33})
self.assertEquals(result.results[0], "33.33")
def test_that_assigning_string_to_number_fails(self):
@ -102,20 +102,37 @@ class TestCQGI(BaseTest):
build_object(h)
"""
)
with self.assertRaises(Exception):
result = self._executeScriptWithParams(script, {'h': "a string"})
result = cqgi.execute(script, {'h': "a string"})
self.assertTrue(isinstance(result.exception, cqgi.InvalidParameterError))
def test_that_assigning_unknown_var_fails(self):
script = textwrap.dedent(
"""
"""
h = 20.0
build_object(h)
"""
)
with self.assertRaises(cqgi.InvalidParameterError):
result = self._executeScriptWithParams(script, {'w': "var is not there"})
def _executeScriptWithParams(self, script, params):
model = cqgi.CQModel(script)
return model.build(params)
result = cqgi.execute(script, {'w': "var is not there"})
self.assertTrue(isinstance(result.exception, cqgi.InvalidParameterError))
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)