Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix test_dataclassess
  • Loading branch information
isidentical committed Oct 1, 2020
commit 246577df8fbe54fe9ddccc3c7243377e9c5b8d5a
6 changes: 4 additions & 2 deletions Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,10 @@ def _create_fn(name, args, body, *, globals=None, locals=None,

ns = {}
exec(txt, globals, ns)
return ns['__create_fn__'](**locals)

func = ns['__create_fn__'](**locals)
for arg, annotation in func.__annotations__.copy().items():
func.__annotations__[arg] = locals[annotation]
Comment thread
isidentical marked this conversation as resolved.
return func

def _field_assign(frozen, name, value, self_name):
# If we're a frozen class, then assign to our fields in __init__
Expand Down
6 changes: 0 additions & 6 deletions Lib/test/dataclass_module_1.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
#from __future__ import annotations
USING_STRINGS = False

# dataclass_module_1.py and dataclass_module_1_str.py are identical
# except only the latter uses string annotations.

import dataclasses
import typing

Expand Down
32 changes: 0 additions & 32 deletions Lib/test/dataclass_module_1_str.py

This file was deleted.

6 changes: 0 additions & 6 deletions Lib/test/dataclass_module_2.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
#from __future__ import annotations
USING_STRINGS = False

# dataclass_module_2.py and dataclass_module_2_str.py are identical
# except only the latter uses string annotations.

from dataclasses import dataclass, InitVar
from typing import ClassVar

Expand Down
32 changes: 0 additions & 32 deletions Lib/test/dataclass_module_2_str.py

This file was deleted.

2 changes: 0 additions & 2 deletions Lib/test/dataclass_textanno.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import dataclasses


Expand Down
94 changes: 34 additions & 60 deletions Lib/test/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import inspect
import builtins
import unittest
from textwrap import dedent
from unittest.mock import Mock
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional
from typing import get_type_hints
Expand Down Expand Up @@ -561,17 +562,17 @@ class C:
self.assertEqual(len(the_fields), 3)

self.assertEqual(the_fields[0].name, 'x')
self.assertEqual(the_fields[0].type, int)
self.assertEqual(the_fields[0].type, 'int')
self.assertFalse(hasattr(C, 'x'))
self.assertTrue (the_fields[0].init)
self.assertTrue (the_fields[0].repr)
self.assertEqual(the_fields[1].name, 'y')
self.assertEqual(the_fields[1].type, str)
self.assertEqual(the_fields[1].type, 'str')
self.assertIsNone(getattr(C, 'y'))
self.assertFalse(the_fields[1].init)
self.assertTrue (the_fields[1].repr)
self.assertEqual(the_fields[2].name, 'z')
self.assertEqual(the_fields[2].type, str)
self.assertEqual(the_fields[2].type, 'str')
self.assertFalse(hasattr(C, 'z'))
self.assertTrue (the_fields[2].init)
self.assertFalse(the_fields[2].repr)
Expand Down Expand Up @@ -757,11 +758,11 @@ class F:
def validate_class(cls):
# First, check __annotations__, even though they're not
# function annotations.
self.assertEqual(cls.__annotations__['i'], int)
self.assertEqual(cls.__annotations__['j'], str)
self.assertEqual(cls.__annotations__['k'], F)
self.assertEqual(cls.__annotations__['l'], float)
self.assertEqual(cls.__annotations__['z'], complex)
self.assertEqual(cls.__annotations__['i'], 'int')
self.assertEqual(cls.__annotations__['j'], 'str')
self.assertEqual(cls.__annotations__['k'], 'F')
self.assertEqual(cls.__annotations__['l'], 'float')
self.assertEqual(cls.__annotations__['z'], 'complex')

# Verify __init__.

Expand All @@ -776,22 +777,22 @@ def validate_class(cls):
self.assertEqual(param.name, 'self')
param = next(params)
self.assertEqual(param.name, 'i')
self.assertIs (param.annotation, int)
self.assertIs (param.annotation, 'int')
self.assertEqual(param.default, inspect.Parameter.empty)
self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
param = next(params)
self.assertEqual(param.name, 'j')
self.assertIs (param.annotation, str)
self.assertIs (param.annotation, 'str')
self.assertEqual(param.default, inspect.Parameter.empty)
self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
param = next(params)
self.assertEqual(param.name, 'k')
self.assertIs (param.annotation, F)
self.assertIs (param.annotation, 'F')
# Don't test for the default, since it's set to MISSING.
self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
param = next(params)
self.assertEqual(param.name, 'l')
self.assertIs (param.annotation, float)
self.assertIs (param.annotation, 'float')
# Don't test for the default, since it's set to MISSING.
self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
self.assertRaises(StopIteration, next, params)
Expand Down Expand Up @@ -1997,15 +1998,15 @@ def test_docstring_one_field(self):
class C:
x: int

self.assertDocStrEqual(C.__doc__, "C(x:int)")
self.assertDocStrEqual(C.__doc__, "C(x:'int')")

def test_docstring_two_fields(self):
@dataclass
class C:
x: int
y: int

self.assertDocStrEqual(C.__doc__, "C(x:int, y:int)")
self.assertDocStrEqual(C.__doc__, "C(x:'int', y:'int')")

def test_docstring_three_fields(self):
@dataclass
Expand All @@ -2014,49 +2015,49 @@ class C:
y: int
z: str

self.assertDocStrEqual(C.__doc__, "C(x:int, y:int, z:str)")
self.assertDocStrEqual(C.__doc__, "C(x:'int', y:'int', z:'str')")

def test_docstring_one_field_with_default(self):
@dataclass
class C:
x: int = 3

self.assertDocStrEqual(C.__doc__, "C(x:int=3)")
self.assertDocStrEqual(C.__doc__, "C(x:'int'=3)")

def test_docstring_one_field_with_default_none(self):
@dataclass
class C:
x: Union[int, type(None)] = None

self.assertDocStrEqual(C.__doc__, "C(x:Optional[int]=None)")
self.assertDocStrEqual(C.__doc__, "C(x:'Union[int, type(None)]'=None)")

def test_docstring_list_field(self):
@dataclass
class C:
x: List[int]

self.assertDocStrEqual(C.__doc__, "C(x:List[int])")
self.assertDocStrEqual(C.__doc__, "C(x:'List[int]')")

def test_docstring_list_field_with_default_factory(self):
@dataclass
class C:
x: List[int] = field(default_factory=list)

self.assertDocStrEqual(C.__doc__, "C(x:List[int]=<factory>)")
self.assertDocStrEqual(C.__doc__, "C(x:'List[int]'=<factory>)")

def test_docstring_deque_field(self):
@dataclass
class C:
x: deque

self.assertDocStrEqual(C.__doc__, "C(x:collections.deque)")
self.assertDocStrEqual(C.__doc__, "C(x:'deque')")

def test_docstring_deque_field_with_default_factory(self):
@dataclass
class C:
x: deque = field(default_factory=deque)

self.assertDocStrEqual(C.__doc__, "C(x:collections.deque=<factory>)")
self.assertDocStrEqual(C.__doc__, "C(x:'deque'=<factory>)")


class TestInit(unittest.TestCase):
Expand Down Expand Up @@ -2805,13 +2806,10 @@ class C:

class TestStringAnnotations(unittest.TestCase):
def test_classvar(self):
# Some expressions recognized as ClassVar really aren't. But
# if you're using string annotations, it's not an exact
# science.
# These tests assume that both "import typing" and "from
# typing import *" have been run in this file.
for typestr in ('ClassVar[int]',
'ClassVar [int]'
'ClassVar [int]',
Comment thread
isidentical marked this conversation as resolved.
' ClassVar [int]',
'ClassVar',
' ClassVar ',
Expand All @@ -2822,17 +2820,9 @@ def test_classvar(self):
'typing. ClassVar[str]',
'typing.ClassVar [str]',
'typing.ClassVar [ str]',

# Not syntactically valid, but these will
# be treated as ClassVars.
'typing.ClassVar.[int]',
'typing.ClassVar+',
Comment thread
isidentical marked this conversation as resolved.
):
with self.subTest(typestr=typestr):
@dataclass
class C:
x: typestr

C = dataclass(type("C", (), {"__annotations__": {"x": typestr}}))
Comment thread
isidentical marked this conversation as resolved.
# x is a ClassVar, so C() takes no args.
C()

Expand All @@ -2853,9 +2843,7 @@ def test_isnt_classvar(self):
'typingxClassVar[str]',
):
with self.subTest(typestr=typestr):
@dataclass
class C:
x: typestr
C = dataclass(type("C", (), {"__annotations__": {"x": typestr}}))

