[issue36580] unittest.mock does not understand dataclasses

2019-09-09 Thread Karthikeyan Singaravelan


Karthikeyan Singaravelan  added the comment:

Closing this as not a bug since autospeccing with create_autospec can be used 
and spec only does attribute access validation.

Thanks

--
resolution:  -> not a bug
stage:  -> resolved
status: open -> closed

___
Python tracker 

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



[issue36580] unittest.mock does not understand dataclasses

2019-04-11 Thread Karthikeyan Singaravelan


Karthikeyan Singaravelan  added the comment:

Below is a even more simpler reproducer without dataclasses. 'name' is not 
listed as a class attribute in dir(Person) since it's not defined with a value 
but 'age' is with zero. Python seems to not to list something not defined with 
a value in declaration as a class attribute in dir().  Hence 'name' is not 
copied when Person is used as spec. spec only does attribute access validation. 
autospeccing [0] can be used for signature validation. The fields for 
dataclasses are defined in __dataclass_fields__ but I am not sure of special 
casing copying __dataclass_fields__ fields along with dir for dataclasses when 
normal Python doesn't list them as class attributes. If needed I would like 
dir(dataclass) to be changed to include __dataclass_fields__. I would propose 
closing as not a bug.

# ../backups/dataclass_dir.py

from unittest.mock import Mock

class Person:
name: str
age: int = 0

def foo(self):
pass

person_mock = Mock(spec=Person)
print(dir(Person))
print(dir(person_mock))
person_mock.foo
print("age" in dir(person_mock))
print("name" in dir(person_mock))

$ cpython git:(master) ./python.exe ../backups/dataclass_dir.py
['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', 
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', 
'__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', 
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
'__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'foo']

['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', 
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', 
'__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', 
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
'__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 
'assert_any_call', 'assert_called', 'assert_called_once', 
'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 
'assert_not_called', 'attach_mock', 'call_args', 'call_args_list', 
'call_count', 'called', 'configure_mock', 'foo', 'method_calls', 
'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value', 'side_effect']
True
False


[0] https://docs.python.org/3/library/unittest.mock.html#autospeccing

--

___
Python tracker 

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



[issue36580] unittest.mock does not understand dataclasses

2019-04-09 Thread Karthikeyan Singaravelan


Karthikeyan Singaravelan  added the comment:

To add to this mock.Mock also copies dir(spec) but creating an instance from 
mock doesn't copy it where it's not a problem with create_autospec. Mock with 
spec does only attribute validation whereas create_autospec does signature 
validation. There is another open issue to make mock use spec passed as if it's 
autospecced issue30587 where this could be used as a data point to change API. 
I am adding mock devs for confirmation.

--
nosy: +cjw296, mariocj89, michael.foord
versions: +Python 3.8

___
Python tracker 

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



[issue36580] unittest.mock does not understand dataclasses

2019-04-09 Thread Karthikeyan Singaravelan

Karthikeyan Singaravelan  added the comment:

mock.Mock doesn't do signature validation by default for constructor and 
methods. This is expected. create_autospec [0] should be used to make sure the 
signature is validated.'

import dataclasses
import unittest.mock

@dataclasses.dataclass
class Foo:
name: str
baz: float
bar: int = 12

FooMock = unittest.mock.create_autospec(Foo)
fooMock = FooMock()  # Will fail now since it's specced

➜  cpython git:(master) ./python.exe ../backups/bpo36580.py
Traceback (most recent call last):
  File "../backups/bpo36580.py", line 11, in 
fooMock = FooMock()  # should fail: Foo.__init__ takes two arguments
  File 
"/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", 
line 984, in __call__
_mock_self._mock_check_sig(*args, **kwargs)
  File 
"/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", 
line 103, in checksig
sig.bind(*args, **kwargs)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/inspect.py", 
line 3021, in bind
return args[0]._bind(args[1:], kwargs)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/inspect.py", 
line 2936, in _bind
raise TypeError(msg) from None
TypeError: missing a required argument: 'name'

On the other hand 'name' in dir(FooMock) doesn't have the attributes (name and 
baz) present I suppose they are constructed dynamically when an object is 
created from Foo since they are present in dir(Foo()) and mock is not able to 
detect them? mock.create_autospec does an initial pass of dir(Foo) to copy the 
attributes [1] and perhaps it's not able to copy name and bar while baz is 
copied. Below are for FooMock = create_autospec(Foo) . So 'name' in dir(Foo) is 
False for dataclasses. Is this a known behavior?

