2

I have an enum in Python (backported enum package to 2.7) that is meant to be of only integers:

import enum
class MyEnum(enum.Enum):
  val = 0

Let's say I receive a PyObject * in a C extension pointing to MyEnum.val. I want the integer value associated with the PyObject *. How do I get it most succinctly?

user
  • 4,920
  • 3
  • 25
  • 38
  • As far as I know, the `enum34` package doesn't provide any special C representation or API, so you just have to call `PyObject_GetAttr` and friends, as with any other pure-Python type. – abarnert May 10 '15 at 07:10
  • Does it provide anything that'd let me unify code-paths for handling them alongside objects `x` for which `PyInt_Check(x) != 0`? e.g. support for built-in protocols, something like `PyNumber_Int(x)` (if that actually worked). – user May 10 '15 at 07:13
  • 1
    `Enum` values are explicitly designed _not_ to act like they're a subtype of `int`. `IntEnum` values are another story. You should be able to `PyInt_Check` those and `PyInt_AsLong` them. But for `Enum`, you shouldn't be able to, so you (at least should) need to get the `value` attr if you want to use them as ints, just as you do from Python. – abarnert May 10 '15 at 07:18
  • Using `enum.IntEnum` on the Python-side instead of `enum.Enum` does seem to be the most convenient path to take then. Thanks. – user May 10 '15 at 07:21
  • 2
    Well, it's convenient if it's what you actually want. Two different `IntEnum` types' constants will compare equal to each other—e.g., `Colors.red == Days.sunday` will be true. The [`IntEnum`](https://docs.python.org/3/library/enum.html#intenum) docs explain why usually, that's _not_ what you want… but of course sometimes it is, which is why they included it in the module. – abarnert May 10 '15 at 07:24
  • It is; I failed to elucidate that in the original question formulation. I've edited the question to clarify the intent. – user May 10 '15 at 07:36
  • Even if the values are all integers, that usually doesn't mean you want an `IntEnum`. The docs I linked explain it better than I can. – abarnert May 10 '15 at 07:44
  • The enumeration is meant to mirror integers coming from a spec. The extra fluff of identity carried by `enum.Enum` doesn't seem to be what I want. – user May 10 '15 at 07:47
  • OK, so you're basically using them like C `#define` macros? In that case, yeah, `IntEnum` is effectively that with better debugging output. – abarnert May 10 '15 at 07:48

1 Answers1

3

Looking at the source to the enum34 backport, just like the enum module in 3.4+, it's pure Python, and does nothing to expose a custom C API.

So, you just use PyObject_GetAttr and friends to access class attributes. In particular, if you have a MyEnum.val, you need to get its value attribute, which will be an int, which you can then PyInt_AsLong.

This is the same way things work in Python. If you try to use MyEnum.val where an int is expected, you should get a TypeError; if you try to explicitly call int(MyEnum.val), you will definitely get a TypeError. So, although I haven't tested it, PyInt_AsLong directly on the constant instead of its value should raise a TypeError and return -1.

If you want enumeration constants that act like subtypes of int, then, as the enum docs explain, you want IntEnum. Usually, that isn't really what you want (as the docs explain), but if it is, of course it works. And you should be able to PyInt_Check and PyInt_AsLong an IntEnum value (although, again, I haven't tested).

abarnert
  • 354,177
  • 51
  • 601
  • 671