Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can roles be defined as enums instead of strings? #427

Open
jace opened this issue Nov 20, 2023 · 3 comments
Open

Can roles be defined as enums instead of strings? #427

jace opened this issue Nov 20, 2023 · 3 comments

Comments

@jace
Copy link
Member

jace commented Nov 20, 2023

Roles are currently defined as string-only. No other object type is accepted, and a role match is defined as a plain string match. However, roles are also scoped to a class, so an editor role on one class is not necessarily the same role on another class. The names are re-used for convenience, to identify their function, but usually are not transferable. Renaming a class-specific role is painful because it requires a careful find and replace, avoiding other uses of the same string.

Could roles be defined as an enum instead? Using StrEnum (Python 3.11+, or LowercaseStrEnum from the StrEnum lib, or a custom implementation) may make it a drop-in replacement:

from enum import StrEnum, auto

class CommonRoles(StrEnum):
    ALL = auto()
    AUTH = auto()
    ANON = auto()

class MyClassRoles(StrEnum):
    EDITOR = auto()

class MyModel(RoleMixin):
    __roles__ = {
        CommonRoles.ALL: {'read': {'attr'}},
        MyClassRoles.EDITOR: {'write': {'attr'}},
    }

With this approach, enums and strings can be used interchangeably (to the developer's peril), but using an enum exclusively will make refactoring vastly easier (with caveats such as InspectableSet-based uses, which still need manual find and replace).

Question: what happens when the same name from two different enums is used in the same class? Are they the same role (as when using only strings) or different? Should we raise a warning when this happens, assuming it's detectable?

@jace
Copy link
Member Author

jace commented Nov 20, 2023

There is also desire for a Role class that can hold custom metadata, including a translatable title (aka lazy string) and a description string. This class will not be interchangeable with plain strings unless it also subclasses str.

@jace
Copy link
Member Author

jace commented Nov 20, 2023

It appears there is a small hack possible, using a dataclass as the type for an enum to include metadata:

@dataclass
class Metadata:
    name: str
    title: str | LazyString  # Sample metadata

    def __str__(self):
        return self.name
    # Insert various other methods to ensure compatibility with `str`, including `__hash__`

class CommonRoles(Metadata, Enum):
    ALL = "all", __("Anybody")
    AUTH = "auth", __("Authenticated user")
    ANON = "anon", __("Anonymous user")

However, interchangeability with str is suspect since Metadata is not a subclass of str. Could it be made a subclass?

@jace
Copy link
Member Author

jace commented Mar 19, 2024

#429 introduced DataclassFromType, allowing str to be subclassed to add metadata while remaining interchangeable with a plain string. This can further be used to construct an Enum, similar to StrEnum.

The caveat is that subclasses of str do not get Python compiler optimisations, so this will have an unknown runtime performance impact.

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

No branches or pull requests

1 participant