Skip to content

Conversation

ryannygard-rldatix
Copy link

@ryannygard-rldatix ryannygard-rldatix commented Jul 22, 2025

Description

This pull request improves the get_replacement_type logic to allow developers to subclass ReprWorkaround. Currently, any subclass of postgresql.ENUM is forcibly replaced with the base ReprWorkaround class, which breaks compatibility with mixins like sqlalchemy_utils.ScalarCoercible that rely on methods such as _coerce.

Key changes:

Updated the get_replacement_type condition to exclude existing subclasses of ReprWorkaround

Modified ReprWorkaround.repr to handle inherited class names and avoid hardcoded module assumptions

Clarified that ReprWorkaround no longer needs to reside in sqlalchemy.dialects.postgresql when user_module_prefix is set to "" in Alembic config

This change enables more flexible enum customization without breaking autogeneration or runtime behavior.

Fixes: #119

Changes and Rationale

  1. Modified get_replacement_type to support ReprWorkaround subclasses
    Change:
if isinstance(replacement_enum_type, postgresql.ENUM) and not isinstance(replacement_enum_type, ReprWorkaround):

Why:
Previously, all postgresql.ENUM instances were replaced with the base ReprWorkaround, regardless of whether they were subclasses. This made it impossible to extend ReprWorkaround with additional functionality (e.g. _coerce from ScalarCoercible) without losing those custom methods. The added and not isinstance(..., ReprWorkaround) condition preserves subclass behavior and prevents unnecessary replacement.

  1. Updated ReprWorkaround.repr for compatibility with subclassing and dynamic module resolution
    Change:
def __repr__(self):
    return f"{super().__repr__()[:-1]}, create_type=False)".replace(
        self.__class__.__name__, "postgresql.ENUM"
    ).replace(", metadata=MetaData()", "")

Why:
The original repr implementation assumed ReprWorkaround was used directly and located within sqlalchemy.dialects.postgresql. This caused issues when subclassing, especially during autogeneration, where the replacement logic relied on strict string matching. The updated method dynamically replaces the current class name rather than hardcoding ReprWorkaround, ensuring that subclasses produce a compatible repr string. This preserves proper type comparison behavior in Alembic, even when using extended custom types.

  1. "user_module_prefix": "" in Alembic migration configuration
    Change:
"user_module_prefix": "",

Why:
To support user-defined column types in tests (e.g. subclasses of ReprWorkaround), it was necessary to configure Alembic with an empty user_module_prefix. This instructs Alembic to render those custom types directly, without requiring them to be fully importable via a Python module path. As a result, we no longer need to assign module = "sqlalchemy.dialects.postgresql" on the ReprWorkaround class.

@ryannygard-rldatix ryannygard-rldatix changed the title Ability to inherit repr workaround Ability to inherit ReprWorkaround Jul 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow inheritance of ReprWorkaround in get_replacement_type for compatibility with mixins like ScalarCoercible
1 participant