diff --git a/cadquery/__init__.py b/cadquery/__init__.py
index fb0bea01..22a4970d 100644
--- a/cadquery/__init__.py
+++ b/cadquery/__init__.py
@@ -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"
diff --git a/cadquery/cq_directive.py b/cadquery/cq_directive.py
index d898d27a..958a3dd2 100644
--- a/cadquery/cq_directive.py
+++ b/cadquery/cq_directive.py
@@ -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
-
- %(outSVG)s
+
+ %(out_svg)s
@@ -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)
-
-
diff --git a/cadquery/cqgi.py b/cadquery/cqgi.py
index fd0b6b67..fda972ee 100644
--- a/cadquery/cqgi.py
+++ b/cadquery/cqgi.py
@@ -7,10 +7,30 @@ import ast
import traceback
import re
import time
+import cadquery
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
diff --git a/doc/examples.rst b/doc/examples.rst
index 2f978257..c2a4f38a 100644
--- a/doc/examples.rst
+++ b/doc/examples.rst
@@ -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
---------------------
diff --git a/tests/TestCQGI.py b/tests/TestCQGI.py
index 925e7c4c..6b213c7a 100644
--- a/tests/TestCQGI.py
+++ b/tests/TestCQGI.py
@@ -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)