Merge lp:~zeitgeist/zeitgeist/autoload-extensions into lp:zeitgeist/0.1

Proposed by Mikkel Kamstrup Erlandsen
Status: Merged
Merge reported by: Mikkel Kamstrup Erlandsen
Merged at revision: not available
Proposed branch: lp:~zeitgeist/zeitgeist/autoload-extensions
Merge into: lp:zeitgeist/0.1
Diff against target: 343 lines (+115/-40)
15 files modified
Makefile.am (+5/-1)
_zeitgeist/Makefile.am (+1/-1)
_zeitgeist/engine/Makefile.am (+1/-1)
_zeitgeist/engine/__init__.py (+0/-28)
_zeitgeist/engine/extension.py (+46/-1)
_zeitgeist/engine/extensions/Makefile.am (+1/-1)
_zeitgeist/engine/main.py (+3/-3)
_zeitgeist/engine/upgrades/Makefile.am (+1/-1)
_zeitgeist/loggers/Makefile.am (+1/-1)
_zeitgeist/loggers/datasources/Makefile.am (+1/-1)
acinclude.m4 (+40/-0)
configure.ac (+5/-0)
zeitgeist-daemon.pc.in (+7/-0)
zeitgeist/__init__.py (+1/-0)
zeitgeist/_config.py.in (+2/-1)
To merge this branch: bzr merge lp:~zeitgeist/zeitgeist/autoload-extensions
Reviewer Review Type Date Requested Status
Markus Korn python parts Approve
Review via email: mp+28236@code.launchpad.net

Description of the change

THis branch implements automatic loading of extensions found in the extensions/ directory. You can still override this via the ZEITGEIST_{DEFAULT,EXTRA}_EXTENSIONS envvars.

It also installs zeitgeist-daemon.pc which defines a 'extensiondir' variable 3rd party extension developers can use to detect the path in which to install extensions.

To post a comment you must log in.
Revision history for this message
Markus Korn (thekorn) wrote :

First of all: I don't zhave enough autotools knowlege to understand what the non-python part of this patch is all about, but some test installations show that it seems to work as expected.

This is why I only had a closer look at the python part, it is looking perfect to me, BUT I have a few minor comments:

dict.get(<key>) always returns None if <key> is not found in dict, this is why os.environ("ENVVAR_NAME", None) should be simplified to os.environ("ENVVAR_NAME")

And I've a second, not too serious comment: You are doing to much C stuff! - this explains to me the ugly extra spaces you are adding to the code ;)
Some examples:
* extensions = _scan_extensions () or _zg = __import__ ("_zeitgeist.engine.extensions." + mod)
* log.debug("Searching for extensions in: %s" % config.extensiondir) or modules = filter(lambda m : m.endswith(".py"), os.listdir(config.extensiondir))

Other that that, big Approval.

thnks for your work,

markus

review: Approve (python parts)
1506. By Mikkel Kamstrup Erlandsen <kamstrup@hardback>

Stylistic updates to reflect Markus' comments

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile.am'
2--- Makefile.am 2009-12-08 14:08:44 +0000
3+++ Makefile.am 2010-07-01 07:47:23 +0000
4@@ -16,7 +16,8 @@
5 COPYRIGHT \
6 NEWS \
7 zeitgeist-daemon.py \
8- zeitgeist-datahub.py
9+ zeitgeist-datahub.py \
10+ zeitgeist-daemon.pc.in
11
12 DISTCLEANFILES = \
13 intltool-extract \
14@@ -27,6 +28,9 @@
15 zeitgeist-daemon \
16 zeitgeist-datahub
17
18+pkgconfigdir = $(libdir)/pkgconfig
19+pkgconfig_DATA = zeitgeist-daemon.pc
20+
21 zeitgeist-daemon: zeitgeist-daemon.py
22 sed \
23 -e "s!\/usr\/bin\/env python!$(PYTHON)!" \
24
25=== modified file '_zeitgeist/Makefile.am'
26--- _zeitgeist/Makefile.am 2010-05-16 15:34:50 +0000
27+++ _zeitgeist/Makefile.am 2010-07-01 07:47:23 +0000
28@@ -1,6 +1,6 @@
29 SUBDIRS = engine loggers
30
31-appdir = $(prefix)/share/zeitgeist/_zeitgeist/
32+appdir = $(datadir)/zeitgeist/_zeitgeist/
33
34 app_PYTHON = \
35 __init__.py \
36
37=== modified file '_zeitgeist/engine/Makefile.am'
38--- _zeitgeist/engine/Makefile.am 2010-06-08 10:21:27 +0000
39+++ _zeitgeist/engine/Makefile.am 2010-07-01 07:47:23 +0000
40@@ -1,6 +1,6 @@
41 SUBDIRS = extensions upgrades
42
43-appdir = $(prefix)/share/zeitgeist/_zeitgeist/engine/
44+appdir = $(datadir)/zeitgeist/_zeitgeist/engine/
45
46 app_PYTHON = \
47 __init__.py \
48
49=== modified file '_zeitgeist/engine/__init__.py'
50--- _zeitgeist/engine/__init__.py 2010-05-27 19:51:09 +0000
51+++ _zeitgeist/engine/__init__.py 2010-07-01 07:47:23 +0000
52@@ -42,31 +42,6 @@
53 import main # _zeitgeist.engine.main
54 _engine = main.ZeitgeistEngine()
55 return _engine
56-
57-def _get_extensions():
58- """looks at the `ZEITGEIST_DEFAULT_EXTENSIONS` environment variable
59- to find the extensions which should be loaded on daemon startup, if
60- this variable is not set the `Blacklist` and the `DataSourceRegistry`
61- extension will be loaded. If this variable is set to an empty string
62- no extensions are loaded by default.
63- To load an extra set of extensions define the `ZEITGEIST_EXTRA_EXTENSIONS`
64- variable.
65- The format of these variables should just be a no-space comma
66- separated list of module.class names"""
67- default_extensions = os.environ.get("ZEITGEIST_DEFAULT_EXTENSIONS", None)
68- if default_extensions is not None:
69- extensions = default_extensions.split(",")
70- else:
71- extensions = [
72- "_zeitgeist.engine.extensions.blacklist.Blacklist",
73- "_zeitgeist.engine.extensions.datasource_registry.DataSourceRegistry",
74- ]
75- extra_extensions = os.environ.get("ZEITGEIST_EXTRA_EXTENSIONS", None)
76- if extra_extensions is not None:
77- extensions += extra_extensions.split(",")
78- extensions = filter(None, extensions)
79- log.debug("daemon is configured to run with these extensions: %r" %extensions)
80- return extensions
81
82 class _Constants:
83 # Directories
84@@ -79,9 +54,6 @@
85 DBUS_INTERFACE = ZeitgeistDBusInterface.INTERFACE_NAME
86 SIG_EVENT = "asaasay"
87
88- # Extensions
89- DEFAULT_EXTENSIONS = _get_extensions()
90-
91 # Required version of DB schema
92 CORE_SCHEMA="core"
93 CORE_SCHEMA_VERSION = 1
94
95=== modified file '_zeitgeist/engine/extension.py'
96--- _zeitgeist/engine/extension.py 2010-07-01 07:38:58 +0000
97+++ _zeitgeist/engine/extension.py 2010-07-01 07:47:23 +0000
98@@ -3,6 +3,7 @@
99 # Zeitgeist
100 #
101 # Copyright © 2009 Markus Korn <thekorn@gmx.de>
102+# Copyright © 2010 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@gmail.com>
103 #
104 # This program is free software: you can redistribute it and/or modify
105 # it under the terms of the GNU Lesser General Public License as published by
106@@ -17,12 +18,15 @@
107 # You should have received a copy of the GNU Lesser General Public License
108 # along with this program. If not, see <http://www.gnu.org/licenses/>.
109
110+import os
111 import logging
112 import weakref # avoid circular references as they confuse garbage collection
113
114 logging.basicConfig(level=logging.DEBUG)
115 log = logging.getLogger("zeitgeist.extension")
116
117+import zeitgeist
118+
119 def safe_issubclass(obj, cls):
120 try:
121 return issubclass(obj, cls)
122@@ -98,7 +102,48 @@
123 pass
124
125
126-def load_class(path):
127+def get_extensions():
128+ """looks at the `ZEITGEIST_DEFAULT_EXTENSIONS` environment variable
129+ to find the extensions which should be loaded on daemon startup, if
130+ this variable is not set the "extensiondir" variable of the configuration
131+ will be scanned for .py files with classes extending Extension
132+ If this variable is set to an empty string no extensions are loaded by
133+ default.
134+ To load an extra set of extensions define the `ZEITGEIST_EXTRA_EXTENSIONS`
135+ variable.
136+ The format of these variables should just be a no-space comma
137+ separated list of module.class names"""
138+ default_extensions = os.environ.get("ZEITGEIST_DEFAULT_EXTENSIONS")
139+ if default_extensions is not None:
140+ extensions = map(_load_class, default_extensions.split(","))
141+ else:
142+ extensions = _scan_extensions()
143+ extra_extensions = os.environ.get("ZEITGEIST_EXTRA_EXTENSIONS")
144+ if extra_extensions is not None:
145+ extensions += map(_load_class, extra_extensions.split(","))
146+ extensions = filter(None, extensions)
147+ log.debug("daemon is configured to run with these extensions: %r" %extensions)
148+ return extensions
149+
150+def _scan_extensions():
151+ """Look in zeitgeist._config.extensiondir for .py files and return
152+ a list of classes with all the classes that extends the Extension class"""
153+ config = zeitgeist._config
154+ log.debug("Searching for extensions in: %s" % config.extensiondir)
155+ extensions = []
156+ modules = filter(lambda m : m.endswith(".py"), os.listdir(config.extensiondir))
157+ modules = map(lambda m : m.rpartition(".")[0], modules)
158+ for mod in modules:
159+ _zg = __import__("_zeitgeist.engine.extensions." + mod)
160+ ext = getattr(_zg.engine.extensions, mod)
161+ for cls in dir(ext):
162+ cls = getattr(ext, cls)
163+ if safe_issubclass(cls, Extension) and not cls is Extension:
164+ extensions.append(cls)
165+ return extensions
166+
167+
168+def _load_class(path):
169 """
170 Load and return a class from a fully qualified string.
171 Fx. "_zeitgeist.engine.extensions.myext.MyClass"
172
173=== modified file '_zeitgeist/engine/extensions/Makefile.am'
174--- _zeitgeist/engine/extensions/Makefile.am 2010-01-21 00:42:48 +0000
175+++ _zeitgeist/engine/extensions/Makefile.am 2010-07-01 07:47:23 +0000
176@@ -1,4 +1,4 @@
177-appdir = $(prefix)/share/zeitgeist/_zeitgeist/engine/extensions
178+appdir = $(datadir)/zeitgeist/_zeitgeist/engine/extensions
179
180 app_PYTHON = \
181 __init__.py \
182
183=== modified file '_zeitgeist/engine/main.py'
184--- _zeitgeist/engine/main.py 2010-07-01 07:34:26 +0000
185+++ _zeitgeist/engine/main.py 2010-07-01 07:47:23 +0000
186@@ -34,7 +34,7 @@
187 from zeitgeist.datamodel import Event as OrigEvent, StorageState, TimeRange, \
188 ResultType, get_timestamp_for_now, Interpretation, Symbol, NEGATION_OPERATOR, WILDCARD
189 from _zeitgeist.engine.datamodel import Event, Subject
190-from _zeitgeist.engine.extension import ExtensionsCollection, load_class
191+from _zeitgeist.engine.extension import ExtensionsCollection, get_extensions
192 from _zeitgeist.engine import constants
193 from _zeitgeist.engine.sql import get_default_cursor, unset_cursor, \
194 TableLookup, WhereClause
195@@ -116,9 +116,9 @@
196 raise RuntimeError("old database version")
197
198 # Load extensions
199- default_extensions = map(load_class, constants.DEFAULT_EXTENSIONS)
200+ default_extensions = get_extensions()
201 self.__extensions = ExtensionsCollection(self,
202- defaults=default_extensions)
203+ defaults=default_extensions)
204
205 self._interpretation = TableLookup(cursor, "interpretation")
206 self._manifestation = TableLookup(cursor, "manifestation")
207
208=== modified file '_zeitgeist/engine/upgrades/Makefile.am'
209--- _zeitgeist/engine/upgrades/Makefile.am 2010-06-08 10:21:27 +0000
210+++ _zeitgeist/engine/upgrades/Makefile.am 2010-07-01 07:47:23 +0000
211@@ -1,4 +1,4 @@
212-appdir = $(prefix)/share/zeitgeist/_zeitgeist/engine/upgrades
213+appdir = $(datadir)/zeitgeist/_zeitgeist/engine/upgrades
214
215 app_PYTHON = \
216 __init__.py \
217
218=== modified file '_zeitgeist/loggers/Makefile.am'
219--- _zeitgeist/loggers/Makefile.am 2009-07-11 23:20:37 +0000
220+++ _zeitgeist/loggers/Makefile.am 2010-07-01 07:47:23 +0000
221@@ -1,6 +1,6 @@
222 SUBDIRS = datasources
223
224-appdir = $(prefix)/share/zeitgeist/_zeitgeist/loggers/
225+appdir = $(datadir)/zeitgeist/_zeitgeist/loggers/
226
227 app_PYTHON = \
228 __init__.py \
229
230=== modified file '_zeitgeist/loggers/datasources/Makefile.am'
231--- _zeitgeist/loggers/datasources/Makefile.am 2009-11-25 17:01:07 +0000
232+++ _zeitgeist/loggers/datasources/Makefile.am 2010-07-01 07:47:23 +0000
233@@ -1,4 +1,4 @@
234-appdir = $(prefix)/share/zeitgeist/_zeitgeist/loggers/datasources
235+appdir = $(datadir)/zeitgeist/_zeitgeist/loggers/datasources
236
237 app_PYTHON = \
238 __init__.py \
239
240=== added file 'acinclude.m4'
241--- acinclude.m4 1970-01-01 00:00:00 +0000
242+++ acinclude.m4 2010-07-01 07:47:23 +0000
243@@ -0,0 +1,40 @@
244+dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
245+dnl
246+dnl example
247+dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
248+dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
249+
250+AC_DEFUN([AS_AC_EXPAND],
251+[
252+ EXP_VAR=[$1]
253+ FROM_VAR=[$2]
254+
255+ dnl first expand prefix and exec_prefix if necessary
256+ prefix_save=$prefix
257+ exec_prefix_save=$exec_prefix
258+
259+ dnl if no prefix given, then use /usr/local, the default prefix
260+ if test "x$prefix" = "xNONE"; then
261+ prefix=$ac_default_prefix
262+ fi
263+ dnl if no exec_prefix given, then use prefix
264+ if test "x$exec_prefix" = "xNONE"; then
265+ exec_prefix=$prefix
266+ fi
267+
268+ full_var="$FROM_VAR"
269+ dnl loop until it doesn't change anymore
270+ while true; do
271+ new_full_var="`eval echo $full_var`"
272+ if test "x$new_full_var"="x$full_var"; then break; fi
273+ full_var=$new_full_var
274+ done
275+
276+ dnl clean up
277+ full_var=$new_full_var
278+ AC_SUBST([$1], "$full_var")
279+
280+ dnl restore prefix and exec_prefix
281+ prefix=$prefix_save
282+ exec_prefix=$exec_prefix_save
283+])
284
285=== modified file 'configure.ac'
286--- configure.ac 2010-06-21 10:22:38 +0000
287+++ configure.ac 2010-07-01 07:47:23 +0000
288@@ -13,12 +13,17 @@
289 AM_GLIB_GNU_GETTEXT
290 IT_PROG_INTLTOOL([0.35.0])
291
292+# Expand variables needed for config.py
293+AS_AC_EXPAND(DATADIR, $datarootdir)
294+AC_SUBST(DATADIR)
295+
296 AC_CONFIG_FILES([
297 Makefile
298 doc/Makefile
299 extra/Makefile
300 extra/ontology/Makefile
301 po/Makefile.in
302+ zeitgeist-daemon.pc
303 zeitgeist/Makefile
304 _zeitgeist/Makefile
305 _zeitgeist/loggers/Makefile
306
307=== added file 'zeitgeist-daemon.pc.in'
308--- zeitgeist-daemon.pc.in 1970-01-01 00:00:00 +0000
309+++ zeitgeist-daemon.pc.in 2010-07-01 07:47:23 +0000
310@@ -0,0 +1,7 @@
311+prefix=@prefix@
312+extensiondir=@datadir@/zeitgeist/_zeitgeist/engine/extensions
313+
314+Name: @PACKAGE_NAME@
315+Description: Zeitgeist Engine Extensions
316+Version: @PACKAGE_VERSION@
317+
318
319=== modified file 'zeitgeist/__init__.py'
320--- zeitgeist/__init__.py 2009-12-31 23:12:25 +0000
321+++ zeitgeist/__init__.py 2010-07-01 07:47:23 +0000
322@@ -23,6 +23,7 @@
323 pkgdatadir = os.path.join(bindir, "data")
324 privatepythondir = bindir
325 datasourcedir = os.path.join(bindir, "_zeitgeist/loggers/datasources")
326+ extensiondir = os.path.join(bindir, "_zeitgeist/engine/extensions")
327 libdir = ""
328 libexecdir = ""
329 PACKAGE = "zeitgeist"
330
331=== modified file 'zeitgeist/_config.py.in'
332--- zeitgeist/_config.py.in 2009-12-30 19:03:24 +0000
333+++ zeitgeist/_config.py.in 2010-07-01 07:47:23 +0000
334@@ -4,7 +4,8 @@
335 localedir = "@datadir@/locale"
336 pkgdatadir = "@pkgdatadir@"
337 privatepythondir = "@prefix@/share/zeitgeist"
338-datasourcedir = "@prefix@/share/zeitgeist/_zeitgeist/loggers/datasources"
339+datasourcedir = "@datadir@/zeitgeist/_zeitgeist/loggers/datasources"
340+extensiondir = "@datadir@/zeitgeist/_zeitgeist/engine/extensions"
341 libdir = "@libdir@"
342 libexecdir = "@libexecdir@"
343 PACKAGE = "@PACKAGE@"