Skip to content

Commit

Permalink
Added support for redefining functions in a module/class.
Browse files Browse the repository at this point in the history
  • Loading branch information
freakboy3742 committed Oct 3, 2016
1 parent b624676 commit c8b9d51
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 26 deletions.
1 change: 0 additions & 1 deletion python/common/org/Python.java
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ public static java.util.Map<java.lang.String, org.python.Object> addToKwargs(jav
return kwargs;
}


@org.python.Method(
__doc__ = "__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module" +
"\n" +
Expand Down
52 changes: 35 additions & 17 deletions python/common/org/python/types/Property.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package org.python.types;

public class Property extends org.python.types.Object {
org.python.Object getter;
org.python.Object setter;
org.python.Object deleter;
org.python.Object docstring;
org.python.Object fget;
org.python.Object fset;
org.python.Object fdel;
org.python.Object doc;

public Property(org.python.Object fget, org.python.Object fset, org.python.Object fdel, org.python.Object doc) {
this.getter = fget;
this.setter = fset;
this.deleter = fdel;
this.docstring = doc;
this.fget = fget;
this.fset = fset;
this.fdel = fdel;
this.doc = doc;
}

@org.python.Method(
Expand All @@ -27,11 +27,11 @@ public org.python.Object __repr__() {
)
public org.python.Object __get__(org.python.Object instance, org.python.Object klass) {
// System.out.println("Property __get__ on " + instance);
if (this.getter != null) {
if (this.fget != null) {
try {
return ((org.python.types.Function) this.getter).invoke(instance, null, null);
return ((org.python.types.Function) this.fget).invoke(instance, null, null);
} catch (java.lang.ClassCastException e) {
throw new org.python.exceptions.TypeError("'" + this.getter.typeName() + "' object is not callable");
throw new org.python.exceptions.TypeError("'" + this.fget.typeName() + "' object is not callable");
}
} else {
throw new org.python.exceptions.AttributeError("can't get attribute");
Expand All @@ -44,11 +44,11 @@ public org.python.Object __get__(org.python.Object instance, org.python.Object k
)
public void __set__(org.python.Object instance, org.python.Object value) {
// System.out.println("Property __set__ on " + instance);
if (this.setter != null) {
if (this.fset != null) {
try {
((org.python.types.Function) this.setter).invoke(instance, new org.python.Object [] { value }, null);
((org.python.types.Function) this.fset).invoke(instance, new org.python.Object [] { value }, null);
} catch (java.lang.ClassCastException e) {
throw new org.python.exceptions.TypeError("'" + this.setter.typeName() + "' object is not callable");
throw new org.python.exceptions.TypeError("'" + this.fset.typeName() + "' object is not callable");
}
} else {
throw new org.python.exceptions.AttributeError("can't set attribute");
Expand All @@ -61,14 +61,32 @@ public void __set__(org.python.Object instance, org.python.Object value) {
)
public void __delete__(org.python.Object instance) {
// System.out.println("Property __delete__ on " + instance);
if (this.deleter != null) {
if (this.fdel != null) {
try {
((org.python.types.Function) this.deleter).invoke(instance, null, null);
((org.python.types.Function) this.fdel).invoke(instance, null, null);
} catch (java.lang.ClassCastException e) {
throw new org.python.exceptions.TypeError("'" + this.deleter.typeName() + "' object is not callable");
throw new org.python.exceptions.TypeError("'" + this.fdel.typeName() + "' object is not callable");
}
} else {
throw new org.python.exceptions.AttributeError("can't delete attribute");
}
}

@org.python.Method(
__doc__ = "",
args = {"fn"}
)
public org.python.Object setter(org.python.Object fn) {
// Duplicate the property, substituting the new setter.
return new org.python.types.Property(this.fget, fn, this.fdel, this.doc);
}

@org.python.Method(
__doc__ = "",
args = {"fn"}
)
public org.python.Object deleter(org.python.Object fn) {
// Duplicate the property, substituting the new deleter.
return new org.python.types.Property(this.fget, this.fset, fn, this.doc);
}
}
21 changes: 21 additions & 0 deletions tests/structures/test_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,24 @@ def second(self):
print(obj.second())
print('Done.')
""")

def test_redefine(self):
self.assertCodeExecution("""
class MyClass:
def __init__(self, val):
print("VAL: ", val)
self.value = val
def stuff(self, delta):
print("DELTA: ", delta)
return self.value + delta
def stuff(self, delta):
print("Redefined DELTA: ", delta)
return self.value + delta * 2
obj = MyClass(4)
obj.stuff(5)
print('Done.')
""", run_in_function=False)
81 changes: 78 additions & 3 deletions tests/structures/test_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,44 @@ def setter(self, value):
self._attr = value * 2
print("Attribute set")
def deleter(self, value):
def deleter(self):
print("Deleting attribute")
self._attr = 42
print("Attribute deleted")
attr = property(getter, setter, deleter)
obj = MyObject()
print("obj.attr =", obj.attr)
obj.attr = 2345
print("obj.attr =", obj.attr)
del obj.attr
print("obj.attr =", obj.attr)
print("Done.")
""")

def test_with_decorators(self):
self.assertCodeExecution("""
class MyObject:
def __init__(self):
self._attr = None
@property
def attr(self):
print("Got attribute")
return self._attr
@attr.setter
def attr(self, value):
print("Setting attribute")
self._attr = value * 2
print("Attribute set")
attr = property(getter)
@attr.deleter
def attr(self):
print("Deleting attribute")
self._attr = 42
print("Attribute deleted")
obj = MyObject()
print("obj.attr =", obj.attr)
Expand All @@ -89,4 +121,47 @@ def deleter(self, value):
del obj.attr
print("obj.attr =", obj.attr)
print("Done.")
""")
""")

def test_with_decorators_misnamed_methods(self):
self.assertCodeExecution("""
class MyObject:
def __init__(self):
self._attr = None
@property
def attr(self):
print("Got attribute")
return self._attr
@attr.setter
def attr1(self, value):
print("Setting attribute")
self._attr = value * 2
print("Attribute set")
@attr.deleter
def attr2(self):
print("Deleting attribute")
self._attr = 42
print("Attribute deleted")
obj = MyObject()
print("obj.attr =", obj.attr)
print("obj.attr1 =", obj.attr1)
print("obj.attr2 =", obj.attr2)
obj.attr1 = 2345
print("obj.attr =", obj.attr)
print("obj.attr1 =", obj.attr1)
print("obj.attr2 =", obj.attr2)
try:
obj.attr2 = 3456
print("Shouldn't be able to set value of attr2")
except AttributeError:
print("Can't set value of attr2")
del obj.attr2
print("obj.attr =", obj.attr)
print("obj.attr1 =", obj.attr1)
print("obj.attr2 =", obj.attr2)
print("Done.")
""")
14 changes: 14 additions & 0 deletions tests/structures/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,17 @@ def myfunc(*args):
print("values count =", myfunc(*values_tuple))
print('Done.')
""", run_in_function=False)

def test_redefine(self):
self.assertCodeExecution("""
def myfunc(value):
print(value * 3)
return value + 5
def myfunc(value):
print(value * 4)
return value + 6
print("value =", myfunc(5))
print('Done.')
""")
3 changes: 2 additions & 1 deletion voc/python/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def __init__(self, parent=None, verbosity=0):
self.parameters = []
self.local_vars = {}
self.deleted_vars = set()
self.symbols = {}

self.generator = None
self.yield_points = []
Expand Down Expand Up @@ -244,7 +245,7 @@ def add_callable(self, function, closure=False):

# Get a Java Method representing the new function
JavaOpcodes.LDC_W(Classref(function.class_descriptor)),
JavaOpcodes.LDC_W(function.name),
JavaOpcodes.LDC_W(function.java_name),

ICONST_val(len(function.parameters)),
JavaOpcodes.ANEWARRAY('java/lang/Class'),
Expand Down
24 changes: 20 additions & 4 deletions voc/python/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ class Function(Block):
def __init__(self, module, name, code, parameters, returns, static=False):
super().__init__(parent=module)
self.name = name

# Python can redefine function symbols. Keep a track of any
# function that is defined; if the symbol has already been
# defined in this context, then append $n to the symbol in
# the Java classfile.
duplicates = self._parent.symbols.setdefault(self.name, [])
duplicates.append(self.method_name)
if len(duplicates) > 1:
self._java_suffix = '$%d' % (len(duplicates) - 1)
else:
self._java_suffix = ''

self.code = code
self.parameters = parameters
self.returns = returns
Expand Down Expand Up @@ -153,6 +165,10 @@ def signature(self):
def method_name(self):
return self.name

@property
def java_name(self):
return self.name + self._java_suffix

@property
def module(self):
return self._parent
Expand Down Expand Up @@ -289,7 +305,7 @@ def method_attributes(self):
def transpile_method(self):
return [
JavaMethod(
self.method_name,
self.java_name,
self.signature,
static=self.static,
attributes=[self.transpile_code()] + self.method_attributes()
Expand Down Expand Up @@ -639,7 +655,7 @@ def transpile_wrapper(self):

wrapper_methods = [
JavaMethod(
self.name,
self.java_name,
self.bound_signature,
attributes=[
JavaCode(
Expand Down Expand Up @@ -693,7 +709,7 @@ def transpile_wrapper(self):

wrapper_methods.append(
JavaMethod(
self.name + "$super",
self.java_name + "$super",
self.bound_signature,
attributes=[
JavaCode(
Expand Down Expand Up @@ -723,7 +739,7 @@ def __repr__(self):
return '<MainFunction %s>' % self.module.name

@property
def method_name(self):
def java_name(self):
return 'main'

@property
Expand Down

0 comments on commit c8b9d51

Please sign in to comment.