Package: codequery
codequeryA library to make it easy to write code queries, asserts on code
facts and design tests for Python code. It provides a simple
wrapper object with an easiear, more readable API to write
queries on ast nodes as well as the tokenizer standard libraries.BEWARE This is just a proof of concept and should be used with
care in any professional context.  My personal and primary use
for this package is to check students code in the context of an
introductory programming course. While automated tests do
provide good feedback and guidance wrt to functional aspects of
the code (whether it does what it is expected to do), they do
not provide any feedback wrt to how the code has been written
(and, in particular, wrt the way it was NOT expected to be). I
usecodequeryto write easy to read and understand design
tests that the students use to restrict the solution space,
while still using a conventional testing framework and tooling
(namely, pytest and unittests).how to installpip3 install codequeryhow to useTBD. However, the following examples should make it clear for
simple cases. Soon I will add here a minimal documentation for
each query method available in the API.examplesThe test below is used in a programming problem in which the
student is asked to implement an in-place bubble sort. It
prevents the module under test from: i) importing any modules;
ii) using thesort()method and thesorted()function (python
built-in functions); and iii) using methods that change lists in
structural way, to assure the algorithm is in-place (this forces
the programmer to manipulate the list as if it was a simple
array, namely, using only item assignments). Thecalls()method
receives a function (or class) name and returns a boolean stating
whether that function is called or not from that module.fromcodequeryimportModulemod=Module(filename="answer.py")deftest_is_inplace():assertnotmod.importedassertnotmod.calls("sorted")assertnotmod.calls(".sort")assertnotmod.calls(".append")assertnotmod.calls(".pop")assertnotmod.calls(".insert")The test below could be used to complement a query-sort
implementation, to require that a recursive solution be written.
Observe theselect()function being used. It receives either an
ast node type (or a string that matches the type name) and returns
all the elements in the tree rooted at the given node (in this
case, the whole modulemod). I'll assume the assert line is
self-explanatory (thecall()is the same in the example above).deftest_is_recursive():functions=mod.select("FunctionDef")assertany(f.calls(f)forfinfunctions)The next test checks whether the module implements the pythonmain
patternand that it has amain()function, called from it.deftest_uses_main_section():mainsect=next(eforeinmod.select("If")ife.get("left")=="__name__")mainsect.calls("main")In fact, as selecting the main section is such a common
pattern,codequeryhas a helper method that makes this code
become simpler and more readable. The first line in the example
above could be:mainsect = mod.select("main_section").scriptsTwo scripts that usecodequeryto analyze code scripts are also
provided:code-outlineandcode-profile, which are used to
create code outlines (a listing of the most relevant node
elements that give the code its outline structure) and code
profiles (a vector containing an ordered counting of the most
important elements in the code under analysis).