[issue47030] singledispatch does not work with positional arguments with default values.

2022-03-15 Thread Randolf Scholz


New submission from Randolf Scholz :

from functools import singledispatch
from typing import Optional

@singledispatch
def load(key: Optional[str] = None, /) -> None:
raise NotImplementedError

@load.register
def _(key: None, /) -> None:
print(f"loaded {key=}") 

@load.register
def _(key: str, /) -> None:
print(f"loaded {key=}")

load()  # TypeError: load requires at least 1 positional argument

--
messages: 415274
nosy: randolf.scholz
priority: normal
severity: normal
status: open
title: singledispatch does not work with positional arguments with default 
values.
type: enhancement
versions: Python 3.10, Python 3.9

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



[issue45356] Calling `help` executes @classmethod @property decorated methods

2021-10-12 Thread Randolf Scholz


Randolf Scholz  added the comment:

@Alex Regarding my proposal, the crucial part is the desiderata, not the hacked 
up implementation I presented.

And I really believe that at least that part I got hopefully right. After all, 
what does `@classmethod` functionally do? It makes the first argument of the 
function receive the class type instead of the object instance and makes it 
possible to call it directly from the class without instantiating it. I would 
still expect `MyClass.__dict__["themethod"]` to behave, as an object, much the 
same, regardless if it was decorated with `@classmethod` or not.

Regarding code breakage - yes that is a problem, but code is already broken and 
imo Python needs to make a big decision going forward: 

1. Embrace decorator chaining and try hard to make it work universally (which 
afaik was never intended originally when decorators were first introduced). As 
a mathematician I would love this, also adding `@` as a general purpose 
function composition operator would add quite some useful functional 
programming aspects to python.
2. Revert changes from 3.9 and generally discourage decorator chaining.

