Skip to content

Commit 21c944d

Browse files
committed
Change implementation of struct type build functions
They(EnumType, EntityType, ComplexType) now handle parsing of invalid metadata independently. Thus, you do not have to wrap build_element in try-except block. Build function either return valid type, null type or fails. (Last two options depend on which policy is set.)
1 parent 9c47514 commit 21c944d

4 files changed

Lines changed: 65 additions & 80 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1818

1919
### Changed
2020
- Implementation and naming schema of `from_etree` - Martin Miksik
21+
- Build functions of struct types now handle invalid metadata independently. - Martin Miksik
2122

2223
### Fixed
2324
- make sure configured error policies are applied for Annotations referencing

pyodata/model/build_functions.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
from pyodata.policies import ParserError
88
from pyodata.config import Config
9-
from pyodata.exceptions import PyODataParserError, PyODataModelError
9+
from pyodata.exceptions import PyODataParserError, PyODataModelError, PyODataException
1010
from pyodata.model.elements import sap_attribute_get_bool, sap_attribute_get_string, StructType, StructTypeProperty, \
1111
Types, EntitySet, ValueHelper, ValueHelperParameter, FunctionImportParameter, \
12-
FunctionImport, metadata_attribute_get, EntityType, ComplexType, build_element
12+
FunctionImport, metadata_attribute_get, EntityType, ComplexType, build_element, NullType
1313

1414
# pylint: disable=cyclic-import
1515
# When using `import xxx as yyy` it is not a problem and we need this dependency
@@ -83,28 +83,37 @@ def build_struct_type(config: Config, type_node, typ, schema=None):
8383

8484

8585
def build_complex_type(config: Config, type_node, schema=None):
86-
return build_element(StructType, config, type_node=type_node, typ=ComplexType, schema=schema)
86+
try:
87+
return build_element(StructType, config, type_node=type_node, typ=ComplexType, schema=schema)
88+
except (PyODataException, KeyError, AttributeError) as ex:
89+
config.err_policy(ParserError.COMPLEX_TYPE).resolve(ex)
90+
return NullType(type_node.get('Name'))
8791

8892

8993
# pylint: disable=protected-access
9094
def build_entity_type(config: Config, type_node, schema=None):
91-
etype = build_element(StructType, config, type_node=type_node, typ=EntityType, schema=schema)
95+
try:
96+
etype = build_element(StructType, config, type_node=type_node, typ=EntityType, schema=schema)
9297

93-
for proprty in type_node.xpath('edm:Key/edm:PropertyRef', namespaces=config.namespaces):
94-
etype._key.append(etype.proprty(proprty.get('Name')))
98+
for proprty in type_node.xpath('edm:Key/edm:PropertyRef', namespaces=config.namespaces):
99+
etype._key.append(etype.proprty(proprty.get('Name')))
95100

96-
for proprty in type_node.xpath('edm:NavigationProperty', namespaces=config.namespaces):
97-
navp = build_element('NavigationTypeProperty', config, node=proprty)
101+
for proprty in type_node.xpath('edm:NavigationProperty', namespaces=config.namespaces):
102+
navp = build_element('NavigationTypeProperty', config, node=proprty)
98103

99-
if navp.name in etype._nav_properties:
100-
raise KeyError('{0} already has navigation property {1}'.format(etype, navp.name))
104+
if navp.name in etype._nav_properties:
105+
raise KeyError('{0} already has navigation property {1}'.format(etype, navp.name))
101106

102-
etype._nav_properties[navp.name] = navp
107+
etype._nav_properties[navp.name] = navp
103108

104-
return etype
109+
return etype
110+
except (KeyError, AttributeError) as ex:
111+
config.err_policy(ParserError.ENTITY_TYPE).resolve(ex)
112+
return NullType(type_node.get('Name'))
105113

106114

107115
def build_entity_set(config, entity_set_node):
116+
# pylint: disable=too-many-locals
108117
name = entity_set_node.get('Name')
109118
et_info = Types.parse_type_name(entity_set_node.get('EntityType'))
110119

