Skip to content

Commit b6913a9

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 d7550c7 commit b6913a9

File tree

4 files changed

+63
-80
lines changed

4 files changed

+63
-80
lines changed

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: 57 additions & 45 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, EnumType, EnumMember, 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,75 +83,87 @@ 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
# pylint: disable=protected-access, too-many-locals
108116
def build_enum_type(config: Config, type_node, namespace):
109-
ename = type_node.get('Name')
110-
is_flags = type_node.get('IsFlags')
117+
try:
118+
ename = type_node.get('Name')
119+
is_flags = type_node.get('IsFlags')
111120

112-
# namespace = kwargs['namespace']
121+
# namespace = kwargs['namespace']
113122

114-
underlying_type = type_node.get('UnderlyingType')
123+
underlying_type = type_node.get('UnderlyingType')
115124

116-
# https://docs.oasis-open.org/odata/odata-csdl-json/v4.01/csprd04/odata-csdl-json-v4.01-csprd04.html#sec_EnumerationType
117-
if underlying_type is None:
118-
underlying_type = 'Edm.Int32'
125+
# https://docs.oasis-open.org/odata/odata-csdl-json/v4.01/csprd04/odata-csdl-json-v4.01-csprd04.html#sec_EnumerationType
126+
if underlying_type is None:
127+
underlying_type = 'Edm.Int32'
119128

120-
valid_types = {
121-
'Edm.Byte': [0, 2 ** 8 - 1],
122-
'Edm.Int16': [-2 ** 15, 2 ** 15 - 1],
123-
'Edm.Int32': [-2 ** 31, 2 ** 31 - 1],
124-
'Edm.Int64': [-2 ** 63, 2 ** 63 - 1],
125-
'Edm.SByte': [-2 ** 7, 2 ** 7 - 1]
126-
}
129+
valid_types = {
130+
'Edm.Byte': [0, 2 ** 8 - 1],
131+
'Edm.Int16': [-2 ** 15, 2 ** 15 - 1],
132+
'Edm.Int32': [-2 ** 31, 2 ** 31 - 1],
133+
'Edm.Int64': [-2 ** 63, 2 ** 63 - 1],
134+
'Edm.SByte': [-2 ** 7, 2 ** 7 - 1]
135+
}
127136

128-
if underlying_type not in valid_types:
129-
raise PyODataParserError(
130-
f'Type {underlying_type} is not valid as underlying type for EnumType - must be one of {valid_types}')
137+
if underlying_type not in valid_types:
138+
raise PyODataParserError(
139+
f'Type {underlying_type} is not valid as underlying type for EnumType - must be one of {valid_types}')
131140

132-
mtype = Types.from_name(underlying_type, config)
133-
etype = EnumType(ename, is_flags, mtype, namespace)
141+
mtype = Types.from_name(underlying_type, config)
142+
etype = EnumType(ename, is_flags, mtype, namespace)
134143

135-
members = type_node.xpath('edm:Member', namespaces=config.namespaces)
144+
members = type_node.xpath('edm:Member', namespaces=config.namespaces)
136145

137-
next_value = 0
138-
for member in members:
139-
name = member.get('Name')
140-
value = member.get('Value')
146+
next_value = 0
147+
for member in members:
148+
name = member.get('Name')
149+
value = member.get('Value')
141150

142-
if value is not None:
143-
next_value = int(value)
151+
if value is not None:
152+
next_value = int(value)
144153

145-
vtype = valid_types[underlying_type]
146-
if not vtype[0] < next_value < vtype[1]:
147-
raise PyODataParserError(f'Value {next_value} is out of range for type {underlying_type}')
154+
vtype = valid_types[underlying_type]
155+
if not vtype[0] < next_value < vtype[1]:
156+
raise PyODataParserError(f'Value {next_value} is out of range for type {underlying_type}')
148157

149-
emember = EnumMember(etype, name, next_value)
150-
etype._member.append(emember)
158+
emember = EnumMember(etype, name, next_value)
159+
etype._member.append(emember)
151160

152-
next_value += 1
161+
next_value += 1
153162

154-
return etype
163+
return etype
164+
except (PyODataParserError, AttributeError) as ex:
165+
config.err_policy(ParserError.ENUM_TYPE).resolve(ex)
166+
return NullType(type_node.get('Name'))
155167

156168

157169
def build_entity_set(config, entity_set_node):

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: 3 additions & 21 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):

0 commit comments

Comments
 (0)