Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
T
TypedCols
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
713
Warden
TypedCols
Commits
08f602f9
Commit
08f602f9
authored
Oct 24, 2017
by
Pavel Kácha
Browse files
Options
Downloads
Patches
Plain Diff
Added dynamically defined OpenTypedDict class
parent
56356a0b
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
test_typedcols.py
+52
-33
52 additions, 33 deletions
test_typedcols.py
typedcols.py
+78
-13
78 additions, 13 deletions
typedcols.py
with
130 additions
and
46 deletions
test_typedcols.py
+
52
−
33
View file @
08f602f9
...
...
@@ -4,25 +4,25 @@
# Copyright (c) 2016, CESNET, z. s. p. o.
# Use of this source is governed by an ISC license, see LICENSE file.
from
typedcols
import
TypedDict
,
TypedList
,
KeyNotAllowed
,
KeysRequired
,
Discard
,
Any
from
typedcols
import
TypedDict
,
OpenTypedDict
,
TypedList
,
KeyNotAllowed
,
KeysRequired
,
Discard
,
Any
from
sys
import
version_info
import
unittest
class
AddressDict
(
TypedDict
):
typedef
=
{
address_typedef
=
{
"
street
"
:
{
"
type
"
:
str
},
"
num
"
:
{
"
type
"
:
int
,
"
description
"
:
"
Street number
"
},
"
city
"
:
str
,
"
state
"
:
{
"
type
"
:
str
,
"
required
"
:
True
}
}
class
AddressDict
(
TypedDict
):
typedef
=
address_typedef
allow_unknown
=
True
def
raise_discard
(
x
=
None
):
raise
Discard
class
PersonDict
(
TypedDict
):
typedef
=
{
person_typedef
=
{
"
name
"
:
{
"
type
"
:
str
,
"
default
"
:
"
_Default_Value_
"
},
"
age
"
:
int
,
"
address
"
:
{
"
type
"
:
AddressDict
},
...
...
@@ -35,18 +35,8 @@ class PersonDict(TypedDict):
"
discard_default1
"
:
{
"
type
"
:
Any
,
"
default
"
:
Discard
},
# Same as no default
"
discard_default2
"
:
{
"
type
"
:
Any
,
"
default
"
:
raise_discard
},
# Same as no default
}
allow_unknown
=
False
# Monkeypatching for cheap Py 2 & 3 compatibility
if
not
hasattr
(
unittest
.
TestCase
,
"
assertRaisesRegex
"
):
unittest
.
TestCase
.
assertRaisesRegex
=
unittest
.
TestCase
.
assertRaisesRegexp
class
TestTypedDict
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
person
=
PersonDict
({
person_init_data
=
{
"
age
"
:
"
34
"
,
"
note
"
:
None
,
"
address
"
:
{
...
...
@@ -58,7 +48,22 @@ class TestTypedDict(unittest.TestCase):
"
discard2
"
:
"
garbage
"
,
"
discard3
"
:
"
rubbish
"
,
"
discard4
"
:
"
scrap
"
})
}
class
PersonDict
(
TypedDict
):
typedef
=
person_typedef
allow_unknown
=
False
# Monkeypatching for cheap Py 2 & 3 compatibility
if
not
hasattr
(
unittest
.
TestCase
,
"
assertRaisesRegex
"
):
unittest
.
TestCase
.
assertRaisesRegex
=
unittest
.
TestCase
.
assertRaisesRegexp
class
TestTypedDict
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
person
=
PersonDict
(
person_init_data
)
def
testTypedefNormalization
(
self
):
self
.
assertEqual
(
self
.
person
.
typedef
[
"
age
"
],
{
"
type
"
:
int
})
...
...
@@ -127,6 +132,20 @@ class TestTypedDict(unittest.TestCase):
(
"
note
"
,
None
)
])
class
TestOpenTypedDict
(
TestTypedDict
):
def
setUp
(
self
):
open_address_dict
=
OpenTypedDict
(
typedef
=
address_typedef
.
copy
())
open_person_typedef
=
person_typedef
.
copy
()
open_person_typedef
[
"
address
"
]
=
open_address_dict
open_person_dict
=
OpenTypedDict
(
typedef
=
open_person_typedef
)
self
.
person
=
open_person_dict
(
person_init_data
)
class
IntList
(
TypedList
):
item_type
=
int
...
...
This diff is collapsed.
Click to expand it.
typedcols.py
+
78
−
13
View file @
08f602f9
...
...
@@ -10,7 +10,7 @@ Defines TypedDict and TypedList, which enforce inserted types based on simple
type definition.
"""
__version__
=
'
0.1.
9
'
__version__
=
'
0.1.
10
'
__author__
=
'
Pavel Kácha <pavel.kacha@cesnet.cz>
'
import
collections
...
...
@@ -42,24 +42,26 @@ def Any(v):
return
v
class
TypedDictMetaclass
(
abc
.
ABCMeta
):
"""
Metaclass for TypedDict, allowing simplified typedefs - if typedef is not
dict, simple type object is assumed and correct dict is created.
Metaclassed to be run just once for the class, not for each instance.
"""
def
dictifyTypedef
(
self
,
typedef
):
def
dictify_typedef
(
typedef
):
for
key
in
typedef
:
tdef
=
typedef
[
key
]
if
not
isinstance
(
tdef
,
collections
.
Mapping
):
if
callable
(
tdef
):
typedef
[
key
]
=
{
"
type
"
:
tdef
}
typedef
[
key
].
setdefault
(
"
type
"
,
Any
)
class
TypedDictMetaclass
(
abc
.
ABCMeta
):
"""
Metaclass for TypedDict, allowing simplified typedefs - if typedef is
callable, simple type object is assumed and correct dict is created.
Metaclassed to be run just once for the class, not for each instance.
"""
def
__init__
(
cls
,
name
,
bases
,
dct
):
super
(
TypedDictMetaclass
,
cls
).
__init__
(
name
,
bases
,
dct
)
cls
.
dictify
T
ypedef
(
cls
.
typedef
)
dictify
_t
ypedef
(
cls
.
typedef
)
class
TypedDict
(
collections
.
MutableMapping
):
class
TypedDict
Base
(
collections
.
MutableMapping
):
"""
Dictionary type abstract class, which supports checking of inserted
types, based on simple type definition.
...
...
@@ -159,7 +161,7 @@ class TypedDict(collections.MutableMapping):
.arg will be message from
"
description
"
field in type definition
"""
tdef
=
self
.
getTypedef
(
key
)
valuetype
=
tdef
.
get
(
"
type
"
,
Any
)
valuetype
=
tdef
[
"
type
"
]
if
valuetype
is
Discard
:
return
try
:
...
...
@@ -211,7 +213,70 @@ class TypedDict(collections.MutableMapping):
# Py 2 requires metaclassing by __metaclass__ attribute, whereas Py 3
# needs metaclass argument. What actually happens is the following,
# so we will do it explicitly, to be compatible with both versions.
TypedDict
=
TypedDictMetaclass
(
"
TypedDict
"
,
(
TypedDict
,),
{})
TypedDict
=
TypedDictMetaclass
(
"
TypedDict
"
,
(
TypedDictBase
,),
{})
class
TypedefSetter
(
object
):
"""
Setter for OpenTypedDict.typedef value, which forces typedef canonicalisation.
Implemented as setter only class, as it does not intercept and slow down read
access.
"""
def
__set__
(
self
,
obj
,
value
):
dictify_typedef
(
value
)
obj
.
__dict__
[
"
typedef
"
]
=
value
class
OpenTypedDict
(
TypedDictBase
):
"""
Dictionary type class, which supports checking of inserted types, based on
simple type definition, which must be provided in constructor and is changeable
by assigning instance.typedef variable.
Note however that changing already populated OpenTypedDict
'
s typedef to
incompatible definition may lead to undefined results and data inconsistent
with definition.
"""
def
__init__
(
self
,
init_data
=
None
,
typedef
=
None
,
allow_unknown
=
False
,
dict_class
=
dict
):
"""
init_data: initial values
typedef: dictionary with keys and their type definitions. Type definition
may be simple callable (int, string, check_func,
AnotherTypedDict), or dict with the following members:
"
type
"
:
type enforcing callable. If callable returns, raises
or is Discard, key will be silently discarded
"
default
"
:
new TypedDict subclass will be initialized with keys
with this value; deleted keys will also revert to it
"
required
"
:
bool, checkRequired method will report the key if not present
"
description
"
:
string, explaining field type in human readable terms
(will be used in exception explanations)
Type enforcing callable must take one argument, and return value,
coerced to expected type. Coercion may even be conversion, for example
arbitrary date string, converted to DateTime.
allow_unknown: boolean, specifies whether dictionary allows unknown keys,
that means keys, which are not defined in
'
typedef
'
dict_class: class or factory for underlying dict implementation
"""
self
.
allow_unknown
=
allow_unknown
self
.
dict_class
=
dict_class
self
.
typedef
=
typedef
or
{}
super
(
OpenTypedDict
,
self
).
__init__
(
init_data
)
typedef
=
TypedefSetter
()
def
__call__
(
self
,
data
):
"""
Instances are made callable so they can be used in nested
"
type
"
definitions. Note however that these classes are mutable, so
assigning new values replaces old ones.
"""
self
.
update
(
data
)
return
self
class
TypedList
(
collections
.
MutableSequence
):
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment