-
Notifications
You must be signed in to change notification settings - Fork 0
/
model_parser.py
executable file
·158 lines (128 loc) · 5.19 KB
/
model_parser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/python2.4
#
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Library to support parsing plain-old-data models.
Parsing is implemented by copying and possibly transforming data from a
'source' model to a 'target' model.
The 'plain-old-data model' concept refers to objects with attributes that are
simple scalars or lists of scalars. The actual type of a scalar attribute is
not prescribed by this module, and is never inferred by it. There is no
dynamic type handling. Instead, the application must have prior knowledge of
the attribute types.
This module includes routines for copying attributes from one model to another
with a different but related type. The two types may share some set of common
attributes, with the association being made by attribute name, or via an
explicit attribute name_map. The name_map contains target attribute names as
keys and source attribute names as values.
The types of each associated attribute are constrained. If the source
attribute is of a scalar type, then the corresponding target attribute must be
of scalar or list type. If the source attribute is of list type, the
corresponding target attribute must also be of list type.
AssignScalarAttrs: scalar to scalar
AppendListAttrs: list to list
AppendScalarAttrs: scalar to list
By default, the corresponding scalar types must be equivalent. However, with
the option of using a scalar type 'converter', the application can transform
the attribute values from source type to target type.
Failure to parse an individual attribute does not raise an exception.
Instead, all exceptions are logged with the exception of
DeadlineExceededError, which is propagated.
"""
__author__ = '[email protected] (Matt Frantz)'
import logging
import traceback
try:
from google.appengine.runtime import DeadlineExceededError
except ImportError:
# google3
from google3.apphosting.runtime.apiproxy_errors import DeadlineExceededError
from google3.pyglib import logging
def AssignScalarAttrs(target, source, attr_names, name_map, converter):
"""Assigns scalar source attributes to scalar target attributes.
Args:
target: Target model object
source: Source model object
attr_names: List of attribute names (list of str)
name_map: Dict to translate target attr names to source attribute names
(str:str)
converter: Function which converts a source attribute to its target form.
"""
def Do(attr_name):
if attr_name in name_map:
source_attr_name = name_map[attr_name]
else:
source_attr_name = attr_name
if hasattr(source, source_attr_name):
attr = getattr(source, source_attr_name)
attr = converter(attr)
setattr(target, attr_name, attr)
_SafeIterate(attr_names, Do)
def AppendListAttrs(target, source, attr_names, name_map, converter):
"""Appends each element of the source attributes to target attributes.
Args:
target: Target model object
source: Source model object
attr_names: List of attribute names (list of str)
name_map: Dict to translate target attr names to source attribute names
(str:str)
converter: Function which converts a source attribute to its target form.
"""
def Do(attr_name):
if attr_name in name_map:
source_attr_name = name_map[attr_name]
else:
source_attr_name = attr_name
source_list = getattr(source, source_attr_name)
target_list = getattr(target, attr_name)
for attr in source_list:
attr = converter(attr)
target_list.append(attr)
_SafeIterate(attr_names, Do)
def AppendScalarAttrs(target, source, attr_names, name_map, converter):
"""Appends the source attributes to target attributes.
Args:
target: Target model object
source: Source model object
attr_names: List of attribute names (list of str)
name_map: Dict to translate target attr names to source attribute names
(str:str)
converter: Function which converts a source attribute to its target form.
"""
def Do(attr_name):
if attr_name in name_map:
source_attr_name = name_map[attr_name]
else:
source_attr_name = attr_name
if hasattr(source, source_attr_name):
attr = getattr(source, source_attr_name)
attr = converter(attr)
target_list = getattr(target, attr_name)
target_list.append(attr)
_SafeIterate(attr_names, Do)
def _SafeIterate(args, do):
"""Vists each attribute name.
Traps and logs errors on each element.
Args:
args: Iterable of arguments.
do: Functor that accepts each argument.
"""
for arg in args:
try:
do(arg)
except (DeadlineExceededError, AssertionError):
raise
except Exception, e:
logging.debug('%s', traceback.format_exc())
logging.error(e)