dir(Foo)

['__annotations__', '__class__', '__dataclass_fields__', 
'__dataclass_params__', '__delattr__', '__dict__', '__dir__', '__doc__', 
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', 
'__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', 
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
'__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar']


dir(Foo(1, 2))

['__annotations__', '__class__', '__dataclass_fields__', 
'__dataclass_params__', '__delattr__', '__dict__', '__dir__', '__doc__', 
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', 
'__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', 
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
'__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'baz', 
'name']

dir(create_autospec(Foo))

['__annotations__', '__class__', '__dataclass_fields__', 
'__dataclass_params__', '__delattr__', '__dict__', '__dir__', '__doc__', 
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', 
'__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', 
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
'__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'assert_any_call', 
'assert_called', 'assert_called_once', 'assert_called_once_with', 
'assert_called_with', 'assert_has_calls', 'assert_not_called', 'attach_mock', 
'bar', 'call_args', 'call_args_list', 'call_count', 'called', 'configure_mock', 
'method_calls', 'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value', 
'side_effect']


print('name' in dir(fooMock)) # False
print('baz' in dir(fooMock)) # False
print('bar' in dir(fooMock)) # True

[0] 
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.create_autospec
[1] 
https://github.com/python/cpython/blob/0e10766574f4e287cd6b5e5860a1ca75488f4119/Lib/unittest/mock.py#L2263

--
nosy: +xtreak

___
Python tracker 

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



[issue36580] unittest.mock does not understand dataclasses

2019-04-09 Thread Eric V. Smith


Eric V. Smith  added the comment:

I'm not sure why dataclasses would be different here:

>>> import dataclasses
>>> import unittest.mock
>>> @dataclasses.dataclass
... class Foo:
... name: str
... baz: float
... bar: int = 12
...
>>> import inspect
>>> inspect.signature(Foo)
 None>
>>>

Foo is just a normal class with a normal __init__.

This is no different than if you don't use dataclasses:

>>> class Bar:
... def __init__(self, name: str, baz: float, bar: int = 12) -> None:
... pass
...
>>> Bar()
Traceback (most recent call last):
  File "", line 1, in 
TypeError: __init__() missing 2 required positional arguments: 'name' and 'baz'
>>> inspect.signature(Bar)
 None>
>>> BarMock = unittest.mock.Mock(Bar)
>>> barMock = BarMock()

--
nosy: +eric.smith

___
Python tracker 

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



[issue36580] unittest.mock does not understand dataclasses

2019-04-09 Thread John Parejko


New submission from John Parejko :

The new dataclasses.dataclass is very useful for describing the properties of a 
class, but it appears that Mocks of such decorated classes do not catch the 
members that are defined in the dataclass. I believe the root cause of this is 
the fact that unittest.mock.Mock generates the attributes of its spec object 
via `dir`, and the non-defaulted dataclass attributes do not appear in dir.

Given the utility in building classes with dataclass, it would be very useful 
if Mocks could see the class attributes of the dataclass.

Example code:

import dataclasses
import unittest.mock

@dataclasses.dataclass
class Foo:
name: str
baz: float
bar: int = 12

FooMock = unittest.mock.Mock(Foo)
fooMock = FooMock()  # should fail: Foo.__init__ takes two arguments
# I would expect these to be True, but they are False
'name' in dir(fooMock)
'baz' in dir(fooMock)
'bar' in dir(fooMock)

--
components: Library (Lib), Tests
messages: 339808
nosy: John Parejko2
priority: normal
severity: normal
status: open
title: unittest.mock does not understand dataclasses
type: behavior
versions: Python 3.7

___
Python tracker 

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