@@ -133,6 +142,7 @@ def build_entity_set(config, entity_set_node):
133142

134143

135144
def build_value_helper(config, target, annotation_node, schema):
145+
# pylint: disable=too-many-locals
136146
label = None
137147
collection_path = None
138148
search_supported = False

pyodata/v2/build_functions.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,10 @@ def build_schema(config: Config, schema_nodes):
3838
schema._decls[namespace] = decl
3939

4040
for complex_type in schema_node.xpath('edm:ComplexType', namespaces=config.namespaces):
41-
try:
42-
ctype = build_element(ComplexType, config, type_node=complex_type)
43-
except (KeyError, AttributeError) as ex:
44-
config.err_policy(ParserError.COMPLEX_TYPE).resolve(ex)
45-
ctype = NullType(complex_type.get('Name'))
46-
47-
decl.add_complex_type(ctype)
41+
decl.add_complex_type(build_element(ComplexType, config, type_node=complex_type, schema=schema))
4842

4943
for entity_type in schema_node.xpath('edm:EntityType', namespaces=config.namespaces):
50-
try:
51-
etype = build_element(EntityType, config, type_node=entity_type)
52-
except (KeyError, AttributeError) as ex:
53-
config.err_policy(ParserError.ENTITY_TYPE).resolve(ex)
54-
etype = NullType(entity_type.get('Name'))
55-
56-
decl.add_entity_type(etype)
44+
decl.add_entity_type(build_element(EntityType, config, type_node=entity_type, schema=schema))
5745

5846
# resolve types of properties
5947
for stype in itertools.chain(schema.entity_types, schema.complex_types):

pyodata/v4/build_functions.py

Lines changed: 40 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -36,31 +36,13 @@ def build_schema(config: Config, schema_nodes):
3636
decl.add_type_definition(build_element(Typ, config, node=type_def))
3737

3838
for enum_type in schema_node.xpath('edm:EnumType', namespaces=config.namespaces):
39-
try:
40-
etype = build_element(EnumType, config, type_node=enum_type, namespace=namespace)
41-
except (PyODataParserError, AttributeError) as ex:
42-
config.err_policy(ParserError.ENUM_TYPE).resolve(ex)
43-
etype = NullType(enum_type.get('Name'))
44-
45-
decl.add_enum_type(etype)
39+
decl.add_enum_type(build_element(EnumType, config, type_node=enum_type, namespace=namespace))
4640

4741
for complex_type in schema_node.xpath('edm:ComplexType', namespaces=config.namespaces):
48-
try:
49-
ctype = build_element(ComplexType, config, type_node=complex_type, schema=schema)
50-
except (KeyError, AttributeError) as ex:
51-
config.err_policy(ParserError.COMPLEX_TYPE).resolve(ex)
52-
ctype = NullType(complex_type.get('Name'))
53-
54-
decl.add_complex_type(ctype)
42+
decl.add_complex_type(build_element(ComplexType, config, type_node=complex_type, schema=schema))
5543

5644
for entity_type in schema_node.xpath('edm:EntityType', namespaces=config.namespaces):
57-
try:
58-
etype = build_element(EntityType, config, type_node=entity_type, schema=schema)
59-
except (KeyError, AttributeError) as ex:
60-
config.err_policy(ParserError.ENTITY_TYPE).resolve(ex)
61-
etype = NullType(entity_type.get('Name'))
62-
63-
decl.add_entity_type(etype)
45+
decl.add_entity_type(build_element(EntityType, config, type_node=entity_type, schema=schema))
6446

6547
# resolve types of properties
6648
for stype in itertools.chain(schema.entity_types, schema.complex_types):
@@ -186,49 +168,53 @@ def build_type_definition(config: Config, node):
186168

