Exhaustively walking the AST tree in Groovy - groovy

This is related to my question on intercepting all accesses to a field in a given class, rather than just those done in a manner consistent with Groovy 'property' style accesses. You can view that here: intercepting LOCAL property access in groovy.
One way I've found that will definitely resolve my issue there is to use AST at compile time re-write any non-property accesses with property accesses. For example, a if a class looks like this:
class Foo {
def x = 1
def getter() {
x
}
def getProperty(String name) {
this."$name" ++
}
}
foo = new Foo()
assert foo.getter() == 1
assert foo.x == 2
These assert statements will work out because the getter method access x directly and the foo.x goes through getProperty("x") which increments x before returning.
After some trial and error I can use an AST transformation to change the behavior of the code such that the expression 'x' in the 'getter' method is actually accessed as a Property rather than as a local field. So far so good!
Now, how do I go about getting to ALL accesses of local fields in a given class? I've been combing the internet looking for an AST tree walker helper of some kind but haven't found one. Do I really need to implement an expression walker for all 38 expression types here http://groovy.codehaus.org/api/org/codehaus/groovy/ast/expr/package-summary.html and all 18 statement types here http://groovy.codehaus.org/api/org/codehaus/groovy/ast/stmt/package-summary.html? That seems like something that someone must have already written (since it would be integral to building an AST tree in the first place) but I can't seem to find it.

Glenn
You are looking for some sort of visitor. Groovy has a few (weakly documented) visitors defined that you could use. I don't have the exact answer for your problem, but I can provide you a few directions.
The snippet below shows how to transverse the AST of a class and print all method names:
class TypeSystemUsageVisitor extends ClassCodeVisitorSupport {
#Override
public void visitExpression(MethodNode node) {
super.visitMethod(node)
println node.name
}
#Override
protected SourceUnit getSourceUnit() {
// I don't know ho I should implement this, but it makes no difference
return null;
}
}
And this is how I am using the visitor defined above
def visitor = new TypeSystemUsageVisitor()
def sourceFile = new File("path/to/Class.groovy")
def ast = new AstBuilder().buildFromString(CompilePhase.CONVERSION, false, sourceFile.text).find { it.class == ClassNode.class }
ast.visitContents(visitor)
Visitors take care of transversing the tree for you. They have visit* methods that you can override and do whatever you want with them. I believe the appropriate visitor for your problem is CodeVisitorSupport, which has a visitVariableExpression method.
I recommend you to read the code of the AST Browser that comes along with groovyConsole for more examples on how to use Groovy AST Visitors. Also, take a look at the api doc for CodeVisitorSupport.

Related

Extract type hints for object attributes in Python [duplicate]

I want to get the type hints for an object's attributes. I can only get the hints for the class and not an instance of it.
I have tried using foo_instance.__class__ from here but that only shows the class variables.
So in the example how do I get the type hint of bar?
class foo:
var: int = 42
def __init__(self):
self.bar: int = 2
print(get_type_hints(foo)) # returns {'var': <class 'int'>}
I just had the same problem. The python doc isn't that clear since the example is made with what is now officially called dataclass.
Student(NamedTuple):
name: Annotated[str, 'some marker']
get_type_hints(Student) == {'name': str}
get_type_hints(Student, include_extras=False) == {'name': str}
get_type_hints(Student, include_extras=True) == {
'name': Annotated[str, 'some marker']
}
It give the impression that get_type_hints() works on class directly. Turns out get_type_hints() returns hints based on functions, not on class. That way it can be use with both if we know that. A normal class obviously not being instantiated at it's declaration, it does not have any of the variables set within the __init__() method who hasn't yet been called. It couldn't be that way either if we want the possibility to get the type hints from class-wide variables.
So you could either call it on __init__(), that is if variables are passed in arguments though (yes i seen it's not in your example but might help others since i didn't seen this anywhere in hours of search);
class foo:
var: int = 42
def __init__(self, bar: int = 2):
self.bar = int
print(get_type_hints(foo.__init__))
At last for your exact example i believe you have two choices. You could instantiate a temporary object and use del to clean it right after if your logic allows it. Or declare your variables as class ones with or without default values so you can get them with get_type_hints() and assign them later in instantiations.
Maybe this is a hack, and you have to be the creator of your instances, but there are a subset of cases in which using a data class will get you what you want;
Python 3.7+
#dataclass
class Foo:
bar: str = 2
if __name__ == '__main__':
f = Foo()
print(f.bar)
print(get_type_hints(f))
2
{'bar': <class 'str'>}
Hints only exist at the class level — by the time an instance is created the type of its attributes will be that of whatever value has been assigned to them. You can get the type of any instance attribute by using the first form of the built-in type() function — e.g. type(foo_instance.var).
This information isn't evaluated and only exists in the source code.
if you must get this information, you can use the ast module and extract the information from the source code yourself, if you have access to the source code.
You should also ask yourself if you need this information because in most cases reevaluating the source code will be to much effort.

Patching superclass methods with mocks

