[issue4331] Add functools.partialmethod

2013-11-04 Thread Nick Coghlan

Nick Coghlan added the comment:

Indeed, added to __all__ in http://hg.python.org/cpython/rev/ac1685661b07

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-11-03 Thread Vajrasky Kok

Vajrasky Kok added the comment:

Should we add partialmethod to __all__ for consistency?

--
nosy: +vajrasky

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-11-02 Thread Nick Coghlan

Changes by Nick Coghlan :


--
resolution:  -> fixed
stage: commit review -> committed/rejected
status: open -> closed

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-11-02 Thread Roundup Robot

Roundup Robot added the comment:

New changeset 46d3c5539981 by Nick Coghlan in branch 'default':
Issue #4331: Added functools.partialmethod
http://hg.python.org/cpython/rev/46d3c5539981

--
nosy: +python-dev

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-31 Thread Nick Coghlan

Nick Coghlan added the comment:

Updated patch based on Alon's last patch.

The major functional change is to ensure __self__ is set appropriately on any 
bound methods returned by the descriptor.

I also updated the docs and docstring, and added a What's New entry (as well as 
rewording the existing entry for functools.singledispatch)

There were a few other cosmetic changes, with the most noticeable being moving 
the partialmethod implementation and tests adjacent to the existing ones for 
functools.partial.

Assuming nobody pokes any significant holes in this idea and implementation in 
the meantime, I'll commit this before beta 1.

--
Added file: http://bugs.python.org/file32436/issue4331_partialmethod.diff

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-31 Thread Matt Joiner

Changes by Matt Joiner :


--
nosy:  -anacrolix

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-31 Thread Nick Coghlan

Changes by Nick Coghlan :


--
stage: needs patch -> commit review

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-29 Thread alon horev

alon horev added the comment:

I've changed the test according to the code review. Thanks

--
Added file: http://bugs.python.org/file32409/4331.v2.patch

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-29 Thread Nick Coghlan

Changes by Nick Coghlan :


--
assignee:  -> ncoghlan

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-28 Thread alon horev

alon horev added the comment:

Adding a patch with tests and documentation. Please feel free to comment on 
anything: my English, coding/testing style. 

I'm pretty sure the documentation can be better but it turns out I speak better 
Python than English.

Two decisions I've made and unsure of:
1. I didn't use @wraps or copied attributes from the wrapped function  
(__doc__, __dict__) to the partialmethod object. This is intentionally so 
partial and partialmethod would have similar semantics.
2. I've implemented a __repr__ although in all cases __get__ returns a partial 
object or bound method. I consider it nice for debugging when looking at an 
object's __dict__.

--
Added file: http://bugs.python.org/file32405/4331.v1.patch

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-27 Thread Nick Coghlan

Nick Coghlan added the comment:

On 27 Oct 2013 22:17, "alon horev"  wrote:
>
>
> alon horev added the comment:
>
> I think the following test demonstrates the API we're looking for.
> 1. Am I missing any functionality?

The keyword arg handling is backwards for unbound methods (the call time
kwds should override the preset ones).

Otherwise, looks good to me.

> 2. How does partialmethod relate to @absolutemethod?

Do you mean @abstractmethod?

We need a __isabstractmethod__ property implementation that delegates the
question to the underlying descriptor.

See http://docs.python.org/dev/library/abc#abc.abstractmethod for details.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-27 Thread alon horev

alon horev added the comment:

I think the following test demonstrates the API we're looking for.
1. Am I missing any functionality?
2. How does partialmethod relate to @absolutemethod?

from functools import partial

class partialmethod(object):

def __init__(self, func, *args, **keywords):
# func could be a descriptor like classmethod which isn't callable,
# so we can't inherit from partial (it verifies func is callable)
if isinstance(func, partialmethod):
# flattening is mandatory in order to place cls/self before all 
other arguments
# it's also more efficient since only one function will be called
self.func = func.func
self.args = func.args + args
self.keywords = {}
self.keywords.update(func.keywords)
self.keywords.update(keywords)
else:
self.func = func
self.args = args
self.keywords = keywords

def _make_unbound_method(self):
def _method(*args, **keywords):
keywords.update(self.keywords)
cls_or_self, *rest = args
call_args = (cls_or_self,) + self.args + tuple(rest)
return self.func(*call_args, **keywords)
return _method

