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