There are a number of similar(ish) questions here about how, in Python, you are supposed to patch the superclasses of your class, for testing. I've gleaned some ideas from them, but I'm still not where I need to be.
Imagine I have two base classes:
class Foo(object):
def something(self, a):
return a + 1
class Bar(object):
def mixin(self):
print("Hello!")
Now I define the class that I want to test as such:
class Quux(Foo, Bar):
def something(self, a):
self.mixin()
return super().something(a) + 2
Say I want to test that mixin has been called and I want to replace the return value of the mocked Foo.something, but importantly (and necessarily) I don't want to change any of the control flow or logic in Quux.something. Presuming patching superclasses "just worked", I tried unittest.mock.patch:
with patch("__main__.Foo", spec=True) as mock_foo:
with patch("__main__.Bar", spec=True) as mock_bar:
mock_foo.something.return_value = 123
q = Quux()
assert q.something(0) == 125
mock_bar.mixin.assert_called_once()
This doesn't work: The superclasses' definitions of something and mixin aren't being mocked when Quux is instantiated, which is not unsurprising as the class' inheritance is defined before the patch.
I can get around the mixin problem, at least, by explicitly setting it:
# This works to mock the mixin method
q = Quux()
setattr(q, "mixin", mock_bar.mixin)
However, a similar approach doesn't work for the overridden method, something.
As I mentioned, other answers to this question suggest overriding Quux's __bases__ value with the mocks. However, this doesn't work at all as __bases__ must be a tuple of classes and the mocks' classes appear to just be the originals:
# This doesn't do what I want
Quux.__bases__ = (mock_foo.__class__, mock_bar.__class__)
q = Quux()
Other answers suggested overriding super. This does work, but I feel that it's a bit dangerous as any calls to super you don't want to patch will probably break things horribly.
So is there a better way of doing what I want than this:
with patch("builtins.super") as mock_super:
mock_foo = MagicMock(spec=Foo)
mock_foo.something.return_value = 123
mock_super.return_value = mock_foo
mock_bar = MagicMock(spec=Bar)
q = Quux()
setattr(q, "mixin", mock_bar.mixin)
assert q.something(0) == 125
mock_bar.mixin.assert_called_once()
The matter is actually simple -
the subclass will contain a reference to the original classes
inside its own structure (the public visible attributes __bases__ and __mro__). That reference is not changed when you mock those base classes -
the mocking would only affect one using those objects explicitly, while the patching is "turned on". In other words, they would only be used if your Quux class would itself be defined inside the with blocks. And that would not work either, as the "mock" object replacing the classes can not be a proper superclass.
However, the workaround, and the right way to do it are quite simple - you just have to mock the methods you want replaced, not the classes.
The question is a bit old now, and I hope you had moved on, but the right thing to do there is:
with patch("__main__.Foo.something", spec=True) as mock_foo:
with patch("__main__.Bar.mixin", spec=True) as mock_bar:
mock_foo.return_value = 123
q = Quux()
assert q.something(0) == 125
mock_bar.assert_called_once()

Having trouble returning through multiple classes in Python

I'm still learning and like to build things that I will eventually be doing on a regular basis in the future, to give me a better understanding on how x does this or y does that.
I haven't learned much about how classes work entirely yet, but I set up a call that will go through multiple classes.
getattr(monster, monster_class.str().lower())(1)
Which calls this:
class monster:
def vampire(x):
monster_loot = {'Gold':75, 'Sword':50.3, 'Good Sword':40.5, 'Blood':100.0, 'Ore':.05}
if x == 1:
loot_table.all_loot(monster_loot)
Which in turn calls this...
class loot_table:
def all_loot(monster_loot):
loot = ['Gold', 'Sword', 'Good Sword', 'Ore']
loot_dropped = {}
for i in monster_loot:
if i in loot:
loot_dropped[i] = monster_loot[i]
drop_chance.chance(loot_dropped)
And then, finally, gets to the last class.
class drop_chance:
def chance(loot_list):
loot_gained = []
for i in loot_list:
x = random.uniform(0.0,100.0)
if loot_list[i] >= x:
loot_gained.append(i)
return loot_gained
And it all works, except it's not returning loot_gained. I'm assuming it's just being returned to the loot_table class and I have no idea how to bypass it all the way back down to the first line posted. Could I get some insight?
Keep using return.
def foo():
return bar()
def bar():
return baz()
def baz():
return 42
print foo()
I haven't learned much about how classes work entirely yet...
Rather informally, a class definition is a description of the object of that class (a.k.a. instance of the class) that is to be created in future. The class definition contains the code (definitions of the methods). The object (the class instance) basically contains the data. The method is a kind of function that can take arguments and that is capable to manipulate the object's data.
This way, classes should represent the behaviour of the real-world objects, the class instances simulate existence of the real-world objects. The methods represent actions that the object apply on themselves.
From that point of view, a class identifier should be a noun that describes category of objects of the class. A class instance identifier should also be a noun that names the object. A method identifier is usually a verb that describes the action.
In your case, at least the class drop_chance: is suspicious at least because of naming it this way.
If you want to print something reasonable about the object--say using the print(monster)--then define the __str__() method of the class -- see the doc.

