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

Enumeration enhancements #35

Open
Praecordi opened this issue Apr 13, 2024 · 5 comments
Open

Enumeration enhancements #35

Praecordi opened this issue Apr 13, 2024 · 5 comments
Labels
enhancement New feature or request runtime

Comments

@Praecordi
Copy link

Great work with this project so far. I just got started, and it's working great so far. Can't wait to create some really great stuff with this. I had a quick suggestion. Currently, if a checkpoint, vae, or lora, etc. are located in a file they appear as file_path_{checkpoint/vae/lora/etc}_name. Since these enumerations are created dynamically, would it be possible to represent the file structures as nested enumerations?

Why is this convenient? For example, I, and I'm sure many others organize their checkpoints into folders. Namely, I have a folder for inpainting models, and SDXL and SD1.5 models. I would love to be able to access models with
Checkpoints.sd15.dreamshaper8 rather than Checkpoints.sd15_dreamshaper8. The former looks much nicer, and even more importantly, aids in looping, say if I want to loop over all my SD1.5 checkpoints. What do you think?

@Chaoses-Ib Chaoses-Ib added enhancement New feature or request runtime labels Apr 13, 2024
@Chaoses-Ib
Copy link
Owner

Yeah, nested enums are in the plan. The reason I didn't make them before is enums cannot be directly nested. For example, the following code doesn't work:

import enum

class Model(enum.Enum):
    ModelA = 'a'
    ModelB = 'b'

    class SDXL(enum.Enum):
        ModelC = 'c'
        ModelD = 'd'

Model.SDXL.ModelC
# AttributeError: 'Model' object has no attribute 'ModelC'

Instead, the child enum must be declared outside of the parent enum, and be accessed through .value:

import enum

class _SDXL(enum.Enum):
   ModelC = 'c'
   ModelD = 'd'

class Model(enum.Enum):
   ModelA = 'a'
   ModelB = 'b'

   SDXL = _SDXL
   
Model.SDXL.value.ModelC
# <_SDXL.ModelC: 'c'>

This is not very ergonomic and I want a better solution. Maybe some third-party libraries like aenum can help.

@Chaoses-Ib
Copy link
Owner

By the way, you can loop over models in a directory without nested enums like this:

for model in Checkpoints:
    if model.startswith('sd15\\'):
        print(model)

# or:
for model in [model for model in Checkpoints if model.startswith('sd15\\')]:
    print(model)

@Praecordi
Copy link
Author

Hi, Thanks for the workaround for now. You're right, this is an issue with Python itself. Although is it necessary to use Enums? They're an extension of the base class, so you can probably define a class that acts as an enum. This was suggested in this stackoverflow question. Another suggestion in the same thread was using dataclasses, but I don't see much value for using those in this case.

@Chaoses-Ib
Copy link
Owner

The main reason Enum is used is to be able to iterate over members, as shown above. Although writing a custom class with __iter__ over __dict__ isn't too complex, using an enum library is still slightly better as it provides more functions.

I've found that Python 3.11 has added a nonmember() decorator that can solve our problem:

import enum

class Model(enum.Enum):
    ModelA = 'a'
    ModelB = 'b'

    @enum.nonmember
    class SDXL(enum.Enum):
        ModelC = 'c'
        ModelD = 'd'

Model.SDXL.ModelC
# <SDXL.ModelC: 'c'>

nonmember is also the default behavior since Python 3.13 (python/cpython#78157). So this is the "standard" solution.

However, requiring 3.11 could be too strict for most users. Using aenum may be the best choice for now.

@Praecordi
Copy link
Author

I agree. Switching to 3.11 on my computer would break a lot of existing code for me. And I sorely regret not having some management for my python versions.

I think aenum is the best option as well, if the additional dependency is not a big issue for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request runtime
Projects
None yet
Development

No branches or pull requests

2 participants