def __get__(self, obj, cls):
get = getattr(self.func, "__get__", None)
if get is None:
if obj is None:
return self._make_unbound_method()
return partial(self._make_unbound_method(), obj) # returns a bound 
method
else:
callable = get(obj, cls)
if callable is self.func:
return self._make_unbound_method()
return partial(callable, *self.args, **self.keywords)

class A(object):
def add(self, x, y):
return self, x + y

add10 = partialmethod(add, 10)
add10class = partialmethod(classmethod(add), 10)
add10static = partialmethod(staticmethod(add), 'self', 10)

return15 = partialmethod(add10, 5) # nested partialmethod

add2partial = partial(add, x=2)
return12 = partialmethod(add2partial, y=10) # partialmethod over partial

def test():
cls = A
instance = cls()

assert instance.add10class(5) == (cls, 15)
assert cls.add10class(5) == (cls, 15)

assert instance.add10static(5) == ('self', 15)
assert cls.add10static(5) == ('self', 15)

assert instance.add10(5) == (instance, 15)
assert cls.add10(instance, 5) == (instance, 15)

assert instance.return15() == (instance, 15)
assert cls.return15(instance) == (instance, 15)

assert instance.return12() == (instance, 12)
assert cls.return12(instance) == (instance, 12)

test()

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-26 Thread Nick Coghlan

Nick Coghlan added the comment:

I like your suggestion of not providing __call__(), as I don't see a way to 
make it work with arbitrary underlying descriptors, and neither classmethod nor 
staticmethod is callable.

In terms of usage, I think this approach will be OK, as in practice I expect 
@classmethod, etc, will be applied to the initial method definition as 
appropriate, so they won't need to be inline except in test cases like these.

Additional test cases needed:

A().add10class(5)
A().add10static(5)
A.add10(A(), 5)

All three should produce 15 as the result, and print the same thing as the 
following:

A.add10class(5)
A.add10static(5)
A().add10(5)

A test method that uses a partial instance as the callable would also be 
helpful in assuring the edge cases are covered.

I believe it may be necessary to have a __get__ method something like:

def _make_unbound_method(self, cls):
def _unbound_method(*args, **kwds):
call_keywords = self.keywords.copy()
call_keywords.update(keywords)
return self.func(*(self.args + args), **call_keywords)
_unbound_method.__objclass__ = cls
return _unbound_method


def __get__(self, obj, cls):
get = getattr(self.func, "__get__")
if get is None:
if obj is None:
return self._make_unbound_method(cls)
callable = self.func
else:
callable = get(obj, cls)
if callable is self.func:
return self._make_unbound_method(cls)
return partial(callable, *self.args, **self.keywords)

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-26 Thread alon horev

alon horev added the comment:

Here's another attempt at a consistent api with regular methods. 
I'm contemplating whether partialmethod should support __call__. Given the fact 
partial is used to bind positional arguments, it will do the 'wrong' thing when 
calling the partialmethod directly and will shift all positional arguments 
(example at the last line of the snippet).

I also think staticmethod in this context is useless but agree consistency is a 
thing (you could just use partial instead).

If consistency hadn't held us back, the first proposal of partialmethod, 
working both for instances and classes, would have been most elegant.

from functools import partial

class partialmethod(object):

def __init__(self, func, *args, **keywords):
self.func = func
self.args = args
self.keywords = keywords

def __call__(self, *args, **keywords):
call_keywords = {}
call_keywords.update(self.keywords)
call_keywords.update(keywords)
return self.func(*(self.args + args), **call_keywords)

def __get__(self, obj, cls):
return partial(self.func.__get__(obj, cls), *self.args, **self.keywords)

class A(object):
def add(self, x, y):
print(self)
return x + y
add10 = partialmethod(add, 10)
add10class = partialmethod(classmethod(add), 10)
add10static = partialmethod(staticmethod(add), 'self', 10)

assert A().add10(5) == 15 # prints <__main__.A object at 0x1097e1390>
assert A.add10class(5) == 15 # prints 
assert A.add10static(5) == 15 # prints self
assert A.add10(2, 3) == 5 # prints 10 because the first positional argument is 
self..

Once we approve of the API I'll provide a full fledged patch.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-25 Thread Nick Coghlan

Nick Coghlan added the comment:

On 26 Oct 2013 05:28, "alon horev"  wrote:
> Is the first option what you had in mind?

That's actually an interesting question. I was going to say yes, but then I
realised it would be better to just "do the right thing" when the
underlying object was a classmethod descriptor, rather than composing them
the other way around.

That view means we should be delegating __get__ to the underlying
descriptor and responding appropriately to the result. And for __call__ we
then can't play games at all, since what my sketch does would be wrong when
wrapping staticmethod.

We also need to make sure the descriptor does the right thing when
@abstractmethod is involved.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-25 Thread alon horev

alon horev added the comment:

I just want to make sure I understand the semantics concerning class methods, 
the following example demonstrates a usage similar to regular methods as much 
as possible:

class A(object):
def add(self, x, y):
print(self)
return x + y
add10 = partialmethod(add, 10)
add10class = classmethod(partialmethod(add, 10))

assert A().add10(5) == 15 # prints <__main__.A object at 0x1097e1390>
assert A.add10class(5) == 15 # prints 

Another option would be to return a class-bound partial from the __get__ 
method. It's not as consistent as the first example but perhaps nicer:

class A(object):
def add(self, x, y):
print(self)
return x + y
add10 = partialmethod(add, 10)

assert A().add10(5) == 15 # prints <__main__.A object at 0x1097e1390>
assert A.add10(5) == 15 # prints 

Is the first option what you had in mind?

--
nosy: +alonho

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-10-24 Thread Nick Coghlan

Nick Coghlan added the comment:

To clarify the current state of this:

- I'm still in favour of adding this feature for Python 3.4
- a suitable patch is still needed, as the currently attached patches modify 
the existing functools.partial object, rather than adding a separate 
"partialmethod" API
- a Python implementation would be fine

The following prototype should work as a starting point to be elaborated into a 
full patch with docs and tests:

class partialmethod(functools.partial):
def __get__(self, obj, cls):
if obj is None:
return self
return functools.partial(self.func,
 *((obj,) + self.args),
 **self.keywords)
def __call__(*args, **kwds):
self, *args = args
call_kwds = {}
call_kwds.update(self.keywords)
call_kwds.update(kwds)
return self.func(self,
 *(self.args + args),
 **call_kwds)

class C:
def example(self, *args, **kwds):
print(self, args, kwds)
fails = functools.partial(example, 1, 2, 3, x=1)
works = partialmethod(example, 1, 2, 3, x=1)

>>> C().fails()
1 (2, 3) {'x': 1}
>>> C().works()
<__main__.C object at 0x7f91cefeea90> (1, 2, 3) {'x': 1}
>>> C().fails(4, 5, 6)
1 (2, 3, 4, 5, 6) {'x': 1}
>>> C().works(4, 5, 6)
<__main__.C object at 0x7f91cefeea10> (1, 2, 3, 4, 5, 6) {'x': 1}

--
stage:  -> needs patch

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-06-05 Thread Matt Joiner

Matt Joiner added the comment:

This sounds excellent Nick.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-06-05 Thread Nick Coghlan

Nick Coghlan added the comment:

Any associated tests may also want check that wrapping classmethod around a 
partialmethod generates a well behaved class method, and ditto for property.

If singledispath, classmethod, partialmethod and class and instance attribute 
access all work correctly, then we can be absolutely certain the results is 
behaving just like an ordinary function does :)

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue4331] Add functools.partialmethod

2013-06-04 Thread Nick Coghlan

Nick Coghlan added the comment:

I don't believe it is reasonable to change the behaviour of partial at this 
late stage of the game. It's documented as behaving like staticmethod (albeit 
by not implementing the descriptor protocol at all), so that's no longer 
something we can change. If issue 11470 is added, then we'll just implement the 
staticmethod-like behaviour explicitly rather than leaving it as implicit.

More importantly, the acceptance of PEP 443's functools.singledispatch makes it 
more desirable than ever to support partial binding for method implementations 
*even when the descriptor protocol is not involved*.

Accordingly, I suggest converting this proposal to a separate 
functools.partialmethod API that:

1. When called directly, passes the first positional argument as the first 
positional argument of the underlying function (providing call time binding of 
self, just like a normal function)
2. When retrieved from a class, returns itself
3. When retrieved from an instance, returns an appropriate bound method object 
(providing method lookup time binding of self, just like a normal function)

functools.partial will then continue to behave as it always has (thus posing no 
backwards compatibility risks), while the new partialmethod implementation 
should work with both class descriptor protocol based dispatch (through point 
3) *and* the new functools.singledispatch mechanism (through point 1).

--
components: +Library (Lib)
nosy: +ncoghlan
title: Can't use _functools.partial() created function as method -> Add 
functools.partialmethod
versions:  -Python 2.7, Python 3.3

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com