tags:

views:

161

answers:

4

What's the correct way to prevent invoking (creating an instance of) a C type from Python?

I've considered providing a tp_init that raises an exception, but as I understand it that would still allow __new__ to be called directly on the type.

A C function returns instances of this type -- that's the only way instances of this type are intended to be created.

Edit: My intention is that users of my type will get an exception if they accidentally use it wrongly. The C code is such that calling a function on an object incorrectly created from Python would crash. I realise this is unusual: all of my C extension types so far have worked nicely when instantiated from Python. My question is whether there is a usual way to provide this restriction.

+1  A: 

Don't prevent them from doing it. "We're all consenting adults here."

Nobody is going to do it unless they have a reason, and if they have such a reason then you shouldn't stop them just because you didn't anticipate every possible use of your type.

Kiv
That's true of Python code, but less true of C extensions, where it is important that you do enough checking to prevent the interpreter from crashing or getting into an inconsistent/undefined state.
Miles
+1  A: 

"The type is a return type of another C function - that's the only way instances of this type are intended to be created" -- that's rather confusing. I think you mean "A C function returns instances of this type -- that's the only way etc etc".

In your documentation, warn the caller clearly against invoking the type. Don't export the type from your C extension. You can't do much about somebody who introspects the returned instances but so what? It's their data/machine/job at risk, not yours.

[Update (I hate the UI for comments!)]

James: "type ...just only created from C": again you are confusing the type and its instances. The type is created statically in C. Your C code contains the type and also a factory function that users are intended to call to obtain instances of the type. For some reason that you don't explain, if users obtain an instance by calling the type directly, subsequent instance.method() calls will crash (I presume that's what you mean by "calling functions on the object". Call me crazy, but isn't that a bug that you should fix?

Re "don't export": try "don't expose".

In your C code, you will have something like this where you list out all the APIs that your module is providing, both types and functions:

static struct PyMethodDef public_functions[] = {
   {"EvilType", (PyCFunction) py_EvilType, ......},
   /* omit above line and punters can't call it directly from Python */
   {"make_evil", (PyCFunction) py_make_evil, ......},
   ......,
   };

module = Py_InitModule4("mymodule", public_functions, module_doc, ...
John Machin
I'm not sure what you mean by not exporting the type. I want the type to be usable, just only created from C.
James Hopkin
Just to slightly explain further: really it's just a case of an type that doesn't make sense to be invoked from Python, since it needs resources only the C library can provide. Currently my code will crash if an uninitialised instance's method is called. One option I have is to keep an 'initialised' member, and raise an exception in every method if it's false. I'm looking for alternatives, partly to get the earliest warning to the user.
James Hopkin
The type lives in, is statically defined in, the C code. Where it's called from is irrelevant. You have one factory function (intended to be called from Python code) that gets memory for a new instance, initialises that instance using resources from the C library, and returns the instance to the caller. Why does it make sense for the factory function to be invoked from Python code and not the type? Why does it not make sense to lose the factory function and have the type do the lot with tp_new and tp_init functions doing what they are intended for?
John Machin
A: 

There is a fantastically bulletproof way. Let people create the object, and have Python crash. That should stop them doing it pretty efficiently. ;)

Also you can underscore the class name, to indicate that it should be internal. (At least, I assume you can create underscored classnames from C too, I haven't actually ever done it.)

Lennart Regebro
I wouldn't call having my library crash the interpreter 'bulletproof' :-)
James Hopkin
+2  A: 

Simple: leave the tp_new slot of the type empty.

>>> Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create 'foo.Foo' instances
>>> Foo.__new__(Foo)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object.__new__(foo.Foo) is not safe, use foo.Foo.__new__()

If you inherit from a type other than the base object type, you will have to set tp_new to NULL after calling PyType_Ready().

Miles
I'm going to give this a try. +1 for now - I'll acccept the answer when I've given it a test.
James Hopkin