[PATCH] Re: Prepare dynamic plugins (again)
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
From: Christoph Höger <choeger@xxxxxxxxxxxxxxx>
Here we go again, this time much smaller and without the
GnomeUI.
This patch allows ui plugins to be dropped only
inside the ui/plugins folder and work without modifying
any other file. This should be good for packagers.
* config:
Old config options like "Curses.Blinkenlights" still work
New ui plugins can use Modulename only if they provide a
getUIClass() method, I think thats a compromise
* default ui:
I have made Curses.Blinkenlights the default ui, but
perhaps TTY.TTYUI is a better choice for that.
* testing:
I tested against Blinkenlights, TTY and Gnome ui (which is not
yet in the mainline branch)
Signed-off-by: Christoph Höger <choeger@xxxxxxxxxxxxxxx>
---
NOTICE: I'm not the author of this patch. The first line in the body
"From: Christoph..." should make Git set the good author if taken with
'git am'.
HACKING | 11 +++++++
offlineimap/ui/__init__.py | 17 -----------
offlineimap/ui/detector.py | 35 +++++++++++++----------
offlineimap/ui/{ => plugins}/Blinkenlights.py | 8 +----
offlineimap/ui/{ => plugins}/Curses.py | 6 ++--
offlineimap/ui/{ => plugins}/Machine.py | 4 +--
offlineimap/ui/{ => plugins}/Noninteractive.py | 6 ++--
offlineimap/ui/{ => plugins}/TTY.py | 2 +-
offlineimap/ui/plugins/__init__.py | 18 ++++++++++++
setup.py | 3 +-
10 files changed, 61 insertions(+), 49 deletions(-)
create mode 100644 HACKING
rename offlineimap/ui/{ => plugins}/Blinkenlights.py (96%)
rename offlineimap/ui/{ => plugins}/Curses.py (99%)
rename offlineimap/ui/{ => plugins}/Machine.py (98%)
rename offlineimap/ui/{ => plugins}/Noninteractive.py (93%)
rename offlineimap/ui/{ => plugins}/TTY.py (98%)
create mode 100644 offlineimap/ui/plugins/__init__.py
diff --git a/HACKING b/HACKING
new file mode 100644
index 0000000..8c6fb19
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,11 @@
+Developing your own UI
+======================
+
+You can develop your own plugin by simply subclassing the UIBase class from
offlineimap.ui.UIBase (have a look at TTYUI for a basic understanding of that).
+In difference to versions <= 6.2.0 you can now easily activate your plugin by
putting it into the offlineimap/ui/plugins folder. This works without any other
preparation. So if your module is named foo.py and your UI class is Bar the
user can now use it with offlineimap -u foo.Bar
+If you want to make the life of the user even easier, just add a getUIClass()
method in your module, let's say:
+
+ # note: you return a class, not the instance!
+ def getUIClass(): return Bar
+
+Then the user can run offlineimap -u foo even without knowing about Bar
(allowing for multiple UI Classes in one module).
diff --git a/offlineimap/ui/__init__.py b/offlineimap/ui/__init__.py
index 0206ab4..081c1fc 100644
--- a/offlineimap/ui/__init__.py
+++ b/offlineimap/ui/__init__.py
@@ -16,23 +16,6 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import UIBase, Blinkenlights
-try:
- import TTY
-except ImportError:
- pass
-
-try:
- import curses
-except ImportError:
- pass
-else:
- import Curses
-
-import Noninteractive
-import Machine
-
# Must be last
import detector
diff --git a/offlineimap/ui/detector.py b/offlineimap/ui/detector.py
index 4ec7503..d73c9d0 100644
--- a/offlineimap/ui/detector.py
+++ b/offlineimap/ui/detector.py
@@ -19,17 +19,8 @@
import offlineimap.ui
import sys
-DEFAULT_UI_LIST = ('Curses.Blinkenlights', 'TTY.TTYUI',
- 'Noninteractive.Basic', 'Noninteractive.Quiet',
- 'Machine.MachineUI')
-
def findUI(config, chosenUI=None):
- uistrlist = list(DEFAULT_UI_LIST)
- namespace={}
- for ui in dir(offlineimap.ui):
- if ui.startswith('_') or ui in ('detector', 'UIBase'):
- continue
- namespace[ui]=getattr(offlineimap.ui, ui)
+ uistrlist = ["Curses.Blinkenlights"]
if chosenUI is not None:
uistrlist = [chosenUI]
@@ -37,7 +28,7 @@ def findUI(config, chosenUI=None):
uistrlist = config.get("general", "ui").replace(" ", "").split(",")
for uistr in uistrlist:
- uimod = getUImod(uistr, config.getlocaleval(), namespace)
+ uimod = getUImod(uistr)
if uimod:
uiinstance = uimod(config)
if uiinstance.isusable():
@@ -45,10 +36,24 @@ def findUI(config, chosenUI=None):
sys.stderr.write("ERROR: No UIs were found usable!\n")
sys.exit(200)
-def getUImod(uistr, localeval, namespace):
+def getUImod(uistr):
+ # ensure backwards compatibility with configs from <= 6.1
+ # uiClassName and dot will be empty if no . is found
+ uimodName,dot,uiClassName=uistr.partition(".")
+
+ # this asserts that all elements are in place
try:
- uimod = localeval.eval(uistr, namespace)
+ # get the module from the plugin path
+ uimod = __import__("offlineimap.ui.plugins." +
uimodName,[],[],[uimodName])
+ if not uiClassName == "":
+ # if uiClassName is set, we use the old method (introspection
+ # rocks!)
+ uiClass=uimod.__dict__[uiClassName]
+ else:
+ # asking the plugin for the class name is less error prone
+ uiClass = uimod.getUIClass()
except (AttributeError, NameError), e:
- #raise
+ print e
+ #raise
return None
- return uimod
+ return uiClass
diff --git a/offlineimap/ui/Blinkenlights.py
b/offlineimap/ui/plugins/Blinkenlights.py
similarity index 96%
rename from offlineimap/ui/Blinkenlights.py
rename to offlineimap/ui/plugins/Blinkenlights.py
index dcc4e01..6982351 100644
--- a/offlineimap/ui/Blinkenlights.py
+++ b/offlineimap/ui/plugins/Blinkenlights.py
@@ -127,15 +127,11 @@ class BlinkenBase:
return tf
finally:
s.tflock.release()
-
- def callhook(s, msg):
- s.gettf().setcolor('white')
- s.__class__.__bases__[-1].callhook(s, msg)
- def sleep(s, sleepsecs, siglistener):
+ def sleep(s, sleepsecs):
s.gettf().setcolor('red')
s.getaccountframe().startsleep(sleepsecs)
- return UIBase.sleep(s, sleepsecs, siglistener)
+ UIBase.sleep(s, sleepsecs)
def sleeping(s, sleepsecs, remainingsecs):
if remainingsecs and s.gettf().getcolor() == 'black':
diff --git a/offlineimap/ui/Curses.py b/offlineimap/ui/plugins/Curses.py
similarity index 99%
rename from offlineimap/ui/Curses.py
rename to offlineimap/ui/plugins/Curses.py
index 8eb709f..949edb8 100644
--- a/offlineimap/ui/Curses.py
+++ b/offlineimap/ui/plugins/Curses.py
@@ -17,7 +17,7 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from Blinkenlights import BlinkenBase
-from UIBase import UIBase
+from offlineimap.ui.UIBase import UIBase
from threading import *
import thread, time, sys, os, signal, time
from offlineimap import version, threadutil
@@ -541,10 +541,10 @@ class Blinkenlights(BlinkenBase, UIBase):
s.c.stop()
UIBase.mainException(s)
- def sleep(s, sleepsecs, siglistener):
+ def sleep(s, sleepsecs):
s.gettf().setcolor('red')
s._msg("Next sync in %d:%02d" % (sleepsecs / 60, sleepsecs % 60))
- return BlinkenBase.sleep(s, sleepsecs, siglistener)
+ BlinkenBase.sleep(s, sleepsecs)
if __name__ == '__main__':
x = Blinkenlights(None)
diff --git a/offlineimap/ui/Machine.py b/offlineimap/ui/plugins/Machine.py
similarity index 98%
rename from offlineimap/ui/Machine.py
rename to offlineimap/ui/plugins/Machine.py
index 0a07e3e..c77a3c5 100644
--- a/offlineimap/ui/Machine.py
+++ b/offlineimap/ui/plugins/Machine.py
@@ -17,7 +17,7 @@
import offlineimap.version
import urllib, sys, re, time, traceback, threading, thread
-from UIBase import UIBase
+from offlineimap.ui.UIBase import UIBase
from threading import *
protocol = '6.0.0'
@@ -175,5 +175,3 @@ class MachineUI(UIBase):
def init_banner(s):
s._printData('initbanner', offlineimap.version.banner)
- def callhook(s, msg):
- s._printData('callhook', msg)
diff --git a/offlineimap/ui/Noninteractive.py
b/offlineimap/ui/plugins/Noninteractive.py
similarity index 93%
rename from offlineimap/ui/Noninteractive.py
rename to offlineimap/ui/plugins/Noninteractive.py
index 9cd5eca..ca8ff5a 100644
--- a/offlineimap/ui/Noninteractive.py
+++ b/offlineimap/ui/plugins/Noninteractive.py
@@ -17,7 +17,7 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import sys, time
-from UIBase import UIBase
+from offlineimap.ui.UIBase import UIBase
class Basic(UIBase):
def getpass(s, accountname, config, errmsg = None):
@@ -33,10 +33,10 @@ class Basic(UIBase):
warntxt = 'warning'
sys.stderr.write(warntxt + ": " + str(msg) + "\n")
- def sleep(s, sleepsecs, siglistener):
+ def sleep(s, sleepsecs):
if s.verbose >= 0:
s._msg("Sleeping for %d:%02d" % (sleepsecs / 60, sleepsecs % 60))
- return UIBase.sleep(s, sleepsecs, siglistener)
+ UIBase.sleep(s, sleepsecs)
def sleeping(s, sleepsecs, remainingsecs):
if sleepsecs > 0:
diff --git a/offlineimap/ui/TTY.py b/offlineimap/ui/plugins/TTY.py
similarity index 98%
rename from offlineimap/ui/TTY.py
rename to offlineimap/ui/plugins/TTY.py
index 99c46d4..32d6279 100644
--- a/offlineimap/ui/TTY.py
+++ b/offlineimap/ui/plugins/TTY.py
@@ -16,7 +16,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-from UIBase import UIBase
+from offlineimap.ui.UIBase import UIBase
from getpass import getpass
import select, sys
from threading import *
diff --git a/offlineimap/ui/plugins/__init__.py
b/offlineimap/ui/plugins/__init__.py
new file mode 100644
index 0000000..1b42165
--- /dev/null
+++ b/offlineimap/ui/plugins/__init__.py
@@ -0,0 +1,18 @@
+# UI module directory
+# Copyright (C) 2002 John Goerzen
+# <jgoerzen@xxxxxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
diff --git a/setup.py b/setup.py
index 92fb1fd..fe3c53d 100644
--- a/setup.py
+++ b/setup.py
@@ -33,7 +33,8 @@ setup(name = "offlineimap",
author_email = offlineimap.version.author_email,
url = offlineimap.version.homepage,
packages = ['offlineimap', 'offlineimap.folder',
- 'offlineimap.repository', 'offlineimap.ui'],
+ 'offlineimap.repository',
+ 'offlineimap.ui','offlineimap.ui.plugins'],
scripts = ['bin/offlineimap'],
license = offlineimap.version.copyright + \
", Licensed under the GPL version 2"
--
Nicolas Sebrecht
NOTICE: the first line in the body "From: ..." should make Git set the
good author.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [PATCH] Re: Prepare dynamic plugins (again),
Nicolas Sebrecht <=
|
|