Skip to content

Code generator produces invalid Python identifiers from generic type names with angle brackets #562

Description

@bennerv

Bug Description

When a Swagger/OpenAPI spec contains a $ref with angle brackets in the type name (e.g., Record<UserAssignedIdentityResourceId>), the code generator produces Python identifiers that contain < and > characters, which are invalid in Python. This causes a syntax error when the generated Azure CLI extension is loaded.

Error

cli.azure.cli.core: Unable to load extension 'aro-hcp: cannot assign to comparison (_create.py, line 403)'. Use --debug for more information.

Root Cause

In src/aaz_dev/swagger/model/schema/cmd_builder.py, the method _get_cls_definition_name() extracts the ref name (e.g., Record<UserAssignedIdentityResourceId>) and passes it through to_camel_case() without stripping angle brackets. This produces a cls name like Record<UserAssignedIdentityResourceId>_CreateOrUpdate_create, which then flows through to_snake_case() in the code generation templates to produce Python identifiers like:

_args_record<userassignedidentityresourceid>_create_or_update_create = None

Python interprets < and > as comparison operators, so this line becomes an attempt to assign to a comparison expression — a syntax error.

Affected Code Paths

The invalid identifier appears in multiple generated patterns:

  • _args_record<...> (class attributes in arg group generators)
  • _build_args_record<...> (method names in arg group generators)
  • _schema_record<...> (class attributes in operation generators)
  • _build_schema_record<...> (method names in operation generators)

Generated from:

  • src/aaz_dev/cli/controller/az_arg_group_generator.py (lines 84, 168)
  • src/aaz_dev/cli/controller/az_operation_generator.py (lines 599, 1021)

Reproduction

  1. Use an API spec that contains a $ref to a type with angle brackets (e.g., #/definitions/Record<UserAssignedIdentityResourceId> from the Microsoft.RedHatOpenShift resource provider, API version 2025-12-23-preview)
  2. Generate CLI commands using aaz-dev
  3. Attempt to load the generated extension — it fails with the syntax error above

Suggested Fix

Strip angle brackets from ref names in _get_cls_definition_name() before converting to a cls name:

def _get_cls_definition_name(self, schema):
    assert isinstance(schema, ReferenceSchema)
    ref_name = schema.ref.split('/')[-1]
    ref_name = re.sub(r'[<>]', '', ref_name)
    schema_cls_name = f"{to_camel_case(ref_name.replace('.', ' '))}_{self.mutability}"
    ...

And as defense-in-depth, strip non-identifier characters in to_snake_case() in src/aaz_dev/utils/case.py:

def to_snake_case(name, separator='_'):
    assert isinstance(name, str)
    name = re.sub(r'[^a-zA-Z0-9_\-]', '', name)
    ...

Additional Note

The CMDClassField validation regex in src/aaz_dev/command/model/configuration/_fields.py uses re.match() which only checks from the start of the string, so Record<...> passes validation because Record matches [A-Z][a-zA-Z0-9_]+. This could be tightened to use a full-match anchor ($) to catch invalid names at validation time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions