Make render_expression use the stack like mock_engine.

This commit is contained in:
Ryan Leckey
2013-10-23 00:25:40 -07:00
parent 3bba347237
commit 1a76f9868d
2 changed files with 30 additions and 8 deletions

View File

@@ -340,7 +340,7 @@ def mock_engine(engine, stream=None):
six.exec_('del __mock', frame.f_globals, frame.f_locals)
def render_expression(expression, bind, context=None):
def render_expression(expression, bind, stream=None):
"""Generate a SQL expression from the passed python expression.
Only the global variable, `engine`, is available for use in the
@@ -351,15 +351,36 @@ def render_expression(expression, bind, context=None):
blindly pass user input to this function as it uses exec.
:param bind: A SQLAlchemy engine or bind URL.
:param context: Dictionary of local variables for the expression.
:param stream: Render all DDL operations to the stream.
"""
# Create a stream if not present.
if stream is None:
stream = cStringIO()
engine = create_mock_engine(bind, stream)
six.exec_(expression, {'engine': engine}, context)
# Navigate the stack and find the calling frame that allows the
# expression to execuate.
return stream.getvalue()
for frame in inspect.stack()[1:]:
try:
frame = frame[0]
local = dict(frame.f_locals)
local['engine'] = engine
six.exec_(expression, frame.f_globals, local)
break
except:
pass
else:
raise ValueError('Not a valid python expression', engine)
return stream
def render_statement(statement, bind=None):

View File

@@ -102,9 +102,10 @@ class TestRender(TestCase):
assert 'WHERE user.id = 3' in text
def test_render_ddl(self):
context = {'table': self.User.__table__}
expression = 'table.create(engine)'
text = render_expression(expression, self.engine, context)
expression = 'self.User.__table__.create(engine)'
stream = render_expression(expression, self.engine)
text = stream.getvalue()
assert 'CREATE TABLE user' in text
assert 'PRIMARY KEY' in text