Namespaces/introspection: collecting sql strings for validation
Peter Otten
__peter__ at web.de
Sun Apr 22 05:14:08 EDT 2007
Martin Drautzburg wrote:
> I would like to validate sql strings, which are spread all over the
> code, i.e. I run ("prepare") them against a database to see if it happy
> with the statements. Spelling errors in sql have been a major pain for
> me.
>
> The statements will not be assembled from smaller pieces, but they will
> not neccessarily be defined at module level. I could live with class
> level, but method level would be best. And I definitely don't want to
> parse the source file, but I am willing to mark the strings so it is
> easier to tell sql from non-sql.
>
> So the code will look like this
>
> class Foo:(...):
> def aQuery(...):
> stmt = """
> -- checkSql
> select 1 from dual
> """
> executeQuery()
>
> at the end of the file I would like to write something like
> if (...):
> validateSql()
>
> The validateSql() is the problem. It would be imported from elsewhere.
> and has to do some serious magic. It would have to lookup its caller's
> module, find all the classes (and methods, if we go that far) extract
> the constants, check if any of them are an SQL statement and validate
> it.
>
> The problem is the first part: how can I lookup the callers module and
> the classobjs defined in there? Or finding any constant strings in the
> caller's module would also be just fine. Or is there a completely
> different way to do such a thing?
Since all strings are constants you could just tokenize the source code:
def strings(filename):
with open(filename, "rU") as instream:
for t in tokenize.generate_tokens(instream.readline):
if t[0] == token.STRING:
yield eval(t[1])
def validateSQL(filename=None):
if filename is None:
# by default operate on the calling module
filename = sys._getframe(1).f_globals["__file__"]
for s in strings(filename):
print "validating", repr(s)
Another option would be to mark SQL statements similar to gettext by
enclosing them in a function call
sql = SQL("select * from...")
and then defining SQL() as either a no-op in production or an actual
validation while you are debugging. Even simpler and safer would be to
always validate once:
def SQL(sql, checked=set()):
if sql in checked:
return True
if not valid_sql(sql): raise ValueError
checked.add(sql)
return sql
Peter
More information about the Python-list
mailing list