Groovy 'as' keyword to implement 2+ interfaces

I would like to implement a Glazed List that has both an AdvancedTableFormat and WritableTableFormat interface.
I am reading here:
http://www.jroller.com/aalmiray/entry/glazedlists_groovy_not_your_regular
and for one interface it seems this is possible in Groovy with the "as" keyword:
# return new EventTableModel(linksList, [
# getColumnCount: {columnNames.size()},
# getColumnName: {index -> columnNames[index]},
# getColumnValue: {object, index ->
# object."${columnNames[index].toLowerCase()}"
# }] as TableFormat)
Is it somehow possible to do this for two interfaces? If so how?
Thank you!
Misha
You can create a new interface that extends the two interfaces you are interested in.
interface PersonalizedTableFormat extends AdvancedTableFormat, WriteableTableFormat {
}
You can cast the object you return to the new interface.
return object as PersonalizedTableFormat;
The "as" keyword is just a fancy way of invoking Groovy's asType(Class) method, which takes only a single Class as an argument. Therefore you can't directly use "as" with more than one interface (unless you take frm's approach and combine the interfaces in one super interface).
I wonder if you want to implement 2 or more interfaces on the fly, and do not prefer to 'hard code' like
interface abc extends aaa,bbb,ccc {}?
May be you can try the following code:
import static java.util.Collections.sort
def doClose(Closeable c){
c.close();
}
def o = new Object()
o.metaClass{
compare{Object a,String b-> return a.size() - b.size()};
close{println 'Lights out - I am closing'};
}
o = new ProxyGenerator().instantiateDelegate([Comparator, Closeable], o)
def items = ['a', 'bbb', 'cc']
sort(items, o);
println items;
doClose(o);
println o.class.getInterfaces();

Groovy way to dynamically invoke a static method

I know in Groovy you can invoke a method on a class/object using a string. For example:
Foo."get"(1)
/* or */
String meth = "get"
Foo."$meth"(1)
Is there a way to do this with the class? I have the name of the class as a string and would like to be able to dynamically invoke that class. For example, looking to do something like:
String clazz = "Foo"
"$clazz".get(1)
I think I'm missing something really obvious, just am not able to figure it out.
As suggested by Guillaume Laforge on Groovy ML,
("Foo" as Class).get(i)
would give the same result.
I've tested with this code:
def name = "java.lang.Integer"
def s = ("$name" as Class).parseInt("10")
println s
Try this:
def cl = Class.forName("org.package.Foo")
cl.get(1)
A little bit longer but should work.
If you want to create "switch"-like code for static methods, I suggest to instantiate the classes (even if they have only static methods) and save the instances in a map. You can then use
map[name].get(1)
to select one of them.
[EDIT] "$name" is a GString and as such a valid statement. "$name".foo() means "call the method foo() of the class GString.
[EDIT2] When using a web container (like Grails), you have to specify the classloader. There are two options:
Class.forName("com.acme.MyClass", true, Thread.currentThread().contextClassLoader)
or
Class.forName("com.acme.MyClass", true, getClass().classLoader)
The first option will work only in a web context, the second approach also works for unit tests. It depends on the fact that you can usually use the same classloader as the class which invokes forName().
If you have problems, then use the first option and set the contextClassLoader in your unit test:
def orig = Thread.currentThread().contextClassLoader
try {
Thread.currentThread().contextClassLoader = getClass().classLoader
... test ...
} finally {
Thread.currentThread().contextClassLoader = orig
}
An augmentation to Chanwit's answer illustrating creation of an instance:
def dateClass = 'java.util.Date' as Class
def date = dateClass.newInstance()
println date
Here's another way
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def target = application.domainClasses.find{it.name == 'ClassName'}
target.clazz.invokeMethod("Method",args)
With this you don't need to specify the package name. Be careful though if you have the same class name in two different packages.
Melix on Groovy ML pointed me in the "right" direction on dynamic class method invokation awhile back, quite useful:
// define in script (not object) scope
def loader = this.getClass().getClassLoader()
// place this in some MetaUtils class, invoked on app startup
String.metaClass.toClass = {
def classPath = getPath(delegate) // your method logic to determine 'path.to.class'
Class.forName(classPath, true, this.loader)
}
// then, anywhere in your app
"Foo".toClass().bar()
You could create another string metaClass method to create instances as well, refactoring as appropriate:
String.metaClass.toObject = {
def classPath = getPath(delegate)
Class.forName(classPath, true, this.loader).newInstance()
}
Groovy is pure fun ;--)
I'm running version 1.8.8 groovy... and the simple example works.
Import my.Foo
def myFx="myMethodToCall"
def myArg = 12
Foo."$myFx"(myArg)
Calls Foo.myMethodToCall(12) as expected and desired. I don't know if this has always been the case though.

Resources