At this point however I want to emphasize that I am neither a CS major nor a 
python expert (I only started working with the language 3 years ago), so take 
everything I say as what it is - the opinion of some random stranger from the 
internet (:

--

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



[issue45356] Calling `help` executes @classmethod @property decorated methods

2021-10-11 Thread Randolf Scholz

Randolf Scholz  added the comment:

Dear Raymond,

I think decorator chaining is a very useful concept. At the very least, if all 
decorators are functional (following the `functools.wraps` recipe), things work 
out great -- we even get associativity of function composition if things are 
done properly!

The question is: can we get similar behaviour when allowing decoration with 
stateful objects, i.e. classes? This seems a lot more difficult. At the very 
least, in the case of `@classmethod` I think one can formulate a 
straightforward desiderata:

### Expectation

- `classmethod(property)` should still be a `property`!
- More generally: `classmethod(decorated)` should always be a subclass of 
`decorated`!

Using your pure-python versions of property / classmethod from 
<https://docs.python.org/3.9/howto/descriptor.html>, I was able to write down a 
variant of `classmethod` that works mostly as expected in conjunction with 
`property`. The main idea is rewrite the `classmethod` to dynamically be a 
subclass of whatever it wrapped; roughly:

```python
def ClassMethod(func):
  class Wrapped(type(func)):
  def __get__(self, obj, cls=None):
  if cls is None:
  cls = type(obj)
  if hasattr(type(self.__func__), '__get__'):
  return self.__func__.__get__(cls)
  return MethodType(self.__func__, cls)
  return Wrapped(func)
```

I attached a full MWE. Unfortunately, this doesn't fix the `help` bug, though 
it is kind of weird because the decorated class-property now correctly shows up 
under the "Readonly properties" section. Maybe `help` internally checks 
`isinstance(cls.func, property)` at some point instead of 
`isinstance(cls.__dict__["func"], property)`?

### Some further Proposals / Ideas

1. Decorators could always have an attribute that points to whatever object 
they wrapped. For the sake of argument, let's take `__func__`.
   ⟹ raise Error when typing `@decorator` if `not hasattr(decorated, 
"__func__")`
   ⟹ Regular functions/methods should probably by default have `__func__` as a 
pointer to themselves?
   ⟹ This could hae several subsidiary benefits, for example, currently, how 
would you implement a pair of decorators `@print_args_and_kwargs` and 
`@time_execution` such that both of them only apply to the base function, no 
matter the order in which they are decorating it? The proposed `__func__` 
convention would make this very easy, almost trivial.
2. `type.__setattr__` could support checking if `attr` already exists and has 
`__set__` implemented.
  ⟹ This would allow true class-properties with `getter`, `setter` and 
`deleter`. I provide a MWE here: 
<https://mail.google.com/mail/u/0/#label/Python+Ideas/FMfcgzGlkPRbJVRkHHtkRPhMCxNsFHpl>
3. I think an argument can be made that it would be really, really cool if `@` 
could become a general purpose function composition operator?
  ⟹ This is already kind of what it is doing with decorators
  ⟹ This is already exacltly what it is doing in numpy -- matrix multiplication 
\*is\* the composition of linear functions.
  ⟹ In fact this is a frequently requested feature on python-ideas!
  ⟹ But here is probably the wrong place to discuss this.

--
Added file: https://bugs.python.org/file50344/ClassPropertyIdea.py

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



[issue45356] Calling `help` executes @classmethod @property decorated methods

2021-10-10 Thread Randolf Scholz


Randolf Scholz  added the comment:

If fact, in the current state it seem that it is impossible to implement real 
class-properties, for a simple reason: 

descriptor.__set__ is only called when setting the attribute of an instance, 
but not of a class!!



```python
import math

class TrigConst: 
const = math.pi
def __get__(self, obj, objtype=None):
print("__get__ called")
return self.const

def __set__(self, obj, value):
print("__set__ called")
self.const = value


class Trig:
const = TrigConst()  # Descriptor instance
```

```python
Trig().const # calls TrigConst.__get__
Trig().const = math.tau  # calls TrigConst.__set__
Trig.const   # calls TrigConst.__get__
Trig.const = math.pi # overwrites TrigConst attribute with float.
```

--

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



[issue45356] Calling `help` executes @classmethod @property decorated methods

2021-10-10 Thread Randolf Scholz


Randolf Scholz  added the comment:

@Terry I think the problem boils down to the fact that `@classmethod @property` 
decorated methods end up not being real properties.

Calling `MyBaseClass.__dict__` reveals:

```python
mappingproxy({'__module__': '__main__',
  'expensive_class_property': ,
  'expensive_instance_property': ,
  '__dict__': ,
  '__weakref__': ,
  '__doc__': None,
  '__abstractmethods__': frozenset(),
  '_abc_impl': <_abc._abc_data at 0x7f893fb98740>})
```

Two main issues:

1. Any analytics or documentation tool that has special treatment for 
properties may not identify 'expensive_class_property' as a property if they 
simply check via `isinstance(func, property)`. Of course, this could be fixed 
by the tool-makers by doing a conditional check:

```python
isinstance(func, property) or (`isinstance(func, classmethod)` and 
`isinstance(func.__func__, property)`
```

2. `expensive_class_property` does not implement `getter`, `setter`, `deleter`. 
This seems to be the real dealbreaker, for example, if we do

```python
MyBaseClass.expensive_class_property = 2
MyBaseClass().expensive_instance_property = 2
```

Then the first line erroneously executes, such that 
MyBaseClass.__dict__['expensive_class_property'] is now `int` instead of 
`classmethod`, while the second line correctly raises `AttributeError: can't 
set attribute` since the setter method is not implemented.

--

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



[issue45356] Calling `help` executes @classmethod @property decorated methods

2021-10-03 Thread Randolf Scholz


Randolf Scholz  added the comment:

I updated the script with dome more info. The class-property gets actually 
executed 5 times when calling `help(MyClass)`

```
Computing class property of  ...DONE!
Computing class property of  ...DONE!
Computing class property of  ...DONE!
Computing class property of  ...DONE!
Computing class property of  ...DONE!
```

--
Added file: https://bugs.python.org/file50326/classmethod_property.py

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



[issue45356] Calling `help` executes @classmethod @property decorated methods

2021-10-03 Thread Randolf Scholz


New submission from Randolf Scholz :

I noticed some strange behaviour when calling `help` on a class inheriting from 
a class or having itself @classmethod @property decorated methods.

```python
from time import sleep
from abc import ABC, ABCMeta, abstractmethod

class MyMetaClass(ABCMeta):
@classmethod
@property
def expensive_metaclass_property(cls):
"""This may take a while to compute!"""
print("computing metaclass property"); sleep(3)
return "Phew, that was a lot of work!"


class MyBaseClass(ABC, metaclass=MyMetaClass):
@classmethod
@property
def expensive_class_property(cls):
"""This may take a while to compute!"""
print("computing class property .."); sleep(3)
return "Phew, that was a lot of work!"

@property
def expensive_instance_property(self):
"""This may take a while to compute!"""
print("computing instance property ..."); sleep(3)
return "Phew, that was a lot of work!"

class MyClass(MyBaseClass):
"""Some subclass of MyBaseClass"""

help(MyClass)
```

Calling `help(MyClass)` will cause `expensive_class_property` to be executed 4 
times (!)

The other two properties, `expensive_instance_property` and 
`expensive_metaclass_property` are not executed.

Secondly, only `expensive_instance_property` is listed as a read-only property; 
`expensive_class_property` is listed as a classmethod and 
`expensive_metaclass_property` is unlisted.

The problem is also present in '3.10.0rc2 (default, Sep 28 2021, 17:57:14) [GCC 
10.2.1 20210110]'

Stack Overflow thread: https://stackoverflow.com/questions/69426309

--
files: classmethod_property.py
messages: 403109
nosy: randolf.scholz
priority: normal
severity: normal
status: open
title: Calling `help` executes @classmethod @property decorated methods
type: behavior
versions: Python 3.10, Python 3.9
Added file: https://bugs.python.org/file50325/classmethod_property.py

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