# x is not a ClassVar, so C() takes one arg.
self.assertEqual(C(10).x, 10)
Expand All @@ -2882,9 +2870,8 @@ def test_initvar(self):
'dataclasses.InitVar+',
):
with self.subTest(typestr=typestr):
@dataclass
class C:
x: typestr
C = dataclass(type("C", (), {"__annotations__": {"x": typestr}}))


# x is an InitVar, so doesn't create a member.
with self.assertRaisesRegex(AttributeError,
Expand All @@ -2898,30 +2885,22 @@ def test_isnt_initvar(self):
'typing.xInitVar[int]',
):
with self.subTest(typestr=typestr):
@dataclass
class C:
x: typestr
C = dataclass(type("C", (), {"__annotations__": {"x": typestr}}))

# x is not an InitVar, so there will be a member x.
self.assertEqual(C(10).x, 10)

def test_classvar_module_level_import(self):
from test import dataclass_module_1
from test import dataclass_module_1_str
from test import dataclass_module_2
from test import dataclass_module_2_str

for m in (dataclass_module_1, dataclass_module_1_str,
dataclass_module_2, dataclass_module_2_str,
):
for m in (dataclass_module_1,
dataclass_module_2):
with self.subTest(m=m):
# There's a difference in how the ClassVars are
# interpreted when using string annotations or
# not. See the imported modules for details.
if m.USING_STRINGS:
c = m.CV(10)
else:
c = m.CV()
c = m.CV(10)
self.assertEqual(c.cv0, 20)


Expand All @@ -2937,14 +2916,9 @@ def test_classvar_module_level_import(self):
# not an instance field.
getattr(c, field_name)

if m.USING_STRINGS:
# iv4 is interpreted as a normal field.
self.assertIn('not_iv4', c.__dict__)
self.assertEqual(c.not_iv4, 4)
else:
# iv4 is interpreted as an InitVar, so it
# won't exist on the instance.
self.assertNotIn('not_iv4', c.__dict__)
# iv4 is interpreted as a normal field.
self.assertIn('not_iv4', c.__dict__)
self.assertEqual(c.not_iv4, 4)

def test_text_annotations(self):
from test import dataclass_textanno
Expand Down