diff -r 937b4376e4b7 Lib/test/test_functools.py --- a/Lib/test/test_functools.py Sat Mar 31 13:39:42 2012 -0700 +++ b/Lib/test/test_functools.py Sat Mar 31 20:59:36 2012 -0600 @@ -144,6 +144,15 @@ join = self.thetype(''.join) self.assertEqual(join(data), '0123456789') + def test_attribute_assignment(self): + f = self.thetype(int, base=2) + doc = 'Convert base 2 string to an int.' + f.__doc__ = doc + self.assertEqual(f.__doc__, doc) + val = 'bar' + f.foo = val + self.assertEqual(f.foo, val) + class PartialSubclass(functools.partial): pass diff -r 937b4376e4b7 src/org/python/modules/_functools/PyPartial.java --- a/src/org/python/modules/_functools/PyPartial.java Sat Mar 31 13:39:42 2012 -0700 +++ b/src/org/python/modules/_functools/PyPartial.java Sat Mar 31 20:59:36 2012 -0600 @@ -10,6 +10,7 @@ import org.python.core.PyStringMap; import org.python.core.PyTuple; import org.python.core.PyType; +import org.python.expose.ExposedDelete; import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; @@ -122,6 +123,16 @@ return func.__call__(argAppl, kwAppl); } + @ExposedSet(name = "func") + public void setFunc(PyObject value) { + throw Py.TypeError("readonly attribute"); + } + + @ExposedDelete(name = "func") + public void delFunc() { + throw Py.TypeError("readonly attribute"); + } + @ExposedGet(name = "args") public PyObject getArgs() { PyObject[] justArgs; @@ -135,6 +146,16 @@ return new PyTuple(justArgs); } + @ExposedSet(name = "args") + public void setArgs(PyObject value) { + throw Py.TypeError("readonly attribute"); + } + + @ExposedDelete(name = "args") + public void delArgs() { + throw Py.TypeError("readonly attribute"); + } + @ExposedGet(name = "keywords") public PyObject getKeywords() { if (keywords.length == 0) { @@ -148,6 +169,22 @@ return kwDict; } + @ExposedSet(name = "keywords") + public void setKeywords(PyObject value) { + throw Py.TypeError("readonly attribute"); + } + + @ExposedDelete(name = "keywords") + public void delKeywords() { + throw Py.TypeError("readonly attribute"); + } + + @Override + public PyObject fastGetDict() { + ensureDict(); + return __dict__; + } + @Override @ExposedGet(name = "__dict__") public PyObject getDict() { @@ -164,6 +201,12 @@ __dict__ = val; } + @Override + @ExposedDelete(name = "__dict__") + public void delDict() { + throw Py.TypeError("a partial object's dictionary may not be deleted"); + } + private void ensureDict() { if (__dict__ == null) { __dict__ = new PyStringMap();