2

I can't figure out how to change the value of a parameter passed from Python to C.

PyArg_ParseTuple (args, "Os", &file_handle, &filename)

will let me get file_handle as a PyObject *. Is there a way to change the value file_handle represents? I know I can return multiple values to a Python function call, but that isn't what I want to do in this case. Just for consistency with the C API I am making a module to represent.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • Are you trying to replicate the behavior of a C function that takes a `something *pointer` and assigns to `*pointer`? That's not something Python supports. – user2357112 Feb 14 '18 at 23:25
  • I tried to answer as best I could, but the lack of details here makes me suspect [an XY problem](https://meta.stackexchange.com/q/66377/322040); in general, you shouldn't have resource allocating functions that are also returning other values (if they fail, they raise exceptions), because it becomes quite difficult to properly manage the resources properly (you'd have to unpack, then use a `with` to ensure proper closure). Can you provide more details? – ShadowRanger Feb 14 '18 at 23:39
  • The file handle is a good example. In C, it would be FILE *. I want to pass one back so I was expecting that I could update the reference passed in with a new value (assuming a FILE ** is passed in in the form of a uint64_t * that I would cast back and forth in the C code assuming that the Python programmer doesn't munge it). It seems that this isn't the right thing to do nor really possible without writing all but broken code or code that is flaky at best. – Jay Lofstead Feb 15 '18 at 15:08

1 Answers1

2

You can't change what the caller's parameter refers to in the caller, all you can do is perform mutations of the object itself using its API. Basically, you received a copy of the caller's pointer, not a C++-style reference (nor a C-style double pointer that would give you access to a pointer declared in the caller), so you can't reassign the argument in the caller.

In general, you don't want to try to perfectly reproduce C APIs (I'm assuming your C API uses double-pointers to allow reassigning the value in the caller?) in Python APIs. That's how PHP operates, and it makes for terribly inconsistent APIs that often take no advantage of being in a high level language.

This case is doubly-fraught because, when used properly with with statements, file-like objects actually have multiple references (not C++ meaning) to them, the named variable (that was passed to your function) and one or more hidden references held inside the interpreter (to ensure the with statement has a consistent __exit__ to call, even if the caller deletes their own binding for the object). Even if you could somehow reassign the caller's argument, the with statement would still refer to the original file object, and it wouldn't be obvious to the caller that they needed to close (implicitly using with or explicitly calling close) the result again because your function replaced their object.

Return multiple results (Py_BuildValue makes this easy), and the caller can replace their value if they want to.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271