got first directive workign
This commit is contained in:
parent
4e2168cad6
commit
190980d4a1
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
---------------------
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user