Import Namespace

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Samstag 1. November 2008, 10:20

Ganz einfache Frage: Kann ich ein bestimmtes Modul in den "Import Namespace" einfügen? Also, das ein anderes Modul dieses Modul beim Versuch des Imports findet, ohne das ich sys.path verändere?

Hintergrund ist mein Plugin System. Jedes Plugin sollte eine Klasse enthalten können, die von einer Basisklasse abgeleitet ist. Diese Basisklasse möchte ich natürlich ungern direkt zu den Plugins schieben, da sie selbst alles nötige implementieren sollte, um für den Rest des Programmes benutzbar zu sein, und man bei jedem Plugin natürlich vom Engine-Code separiert arbeitet. Zu sys.path hinzufügen, kurz bevor mein Plugin System das Modul importiert, möchte ich auch ungerne, da das einen Zugriff auf ~50% der Engine Funktionalitäten erlauben würde und zugleich fehleranfällig wäre (Sich ändernde Paths, beschränkt auf den Path des Plugin Systems, etc.).
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Samstag 1. November 2008, 10:23

Du kannst die Umgebungsvariable ``PYTHONPATH`` verwenden, aber sonderlich toll ist das auch nicht. Vielleicht reicht dir ja ein Import-Hook?

Übrigens muss ich sagen, dass deine Einwände ``sys.path`` zu erweitern mich eigentlich nicht überzeugen.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Samstag 1. November 2008, 10:32

Nun, würde ich sys.path verändern, müsste ich erstmal den Pfad, in dem das Modul liegt, herausfinden. Ginge das über os.path.dirname(__file__)? Und ist das garantiert? Bisher erschien mir __file__ manchmal mit Pfadangabe, manchmal ohne. Ansonsten habe ich in os.path nichts gefunden (und os.getcwd fällt wohl flach).

Wenn das garantiert wäre, könnte ich sys.path verwenden, und würde es wohl auch tun.

Import-Hook:

Kann ich (noch) nichts mit anfangen, werde mal danach suchen.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Samstag 1. November 2008, 11:50

str1442 hat geschrieben:Nun, würde ich sys.path verändern, müsste ich erstmal den Pfad, in dem das Modul liegt, herausfinden. Ginge das über os.path.dirname(__file__)? Und ist das garantiert? Bisher erschien mir __file__ manchmal mit Pfadangabe, manchmal ohne.
Den Pfad herauszufinden hat bei mir eigentlich immer funktioniert, siehe wie das Plugin System von What's On Air das macht.

Eine andere Idee für Plugins weren die setuptools-Entrypoints. Damit habe ich selbst noch nichts gemacht, weil es damals(TM) noch keine setuptools gab.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Samstag 1. November 2008, 12:07

Das bisherige Plugin System (nur der Import Teil) sieht so aus:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import with_statement

import imp

DESCRIPTION = (r".py", r"U", imp.PY_SOURCE)

ALL = []

def load_plugin(name, full_path):
    with open(full_path, DESCRIPTION[1]) as file_obj:
        imp.acquire_lock()

        try:
            new_plugin_module = imp.load_module(name, file_obj,
                                                full_path,
                                                DESCRIPTION)
        finally:
            imp.release_lock()

    ALL.append(new_plugin_module)

    return new_plugin_module
und funktioniert, wobei die einzelnen Plugins und deren Namen + Pfade in einer CSV Datei verwaltet werden.

Ich habe mir What's on Air mal angeschaut, und da wird os.path.dirname(__file__) ja auch verwendet, wird wohl also funktionieren, wenn ich das noch in gescheid' kapsele. Danke für die Hilfe.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Samstag 1. November 2008, 12:10

str1442 hat geschrieben:Ginge das über os.path.dirname(__file__)? Und ist das garantiert?
Hallo str!

Natürlich geht das damit. Allerdings musst du zusätzlich ``os.path.abspath()`` mitverwenden um plattformübergreifend, garantiert den Pfad zurück zu erhalten.

Code: Alles auswählen

os.path.dirname(os.path.abspath(__file__))
Z.B.:

Code: Alles auswählen

APPDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, APPDIR)
mfg
Gerold
:-)

PS: http://www.python-forum.de/post-64471.html#64471

