pythonenumsmixinspathlib

How to create a Mixin Enum


I'm trying to create an enum with a pathlib.Path mixin, MCVE:

import enum, pathlib
class Test(pathlib.Path, enum.Enum):
    path1 = pathlib.Path('/path1')
    path2 = pathlib.Path('/path2')

which gives a totally cryptic error:

      1 import enum, pathlib
----> 2 class Test(pathlib.Path, enum.Enum):
      3     path1 = pathlib.Path('/path1')
      4     path2 = pathlib.Path('/path2')

File ~/anaconda3/envs/main/lib/python3.9/enum.py:288, in EnumMeta.__new__(metacls, cls, bases, classdict, **kwds)
    286         enum_member._value_ = value
    287 else:
--> 288     enum_member = __new__(enum_class, *args)
    289     if not hasattr(enum_member, '_value_'):
    290         if member_type is object:

File ~/anaconda3/envs/main/lib/python3.9/pathlib.py:1082, in Path.__new__(cls, *args, **kwargs)
   1080 if cls is Path:
   1081     cls = WindowsPath if os.name == 'nt' else PosixPath
-> 1082 self = cls._from_parts(args, init=False)
   1083 if not self._flavour.is_supported:
   1084     raise NotImplementedError("cannot instantiate %r on your system"
   1085                               % (cls.__name__,))

File ~/anaconda3/envs/main/lib/python3.9/pathlib.py:707, in PurePath._from_parts(cls, args, init)
    702 @classmethod
    703 def _from_parts(cls, args, init=True):
    704     # We need to call _parse_args on the instance, so as to get the
    705     # right flavour.
    706     self = object.__new__(cls)
--> 707     drv, root, parts = self._parse_args(args)
    708     self._drv = drv
    709     self._root = root

File ~/anaconda3/envs/main/lib/python3.9/pathlib.py:700, in PurePath._parse_args(cls, args)
    695         else:
    696             raise TypeError(
    697                 "argument should be a str object or an os.PathLike "
    698                 "object returning str, not %r"
    699                 % type(a))
--> 700 return cls._flavour.parse_parts(parts)

File ~/anaconda3/envs/main/lib/python3.9/enum.py:429, in EnumMeta.__getattr__(cls, name)
    427     return cls._member_map_[name]
    428 except KeyError:
--> 429     raise AttributeError(name) from None

AttributeError: _flavour

What am I doing wrong?


Solution

  • There are two issues going on here -- one is that when you mix in a class, the contents for the member definition line should be the mixin's arguments; in other words, instead of

        path1 = pathlib.Path('/path1')
    

    you should have

        path1 = '/path1'
    

    At this point you hit the second issue, which is found in the Path implementation:

        if cls is Path:
            cls = WindowsPath if os.name == 'nt' else PosixPath
    

    and cls is Test, so neither WindowsPath nor PosixPath is chosen.

    The easiest solution for that is to inherit from WindowsPath or PosixPath beforehand, and use that in the enum header:

    BasePath = pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath
    class Test(BasePath, enum.Enum):
        path1 = '/path1'
    

    and in use:

    >>> Test
    <enum 'Test'>
    
    >>> list(Test)
    [<Test.path1: PosixPath('/path1')>]
    

    Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.