Skip to content

EnumTypeProperty.escape_value() hardcodes Microsoft.Dynamics.DataEntities namespace instead of using the actual EDM schema namespace #57

Description

@dtcopcp

Summary

When filtering on an enum-typed property (Entity.SomeEnumProp == SomeEnum.Value), the generated OData filter literal is hardcoded to the Microsoft.Dynamics.DataEntities namespace regardless of the actual namespace declared in the service's $metadata. This breaks filtering on enum properties against any OData v4 service that isn't Microsoft Dynamics 365 (e.g. SAP, IFS Cloud, and presumably most other real-world OData v4 services).

Steps to reproduce

from odata import ODataService

Service = ODataService(service_url, session=session, reflect_entities=True)
SomeEntity = Service.entities["SomeEntitySet"]

# SomeEnumProp is correctly reflected as an enum property from $metadata
prop = SomeEntity.SomeEnumProp
enum_cls = prop.enum_class
print(list(enum_cls))  # correctly shows enum members from the real service

# But the escaped filter value is wrong:
print(prop.escape_value(list(enum_cls)[0]))
# -> "Microsoft.Dynamics.DataEntities.SomeEnumProp'FirstValue'"

Tested live against a real IFS Cloud OData v4 service, whose actual EDM namespace for this type is IfsApp.RequestHandling.SrvRequestState (not Microsoft.Dynamics.DataEntities). Sending the generated filter causes the service to reject the request (type not defined in that namespace).

Root cause

In odata/enumtype.py:

def escape_value(self, value):
    if self.enum_class.__module__:
        return f"Microsoft.Dynamics.DataEntities.{self.enum_class.__name__}'{value.name}'"
    return f"{self.enum_class.__name__}'{value.name}'"

self.enum_class.__module__ is essentially always truthy (a Python module name), so the Microsoft Dynamics branch is effectively hit unconditionally — the fallback branch is unreachable in practice, and neither branch is correct for a generic service.

The interesting part: the correct fully-qualified namespace is available during metadata parsing but gets discarded before reaching this code. In odata/metadata.py:

for enum_type in schema['enum_types']:
    ...
    created_enum = EnumType(enum_type['name'], names=names)
    all_types[enum_type['fully_qualified_name']] = created_enum

enum_type['fully_qualified_name'] (e.g. IfsApp.RequestHandling.SrvRequestState) is used only as a dict key to register the type — it's never stored on the EnumType/EnumTypeProperty object itself, so escape_value() has no way to reconstruct the real namespace later and falls back to the hardcoded string.

Suggested fix

Store the fully-qualified namespace on the EnumType (or on EnumTypeProperty) when it's constructed in metadata.py, and have escape_value() use that instead of the hardcoded Microsoft.Dynamics.DataEntities string, e.g.:

def escape_value(self, value):
    return f"{self.enum_class.namespace}.{self.enum_class.__name__}'{value.name}'"

Environment

  • python-odata 0.8.1
  • Python 3.14
  • Tested against IFS Cloud (OData v4), reflected via reflect_entities=True

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions