What is Python looking for when it tries to "import"? - python

Imagine some hypothetical python code:
from turboencabulator import panametricfan
If i try to run this on my computer (Windows, 64-bit, Windows 10), it fails to find the turboencabulator module:
What is it looking for?
When Python is attempting to load a module, what is it looking for? Is it a .py file? Is it a .dll (since this is Windows). Is it a folder?
Windows has modules; they're called assemblies
Windows has the notion of assemblies. If you declare you want to use an assembly called turboencabulator, it will look for an assembly manifest:
turboencabulator.manifest
You can also create a folder called turboencabulator, where it will look for an assemebly manifest there:
turboencabulator
|--- turboencabulator.manifest
That manifest will then in turn declare all the files that make up that assembly.
For example, if i need to use the Microsoft Visual C Runtime 8.0, i would declare a dependency on the module
Microsoft.VC80.CRT
Then Windows will go hunting for:
Microsoft.VC80.CRT.manifest file in the folder
Microsoft.VC80.CRT folder
which then must contain Microsoft.VC80.CRT.manifest
Some other places (like WinSxS, maybe the path, who knows)
And then the manifest is simply a text file that lets you know that msvcrt is comprised of 3 dlls:
<file name="msvcr80.dll" />
<file name="msvcp80.dll" />
<file name="msvcm80.dll" />
What is python looking for?
When i specify i want to import a module:
from RetroEncabulator import GirdleSpring
what is python looking for?
a binary?
a zip?
is it a named item that has been registered in the registry?
the name of a module manifest file that declares all the files that make up the module?
What, exactly, is python looking for?
Bonus Questions
where does it look
can it be configured to look places
can modules be registered somewhere, so it doesn't have to go hunting? (as correctly written Windows applications are supposed to do)
Bonus Chatter
in reality i'm from numpy
in reality i'm from cntk
And neither of these "things" are "found".
Bonus Reading
Python - Modules
Additional Investigation
To contrast the heresay so far, i decided to actually spy on python to see what it goes and looks for:
HKEY_CURRENT_USER/Software/Python/PythonCore/2.7/Modules/turboencabulator
HKEY_LOCAL_MACHINE/Software/Python/PythonCore/2.7/Modules/turboencabulator
D:\DevelopPy\Bunny\turboencabulator
D:\DevelopPy\Bunny\turboencabulator.pyd
D:\DevelopPy\Bunny\turboencabulator.py
D:\DevelopPy\Bunny\turboencabulator.pyw
D:\DevelopPy\Bunny\turboencabulator.pyc
D:\Programs\Python27\DLLs\turboencabulator
D:\Programs\Python27\Lib\turboencabulator
D:\Programs\Python27\Lib\site-packages\turboencabulator
I see it looking in the registry. Does that mean it can be registered?
I see it looking for files besides py (pyc, pyw, pyd). Are those compiled?
I see it looking in the Python\DLLs folder. Does that means that there can be DLLs?
I see it looking in Lib? Are those for intermediate object binaries?
Man, it really has been four years without being able to figure it out. Sure would be super if the SO community was helpful. Well-earned reputation.

When you include import statement in your code, python tries to find the python file or package directory with __init__.py file in it the following order
current working directory
local user site packages
common site packages
You can see the path by the following code
import sys
print(sys.path)
You can also add your own path.
Note: You must add this line before importing other modules
import sys
sys.path.append("your/complete/path/to/directory")

Related

How to simply call a PYD from a wheel installed package?

I'm building a pyd file via pybind11. There's no python-only code in the project (not even in the final wheel). I'm packing this prebuilt pyd into a wheel package. It's all working well so far, but I've got one small issue. Every time I want to use the functions from the pyd file (which is actually the primary "source" of code) I have to type the following: from python_lib_name.pyd_name import pyd_exported_class example: from crusher.crusher import Shard. This looks pretty lame, is there any way to prevent this and let the developer simply use the from crusher import Shard syntax?
Also, I'm not sure what information to provide, obviously, I wouldn't like to throw the whole project at you unnecessarily. I'm glad to provide any details by editing.
how about this: put your pyd in a folder and include a __init__.py that import the stuff from the pyd
my_project
|---my_pyd_lib
| |---my_pyd_lib.pyd
| |---__init__.py
|---#the other stuff
and in that init just put
#__init__.py
from .my_pyd_lib import *
the dot in the import make it a relative import useful to make sure that import the thing in the same folder as that file, it also make sure you import your thing if it happens to share a name with some other package/module you happens to have installed
One way to make a pyd file simply importable is to use the Python interpreter's -m flag. For example, if your file is called foo.pyd and is located in the current directory, you can import it by running the following command:
python -m foo
This will cause the Python interpreter to load the module from foo.pyd and then execute its contents as if they were written in a normal .py file. You can also use this technique to import modules that are located in different directories; just specify the path to the module as an argument to -m.
Another way to make a pyd file simply importable is to convert it into a normal .py file.

Using embedded python, loading a *.pyd outside of DLLs directory fails

I have a C++ application (X-Plane) for which there is a plugin which permits the use of python scripts (XPPython3 plugin). The plugin is written in C, using python CAPI, and works great, allowing me to write python scripts which get executed within the C++ application.
On Windows 10, I want to extend my python features by importing imgui. I have a python cython-built pyd file (_imgui.cp39-win_amd64.pyd).
If I place the pyd file in C\Program Files\Python39\DLLs, it works as expected: C++ application calls CAPI to python, which loads script which imports and executes imgui code.
If I place the pyd file anywhere else, embedded python either reports "module not found" -- if the pyd isn't on sys.path(), or if it is on sys.path():
ImportError: DLL load failed while importing _imgui: The parameter is incorrect.'
Changes using: os.add_dll_directory(r'D:\somewhere\else')
Does not effect whether the module is found or not, nor does it change the 'parameter incorrect' error. (see https://bugs.python.org/issue36085 for details on this change. -- my guess is add_dll_directory changes lookup for DLLs, but not for pyd?) sys.path appears to be used for locating pyd.
Yes, the pyd is compiled with python3.9: I've compiled it both with mingw and with visual studio toolchains, in case that might be a difference.
For fun, I moved python-standard _zoneinfo.pyd from Python39\DLLs and it fails in the same way in embedded python: "The parameter is incorrect". So, that would appear to rule out my specific pyd file.
The key question is/are:
Other than placing a pyd file under PythonXX\DLLs, is there a way to load a PYD in an embedded python implementation? (I want to avoid having to tell users to move my pyd file into the Python39\DLLs directory... because they'll forget.)
Note that using IDLE or python.exe, I can load pyds without error -- anywhere on sys.path -- so they don't have to be under Python39\DLLs. It's only when trying to load from embedded python that the "Parameter is incorrect" appears. And of course, this works flawlessly on Mac.
(Bonus question: what parameter? It appears to be python passing through a windows error.)
There seems to be a simple answer, though I suspect it's better characterized as a python bug.
There is nothing magical about Python39\DLLs directory.
The problem is using absolute vs relative paths in sys.path.
Python can find modules using absolute or relative paths. So if zippy.py is in folder foobar,
sys.path.append('foobar')
import zippy
# Success
Python and find, BUT NOT LOAD pyd files using relative paths. For example, move _zoneinfo.pyd from PythonXX\LDDs to foobar
sys.path.append('foobar')
import _zoneinfo
# ImportError: DLL load failed while importing _zoneinfo: The parameter is incorrect.'
Instead, use absolute path, and it will find and load PYD:
sys.path.append(r'c:\MyTest\foobar')
import _zoneinfo
# Success
So, there is actually a way to do this—that is, ship your application with the desired libraries. The solution is to use an embedded distribution and ship this with your application. You can find the correct distribution on the official Python download page corresponding to your desired version (here's the link to the lastest 3.9 release which seems to be what you're using: https://www.python.org/downloads/release/python-392/). Look for the Windows Embeddable Package.
You can then simply drop in your .pyd file alongside the standard library files (note that if your third-party library is dependent on any other libraries, you will have to include them, as well). Shipping your application with an embeddable distribution should not only solve your current issue, but will also mean that your application will work regardless of which version of Python a user has installed (or without having Python installed at all).

Is it possible to specify the search path for a module in a python script? If it is, how do I do that?

I have been coding in python for about 2 months, but I'm only familiar with basic object-oriented programming, so I do not really understand things like how searching for modules is implemented. (Basically I'm a noob.)
I pip installed a package called Opentrons Opentrons 2.5.2 and all its dependencies into the samefolder as a python script I'm currently writing. However when I tried to import the module below[1], I get an error saying that "Opentrons is not a module". Then, I tried shifting it into the python library because I found out the search path using the pprint module and it seems to work. I was wondering if I can specify the search path from the .py file itself instead of manually printing the search path and putting the file into the library that the script searches for. (Willing to put in images of the directories I put the opentrons package in if it helps.)
[1]
import sys
import pprint
pprint.pprint(search.path)
from opentrons import robot, containers, instruments
Edit: I realise that the fact that I am running all my scripts in a Spyder console located in a python 3.6 environment might be important.
You can try using the __import__ function, or importlib. This should allow you to specify the path.

Importing dependencies works in Pycahrm not Terminal?

I used below solution for importing dependencies.
I found this solution works if I run the code in Pycharm but not in Terminal.
The error message in Terminal is "cannot find graphics.primitive".
I'm using Mac and Python 3.5.
Why I see different behaviors from the Terminal and Pycharm?
How may I make the solution work for both?
http://chimera.labs.oreilly.com/books/1230000000393/ch10.html#_solution_169
Making a Hierarchical Package of Modules
Problem
You want to organize your code into a package consisting of a hierarchical collection of modules.
Solution
Making a package structure is simple. Just organize your code as you wish on the file-system and make sure that every directory defines an init.py file. For example:
graphics/
__init__.py
primitive/
__init__.py
line.py
fill.py
text.py
formats/
__init__.py
png.py
jpg.py
Once you have done this, you should be able to perform various import statements, such as the following:
import graphics.primitive.line
from graphics.primitive import line
import graphics.formats.jpg as jpg
You need to make sure that the graphics package is in the Python search path. PyCharm does this by extending sys.path as follows:
import sys
sys.path.extend(['/Users/hackworth/Development/graphics_parent_dir', '/Applications/PyCharm.app/Contents/helpers/pycharm', '/Applications/PyCharm.app/Contents/helpers/pydev'])
You can do the same in your code replacing /Users/hackworth/graphics_parent_dir with the appropriate path, or you can include the full path to graphics_parent_dir in the PYTHONPATH environment variable. See the Python documentation for details.
Another option would be to place the graphics package into a location the is searched by default on your system.

Fail python import from another folder

I am experimenting with python, mostly troubleshooting other people's code. I am trying to get a program to run, "path\folderA\program.py".
I am running the program from path\folderA
I am getting an error:
ImportError: No module named fff.ggg.ppp
program.py contains an import:
from fff.ggg.ppp import mmm
In the folder "path\folderB" there are:
"path\folderB\fff\__init__.py"
"path\folderB\fff\ggg"
folder ggg also contains __init__.py, as well as program ppp.py
From reading other posts, like Python error "ImportError: No module named" I understand that having the __init__.py makes a folder a "package" which makes imports from it possible - but it doesn't work, since I am getting an error.
This has been working for other people that worked with these projects, so there is something wrong with my setup.
I read something about the directories having to be in the sys.path. Does that mean I have to add them to the environment variable path ? That would mean adding a lot of directories to the PATH though, so it can't be.
So I also found the following:
import sys
sys.path.append( <path to FolderB> )
But that means changing the code (which has not been necessary for other people) and hard-coding a path to what it is on my local machine - which I shouldn't have to, right ?
I can't visualize it - apparently I am not supposed to change the code and hard-code the physical path to the import module - so how can a program from folderA even know to look in folderB for an import ?
How does the magic of __init__.py work ?
I can't visualize it - apparently I am not supposed to change the code
and hard-code the physical path to the import module - so how can a
program from folderA even know to look in folderB for an import ?
You are correct. Somehow you have to tell python to look for imported modules in folderB. There is no __init__.py magic that lets you import from other folders on your hard drive.
Usually, if you've got various different python packages like that, they work by being installed into python's library. That way they can imported from anywhere. This is usually accomplished by a setup.py script. Check if folderB has one. Run it with python setup.py install.
If that doesn't work, we'll need more information about how this code is structured.
Folder B must be on the sys.path, so you would either need to move mmm to A, or modify sys.path from within A (not sure if that works). __init__.py tells python that the folder is a package, so you could have folders with __init__.py within folders with __init__.py and python treats the folders inside as parts of the parent folder. Check out sympy or almost any large python library and you will find such a structure. It can also contain code to be run on import, but can also be empty.

Categories