Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
03220fdb
Commit
03220fdb
authored
Dec 29, 2017
by
Eric V. Smith
Committed by
GitHub
Dec 29, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpo-32427: Expose dataclasses.MISSING object. (#5045)
parent
e3256087
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
70 additions
and
23 deletions
+70
-23
Lib/dataclasses.py
Lib/dataclasses.py
+21
-20
Lib/test/test_dataclasses.py
Lib/test/test_dataclasses.py
+49
-3
No files found.
Lib/dataclasses.py
View file @
03220fdb
...
...
@@ -8,6 +8,7 @@ __all__ = ['dataclass',
'field'
,
'FrozenInstanceError'
,
'InitVar'
,
'MISSING'
,
# Helper functions.
'fields'
,
...
...
@@ -29,11 +30,11 @@ class _HAS_DEFAULT_FACTORY_CLASS:
return
'<factory>'
_HAS_DEFAULT_FACTORY
=
_HAS_DEFAULT_FACTORY_CLASS
()
# A sentinel object to detect if a parameter is supplied or not.
class
_MISSING_FACTORY
:
def
__repr__
(
self
)
:
return
'<missing>'
_MISSING
=
_MISSING_FACTORY
()
# A sentinel object to detect if a parameter is supplied or not.
Use
# a class to give it a better repr.
class
_MISSING_TYPE
:
pass
MISSING
=
_MISSING_TYPE
()
# Since most per-field metadata will be unused, create an empty
# read-only proxy that can be shared among all fields.
...
...
@@ -114,7 +115,7 @@ class Field:
# This function is used instead of exposing Field creation directly,
# so that a type checker can be told (via overloads) that this is a
# function whose type depends on its parameters.
def
field
(
*
,
default
=
_MISSING
,
default_factory
=
_
MISSING
,
init
=
True
,
repr
=
True
,
def
field
(
*
,
default
=
MISSING
,
default_factory
=
MISSING
,
init
=
True
,
repr
=
True
,
hash
=
None
,
compare
=
True
,
metadata
=
None
):
"""Return an object to identify dataclass fields.
...
...
@@ -130,7 +131,7 @@ def field(*, default=_MISSING, default_factory=_MISSING, init=True, repr=True,
It is an error to specify both default and default_factory.
"""
if
default
is
not
_MISSING
and
default_factory
is
not
_
MISSING
:
if
default
is
not
MISSING
and
default_factory
is
not
MISSING
:
raise
ValueError
(
'cannot specify both default and default_factory'
)
return
Field
(
default
,
default_factory
,
init
,
repr
,
hash
,
compare
,
metadata
)
...
...
@@ -149,12 +150,12 @@ def _tuple_str(obj_name, fields):
def
_create_fn
(
name
,
args
,
body
,
globals
=
None
,
locals
=
None
,
return_type
=
_
MISSING
):
return_type
=
MISSING
):
# Note that we mutate locals when exec() is called. Caller beware!
if
locals
is
None
:
locals
=
{}
return_annotation
=
''
if
return_type
is
not
_
MISSING
:
if
return_type
is
not
MISSING
:
locals
[
'_return_type'
]
=
return_type
return_annotation
=
'->_return_type'
args
=
','
.
join
(
args
)
...
...
@@ -182,7 +183,7 @@ def _field_init(f, frozen, globals, self_name):
# initialize this field.
default_name
=
f'_dflt_
{
f
.
name
}
'
if
f
.
default_factory
is
not
_
MISSING
:
if
f
.
default_factory
is
not
MISSING
:
if
f
.
init
:
# This field has a default factory. If a parameter is
# given, use it. If not, call the factory.
...
...
@@ -210,10 +211,10 @@ def _field_init(f, frozen, globals, self_name):
else
:
# No default factory.
if
f
.
init
:
if
f
.
default
is
_
MISSING
:
if
f
.
default
is
MISSING
:
# There's no default, just do an assignment.
value
=
f
.
name
elif
f
.
default
is
not
_
MISSING
:
elif
f
.
default
is
not
MISSING
:
globals
[
default_name
]
=
f
.
default
value
=
f
.
name
else
:
...
...
@@ -236,14 +237,14 @@ def _init_param(f):
# For example, the equivalent of 'x:int=3' (except instead of 'int',
# reference a variable set to int, and instead of '3', reference a
# variable set to 3).
if
f
.
default
is
_MISSING
and
f
.
default_factory
is
_
MISSING
:
if
f
.
default
is
MISSING
and
f
.
default_factory
is
MISSING
:
# There's no default, and no default_factory, just
# output the variable name and type.
default
=
''
elif
f
.
default
is
not
_
MISSING
:
elif
f
.
default
is
not
MISSING
:
# There's a default, this will be the name that's used to look it up.
default
=
f'=_dflt_
{
f
.
name
}
'
elif
f
.
default_factory
is
not
_
MISSING
:
elif
f
.
default_factory
is
not
MISSING
:
# There's a factory function. Set a marker.
default
=
'=_HAS_DEFAULT_FACTORY'
return
f'
{
f
.
name
}
:_type_
{
f
.
name
}{
default
}
'
...
...
@@ -261,13 +262,13 @@ def _init_fn(fields, frozen, has_post_init, self_name):
for
f
in
fields
:
# Only consider fields in the __init__ call.
if
f
.
init
:
if
not
(
f
.
default
is
_MISSING
and
f
.
default_factory
is
_
MISSING
):
if
not
(
f
.
default
is
MISSING
and
f
.
default_factory
is
MISSING
):
seen_default
=
True
elif
seen_default
:
raise
TypeError
(
f'non-default argument
{
f
.
name
!
r
}
'
'follows default argument'
)
globals
=
{
'
_MISSING'
:
_
MISSING
,
globals
=
{
'
MISSING'
:
MISSING
,
'_HAS_DEFAULT_FACTORY'
:
_HAS_DEFAULT_FACTORY
}
body_lines
=
[]
...
...
@@ -368,7 +369,7 @@ def _get_field(cls, a_name, a_type):
# If the default value isn't derived from field, then it's
# only a normal default value. Convert it to a Field().
default
=
getattr
(
cls
,
a_name
,
_
MISSING
)
default
=
getattr
(
cls
,
a_name
,
MISSING
)
if
isinstance
(
default
,
Field
):
f
=
default
else
:
...
...
@@ -404,7 +405,7 @@ def _get_field(cls, a_name, a_type):
# Special restrictions for ClassVar and InitVar.
if
f
.
_field_type
in
(
_FIELD_CLASSVAR
,
_FIELD_INITVAR
):
if
f
.
default_factory
is
not
_
MISSING
:
if
f
.
default_factory
is
not
MISSING
:
raise
TypeError
(
f'field
{
f
.
name
}
cannot have a '
'default factory'
)
# Should I check for other field settings? default_factory
...
...
@@ -474,7 +475,7 @@ def _process_class(cls, repr, eq, order, hash, init, frozen):
# with the real default. This is so that normal class
# introspection sees a real default value, not a Field.
if
isinstance
(
getattr
(
cls
,
f
.
name
,
None
),
Field
):
if
f
.
default
is
_
MISSING
:
if
f
.
default
is
MISSING
:
# If there's no default, delete the class attribute.
# This happens if we specify field(repr=False), for
# example (that is, we specified a field object, but
...
...
Lib/test/test_dataclasses.py
View file @
03220fdb
from
dataclasses
import
(
dataclass
,
field
,
FrozenInstanceError
,
fields
,
asdict
,
astuple
,
make_dataclass
,
replace
,
InitVar
,
Field
make_dataclass
,
replace
,
InitVar
,
Field
,
MISSING
)
import
pickle
...
...
@@ -917,12 +917,12 @@ class TestCase(unittest.TestCase):
param
=
next
(
params
)
self
.
assertEqual
(
param
.
name
,
'k'
)
self
.
assertIs
(
param
.
annotation
,
F
)
# Don't test for the default, since it's set to
_
MISSING
# 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
)
# Don't test for the default, since it's set to
_
MISSING
# 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
)
...
...
@@ -948,6 +948,52 @@ class TestCase(unittest.TestCase):
validate_class
(
C
)
def
test_missing_default
(
self
):
# Test that MISSING works the same as a default not being
# specified.
@
dataclass
class
C
:
x
:
int
=
field
(
default
=
MISSING
)
with
self
.
assertRaisesRegex
(
TypeError
,
r'__init__\
(
\) missing 1 required '
'positional argument'
):
C
()
self
.
assertNotIn
(
'x'
,
C
.
__dict__
)
@
dataclass
class
D
:
x
:
int
with
self
.
assertRaisesRegex
(
TypeError
,
r'__init__\
(
\) missing 1 required '
'positional argument'
):
D
()
self
.
assertNotIn
(
'x'
,
D
.
__dict__
)
def
test_missing_default_factory
(
self
):
# Test that MISSING works the same as a default factory not
# being specified (which is really the same as a default not
# being specified, too).
@
dataclass
class
C
:
x
:
int
=
field
(
default_factory
=
MISSING
)
with
self
.
assertRaisesRegex
(
TypeError
,
r'__init__\
(
\) missing 1 required '
'positional argument'
):
C
()
self
.
assertNotIn
(
'x'
,
C
.
__dict__
)
@
dataclass
class
D
:
x
:
int
=
field
(
default
=
MISSING
,
default_factory
=
MISSING
)
with
self
.
assertRaisesRegex
(
TypeError
,
r'__init__\
(
\) missing 1 required '
'positional argument'
):
D
()
self
.
assertNotIn
(
'x'
,
D
.
__dict__
)
def
test_missing_repr
(
self
):
self
.
assertIn
(
'MISSING_TYPE object'
,
repr
(
MISSING
))
def
test_dont_include_other_annotations
(
self
):
@
dataclass
class
C
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment