Could you give a quick explanation why? I thought the whole point of the "extern" specifier was to flag that this variable was defined elsewhere.
Right, but with most platforms' shared library systems, this means "in another source file that is part of the same shared library", not "in another shared library" or "in the main executable".
Otherwise, doesn't it imply that the API pointer is defined in each file that includes arrayobject.h?
i.e. shouldn't headers declare "extern double x" for everything except the file that actually defines x?
If a dynamically loaded module consists of more than one source file, then all but one of them (the one which calls import_array()) must define NO_IMPORT_ARRAY before including arrayobject.h. This is also the answer to Kevin Rodgers' question.
However, it seems to me that the current arrangement in NumPy has another serious drawback: it should be impossible to link NumPy statically with the Python interpreter, due to multiply defined symbols. And I am rather sure that this was possible many versions ago, since I used NumPy on a Cray T3E, which does not have shared libraries.
I checked my own extension modules that export a C API, and they all declare the API pointer static. This is also what the C API export section in the "Extending & Embedding" manual recommends. (OK, I admit that I wrote that section, so that is not a coincidence!)
So perhaps the best solution is to make this static. Client modules that consist of more than one source code file with PyArray... calls must then call import_array() once in every such file, or equivalently pass on the PyArray_API pointer explicitly between the files. That sounds quite acceptable to me.
BTW, extension modules with more than one source file create a risk of portability problems in any case, as the symbols shared between the files must necessarily be global. On platforms such as MacOS, or with static linking, this means they are global to the interpreter and all extension modules, with a resulting risk of name clashes.