187169
# pylint: disable=protected-access, too-many-locals
188170
def build_enum_type(config: Config, type_node, namespace):
189-
ename = type_node.get('Name')
190-
is_flags = type_node.get('IsFlags')
171+
try:
172+
ename = type_node.get('Name')
173+
is_flags = type_node.get('IsFlags')
191174

192-
# namespace = kwargs['namespace']
175+
# namespace = kwargs['namespace']
193176

194-
underlying_type = type_node.get('UnderlyingType')
177+
underlying_type = type_node.get('UnderlyingType')
195178

196-
# https://docs.oasis-open.org/odata/odata-csdl-json/v4.01/csprd04/odata-csdl-json-v4.01-csprd04.html#sec_EnumerationType
197-
if underlying_type is None:
198-
underlying_type = 'Edm.Int32'
179+
# https://docs.oasis-open.org/odata/odata-csdl-json/v4.01/csprd04/odata-csdl-json-v4.01-csprd04.html#sec_EnumerationType
180+
if underlying_type is None:
181+
underlying_type = 'Edm.Int32'
199182

200-
valid_types = {
201-
'Edm.Byte': [0, 2 ** 8 - 1],
202-
'Edm.Int16': [-2 ** 15, 2 ** 15 - 1],
203-
'Edm.Int32': [-2 ** 31, 2 ** 31 - 1],
204-
'Edm.Int64': [-2 ** 63, 2 ** 63 - 1],
205-
'Edm.SByte': [-2 ** 7, 2 ** 7 - 1]
206-
}
183+
valid_types = {
184+
'Edm.Byte': [0, 2 ** 8 - 1],
185+
'Edm.Int16': [-2 ** 15, 2 ** 15 - 1],
186+
'Edm.Int32': [-2 ** 31, 2 ** 31 - 1],
187+
'Edm.Int64': [-2 ** 63, 2 ** 63 - 1],
188+
'Edm.SByte': [-2 ** 7, 2 ** 7 - 1]
189+
}
207190

208-
if underlying_type not in valid_types:
209-
raise PyODataParserError(
210-
f'Type {underlying_type} is not valid as underlying type for EnumType - must be one of {valid_types}')
191+
if underlying_type not in valid_types:
192+
raise PyODataParserError(
193+
f'Type {underlying_type} is not valid as underlying type for EnumType - must be one of {valid_types}')
211194

212-
mtype = Types.from_name(underlying_type, config)
213-
etype = EnumType(ename, is_flags, mtype, namespace)
195+
mtype = Types.from_name(underlying_type, config)
196+
etype = EnumType(ename, is_flags, mtype, namespace)
214197

215-
members = type_node.xpath('edm:Member', namespaces=config.namespaces)
198+
members = type_node.xpath('edm:Member', namespaces=config.namespaces)
216199

217-
next_value = 0
218-
for member in members:
219-
name = member.get('Name')
220-
value = member.get('Value')
200+
next_value = 0
201+
for member in members:
202+
name = member.get('Name')
203+
value = member.get('Value')
221204

222-
if value is not None:
223-
next_value = int(value)
205+
if value is not None:
206+
next_value = int(value)
224207

225-
vtype = valid_types[underlying_type]
226-
if not vtype[0] < next_value < vtype[1]:
227-
raise PyODataParserError(f'Value {next_value} is out of range for type {underlying_type}')
208+
vtype = valid_types[underlying_type]
209+
if not vtype[0] < next_value < vtype[1]:
210+
raise PyODataParserError(f'Value {next_value} is out of range for type {underlying_type}')
228211

229-
emember = EnumMember(etype, name, next_value)
230-
etype._member.append(emember)
212+
emember = EnumMember(etype, name, next_value)
213+
etype._member.append(emember)
231214

232-
next_value += 1
215+
next_value += 1
233216

234-
return etype
217+
return etype
218+
except (PyODataParserError, AttributeError) as ex:
219+
config.err_policy(ParserError.ENUM_TYPE).resolve(ex)
220+
return NullType(type_node.get('Name'))

0 commit comments

Comments
 (0)