.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Qubit
User
Beiträge: 75
Registriert: Dienstag 7. Oktober 2008, 09:07

Samstag 1. November 2008, 14:45

str1442 hat geschrieben:Ganz einfache Frage: Kann ich ein bestimmtes Modul in den "Import Namespace" einfügen? Also, das ein anderes Modul dieses Modul beim Versuch des Imports findet, ohne das ich sys.path verändere?
Meinst du vielleicht sowas..

Code: Alles auswählen

from __future__ import with_statement 
import sys,imp

class Plugins(object):

    __full_path ='C:\\Users\\Qubit\\pytest\\'
    __modules = set()

    DESCRIPTION = (r".py", r"U", imp.PY_SOURCE)
    ALL=[]

    @classmethod
    def add(cls, mod_name):
       cls.__modules.add(mod_name)

    def find_module(self, mod_name, package_path):
       if package_path: return
       if mod_name in self.__modules: return self

    def load_module(self, mod_name):
        file_name = self.__full_path+'Plugins_'+mod_name+'.py'
        with open(file_name, self.DESCRIPTION[1]) as file_obj:
            imp.acquire_lock()
            try:
                plugin_module = imp.load_module(mod_name, file_obj,
                                                file_name,
                                                self.DESCRIPTION)
            finally:
                imp.release_lock()
        self.ALL.append(plugin_module)
        print "Imported:", mod_name
        return plugin_module 



sys.meta_path.append(Plugins())
Plugins.add("Test")

>>> dir()
['Plugins', '__builtins__', '__doc__', '__name__', 'imp', 'sys', 'with_statement']
>>> from Test import Test
Imported: Test
>>> dir()
['Plugins', 'Test', '__builtins__', '__doc__', '__name__', 'imp', 'sys', 'with_statement']
>>> Test.me()
Hello, Plugins_Test
>>> 'C:\\Users\\Qubit\\pytest' in sys.path
False
>>> 
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Samstag 1. November 2008, 15:14

@gerold:

Danke für die Erläuterungen! Ich war mir nur nicht ganz sicher, da __file__ ja nicht unbedingt auf "path" hindeutet, und ich einmal __file__ einmal mit und einmal ohne path gesehen habe.

@Qubit:

Nein, ich meinte es eigentlich genau andersrum :D.

Also das ein Plugin, das mithilfe des Plugin Systems importiert werden soll, per "import xyz" xyz importieren kann, obwohl der Pfad von xyz nicht in sys.path ist (also auch nicht "." ist).

Ich werde aber nun wohl doch den Weg über sys.path gehen, ich muss nur noch sehen, ob ich eine Art "importable_from_plugins" Directory anlege, und dort "dummy Module" reinlege, oder direkt auf das richtige Package verlinke, je nachdem, was besser gekapselt und sauber wirkt.

Jetzt schaue ich mir noch sys.meta_path an :D.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Sonntag 2. November 2008, 14:52

Jetzt schaue ich mir noch sys.meta_path an
In der Doku steht dazu nichts :?:

EDIT:
sys.meta_path is a list of importer objects that will be traversed before sys.path is checked. This list is initially empty, but user code can add objects to it. Additional built-in and frozen modules can be imported by an object added to this list.
Stand in "Whats new in Py 2.3", wobei dabei nicht stand, wie genau die Objekte "traversiert" werden.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Mittwoch 15. April 2009, 08:14

Da das hier so gut reinpasst:

Man kann auch selbst ein Modul bereitstellen per sys.modules. Man könnte zb sowas machen:

Code: Alles auswählen

import sys

from types import ModuleType

plugin_mod = ModuleType("imaginary_plugin")
plugin_mod.Plugin = <Plugin Klasse>

sys.modules[plugin_mod.__name__] = plugin_mod
Bei einem import wird dieses Modul dann immer gefunden. Eine andere Möglichkeit ist, die "Plugin" Klasse dem "builtins" Modul hinzuzufügen, wodurch man immer Zugriff darauf hat.

Das sind aber mehr oder weniger Hacks, die man vermeiden sollte - zumindest zweiteres ist das definitiv. Die erste Möglichkeit ist imho grenzwertig.
Antworten