codebrainz / geanypy

Python bindings for the Geany plugin API.
http://codebrainz.github.com/geanypy
GNU General Public License v2.0
45 stars 17 forks source link

Importerror with multi module plugin #17

Open deusyss opened 9 years ago

deusyss commented 9 years ago

Hi,

I'm trying to create a plugin. I made a module for UI, one for database and one for funcionalities. All that modules are import in main module (with geany.plugin).

If i want to import correctly these module i have to import them into init of class which inherit from geany.plugin.

I attach my beginning plugin with the error (change the extension to zip).

I'm working under Linux Mint , and my plugin is under .conf directory in my home.

gearcos

frlan commented 9 years ago

I'm having also issues here. I have a class file in combination with an init.py inside a subfolder, but importing scripts seems not to recognize it. Could it be possible, that module search path due to the way of handling Python file maybe is screwed up?

elextr commented 9 years ago

Probably the geany conf dir is not in Python's sys.path. The sys.path will probably start with where the geanypy Python script was loaded from. You can try printing sys.path from a pyplugin to see.

If thats the case you can just add an appropriate dir to sys.path.

frlan commented 9 years ago

Geany config dir is really not inside. Only geanypy related path is /usr/local/lib/geany-plugins/geanypy. From my understanding, folder of file should be also part of this. Can this being done by geanypy via default?

elextr commented 9 years ago

The problem is Geanypy loads plugins itself, it doesn't use Python import so no paths are added to sys.path. The potential problem of adding the plugin directory to sys.path is that it is a global, ie any value added for one plugin will affect the load path of another. The best way is for a plugin to append what it needs to sys.path possibly using the directory part of its __FILE__ , import its modules and then restore sys.path so it won't affect other plugins.

As an aside, plugins should be aware that they are sharing a Python instance, so loading stuff in the global namespace (like modules) may also clash with another plugin.

codebrainz commented 9 years ago

FWIW, I think this is the only sys.path modifications, needed in order to be able to import the geany module. As @elextr mentioned, the loader "manually" imports plugins by file path, like this.

frlan commented 9 years ago

Not sure whether it's the best solution as it looks little hackish to me: I'm try to succeed with something like that

import sys import os sys.path.append(os.path.dirname(file)) import my local modules sys.path.remove(os.path.dirname(file))

for the meanwhile. Will keep you updated whether it a working solution.

elextr commented 9 years ago

I agree that the Python import system is quite annoying, and quite the most poorly documented part of the language, the package layout and so on being in PEPs only referred to from the manual, so searching the manual finds nothing about it. (end rant, and Python3 docs are a bit better :)

What it might be possible to do is to have the system and user geanypy plugin directories set up as modules (by having an empty __init__.py in them). Geanypy could load them as modules with known names eg system_plugins and user_plugins. Then the plugins can load stuff in their directory and below by

import user_plugins.franks_plugin.module.whatever

I have used this technique on Python 3, but I'm not sure if it works on Python 2.

And as Enrico would say "somebodys go to do it, PRs are welcome" :)

deusyss commented 9 years ago

Hi, thanks to watch this issue. I use Python 2. For the moment, i juste have to import the module once, but i think it could be a problem for another dev.

ghost commented 9 years ago

Can I suggest a possibly safer way to import your modules:

self.testmodule = imp.load_source('TestModule',os.path.dirname(__file__)).TestModule()

os is already imported, so this is a one liner to create an instance of your class. You need to import imp.

the path can be appended to the base plugin dir

self.testmodule = imp.load_source('TestModule', os.path.join(os.path.dirname(__file__), 'TestModule') ).TestModule()

Hope this helps someone

deusyss commented 9 years ago

Hi robblue2x,

Thanks for this information. I didn't know imp, but after doc reading, it seems very nice. Thank you for this tip :)