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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions