Merge lp:~edgar-b-dsouza/acire/snippet_dependency_res_2 into lp:acire

Proposed by Ed S on 2010-05-06
Status: Needs review
Proposed branch: lp:~edgar-b-dsouza/acire/snippet_dependency_res_2
Merge into: lp:acire
Diff against target: 873 lines (+812/-2) 6 files modified
To merge this branch: bzr merge lp:~edgar-b-dsouza/acire/snippet_dependency_res_2
Reviewer Review Type Date Requested Status
Jono Bacon 2010-05-06 Needs Fixing on 2010-06-02
Review via email: mp+24813@code.launchpad.net

Description of the Change

Merged in snippet dependency checker against trunk rev 61.

The (newly added) README.dep_checker.txt file contains lots of info about the added files and how it hangs together.

I have tested earlier on Karmic and now on Lucid, with a Clutter script, and it works. Doesn't mean it's bug-free, of course :-) And from the performance viewpoint, I'd hope that contributors could change the check to a regex-based one, which ought to be faster.

Have incorporated your points from https://code.launchpad.net/~edgar-b-dsouza/acire/snippet_dependency_res_1/+merge/21924 as regards:

- Firefox-style yellow bar showing atop the GTKSourceView - in fact, the README contains your exact use case, copied from that merge proposal :-) (NB - I have been driven to almost leaving claw marks on the walls by the GtkLabel's wrapping behavior on Karmic and Lucid... every time I think I got it fixed, it springs out in a different way :-/ Somebody who can make that look decent, PLEASE fix the appearance).

- Keeping code out of bin/acire as much as possible - right, have done that, except for the few lines absolutely required to hook it up.

- Mechanism for hackers on other distros to add distro-specific package-handling code (of course, only tested on Ubuntu, may have bugs on other distros, needs to be tried out).

Looking forward to review - hope this one can be accepted to the trunk.

Thanks
Ed.

To post a comment you must log in.
Jono Bacon (jonobacon) wrote :

Sorry for the delay, Ed! I just tested on Maverick and this doesn't work when I tested a snippet with Zeitgeist.

Jono Bacon (jonobacon) :
review: Needs Fixing
Ed S (edgar-b-dsouza) wrote :

Hi Jono,
I tried installing Maverick on an old laptop and ran into problems. Since I'm happily (for the most part) using Lucid on my main laptop, I don't want to put Maverick on that. I started installing into a VM to check, but got sidetracked by other stuff.

I hope to get around to debugging this soon. I think the error is probably in (my use of) the modulefinder module; if so, then I'm at a loss as to what to do, other than go the regex way :-( and I find that a bit too hard; could sure use help with that.

Did you try it with any other snippets that tried to import an unavailable module? Did the code work at least once on your system?

And when you tried it on the Zeitgeist snippet, were any messages printed to stdout (presuming you run Acire from a terminal)? Could you please share those, if any?

Thanks,
Ed.

Unmerged revisions

62. By Ed S on 2010-05-06

Merged in snippet dependency checker against rev 61

Preview Diff

1=== added file 'acire/README.dep_checker.txt'
2--- acire/README.dep_checker.txt 1970-01-01 00:00:00 +0000
3+++ acire/README.dep_checker.txt 2010-05-06 10:56:27 +0000
4@@ -0,0 +1,123 @@
5+README for the Acire Snippet Dependency Checker feature.
6+=========================================================
7+
8+v. 1 - 03 Apr 2010 - Edgar D'Souza <edgar.b.dsouza@gmail.com>
9+
10+
11+Original Problem
12+================
13+When a code snippet tried to import a module that was missing on the system, an
14+ImportError exception was printed to stdout. Users running Acire full-screen
15+(or from an Applications menu entry) didn't even see that. It just failed.
16+
17+Dependency Checker Goal
18+=======================
19+Check the snippet for missing modules on load of the snippet into Acire's
20+GTKSourceview. Prompt the user about the missing dependencies and offer to
21+install the missing module(s), if candidate package(s) can be found (in
22+repositories already set up on user's system).
23+
24+Additional Requirements/Goals
25+=============================
26+1. Ensure that code used to interact with the package-management system is
27+ NOT distribution-specific to Ubuntu, or can be extended for other distros.
28+
29+2. Have a privilege-elevation mechanism for installation of packages.
30+ Some distros differ in how they achieve superuser privileges; this should
31+ also be extendable (add a distro-specific function wherever required).
32+
33+3a. Try to make the package installation as GUI-centric as possible.
34+ From a merge request discussion with project founder Jono Bacon, this is
35+ what he wrote:
36+ ------------------------------------
37+ my recommendation would be the following use case:
38+
39+ 1. User loads a snippet that does not have a module installed.
40+ 2. Above the source view a little bar appears (like the plugin missing bar
41+ in Firefox) where it says what module(s) are missing and a button called
42+ 'Install'.
43+ 3. User clicks the 'Install' button, a dialog box pops up to confirm what
44+ will be installed, the user clicks the 'OK' button, is asked for password
45+ and then a dialog displays progress of the installation.
46+ 4. The bar disappears from the snippet.
47+
48+ I would prefer that the code for this feature does not clutter up bin/acire
49+ if possible. Probably best is that the logic lives in a file called
50+ moduleinstaller.py and there is a separate .py file with the dialog box.
51+ ------------------------------------
52+3b. Some distros may have problems with superuser-priv processes connecting to
53+ the X server for the ordinary user account. The privilege-elevation
54+ mechanism is expected to take care of this, or extra code could be added.
55+
56+First-cut implementation specifics
57+==================================
58+New files added to Acire by this implementation:
59+ depchecker.py
60+ distropkgutils.py
61+ install_packages.py
62+ utils.py
63+ README.dep_checker.txt (this file)
64+
65+The role of each Python file is explained below.
66+
67+The checking code is invoked from bin/acire (the main executable), in the
68+snippet_selected() function, via the line beginning with:
69+ "check_results = depchecker.check_script"
70+
71+The depchecker.py module contains generic (distro-independent, AFAIK) functions
72+that use the built-in Python modulefinder module to see if any import
73+statements in the snippet use modules that are not installed on the system.
74+
75+depchecker.py imports distropkgutils.py, creates an instance of the
76+distropkgutils.DistroPkgUtils class, and calls said class's methods, to
77+accomplish distro-specific functionality. It then builds a GTKEventBox (to show
78+a yellow background color), and its contained controls (HBox, which in turn
79+contains a GTKLabel and GTKButton). Code in this file customizes the message
80+that is displayed, and, if there aren't (enough) installation candidates, the
81+caption of the GTKButton. If there are enough packages to install to supply the
82+missing modules, then the button caption remains "Install", and the label's
83+message lists the packages that will be installed. If unable to find packages
84+to install, then the label message is changed to reflect this, and the GTKButton
85+caption is changed to "More Info" - clicking this will take the user to a wiki
86+page (the URL is at the beginning of depchecker.py).
87+
88+depchecker.py has a remove_eventbox() function, which is called internally,
89+from the handler for the GTKButton, and also from bin/acire, in the
90+snippet_selected() function, to hide & destroy the eventbox if it still exists.
91+This gets rid of the eventbox both when user clicks the Install button, or if
92+user ignores it and clicks another snippet in the treeview.
93+
94+Class DistroPkgUtils in distropkgutils.py:
95+Exposes generically-named methods (see 'Internals' below) to calling code:
96+- get_pkg_list(): Returns list of UNinstalled python library packages
97+- install_pkgs(pkglist): installs the given packages, as described below.
98+
99+Class & Module Internals -- distropkgutils.py:
100+- In __init__(), class checks which distribution it is running under, and looks
101+ for an appropriate distro-specific package-listing function (in the same
102+ module) that it exposes as get_pkg_list() to calling code. This is because
103+ retrieving a list of uninstalled packages does not need superuser privs.
104+ IF an __get_pkg_list function is not found for the current distro, the
105+ default implementation of the method simply returns an empty list, warning
106+ calling code that something is amiss (if it wants to handle this).
107+- The install_pkgs() method accepts a list of packages as a parameter, and
108+ calls a private function, _as_superuser(), to launch install_packages.py
109+ as a subprocess with superuser privs. The list of packages to install is
110+ passed on the command-line as arguments.
111+- The _as_superuser() function has a default implementation using gksu,
112+ in this same class. However, __init__() checks for a distro-specific
113+ implementation of this function; if found, it overwrites the default
114+ implementation with the found one. This lets you add privilege
115+ escalation code for different distros (which don't use gksu/sudo).
116+
117+install_packages.py -- contains distro-specific package installation functions.
118+As in distropkgutils.py, this script too checks for distro via platform.dist()
119+and attempts to retrieve a matching install function from itself. If a callable
120+object is obtained, it calls that function, passing the command-line arguments.
121+This file expects to be launched with administrative/superuser privileges
122+needed to install packages, and does not contain any code for privilege
123+escalation.
124+
125+utils.py -- contains a few functions that are used from multiple files,
126+and/or are not exclusively related to the package-management code (i.e. could
127+be re-used elsewhere).
128\ No newline at end of file
129
130=== added file 'acire/depchecker.py'
131--- acire/depchecker.py 1970-01-01 00:00:00 +0000
132+++ acire/depchecker.py 2010-05-06 10:56:27 +0000
133@@ -0,0 +1,179 @@
134+#! /usr/bin/env python
135+#coding=utf-8
136+### BEGIN LICENSE
137+# Copyright (C) 2010 Edgar D'Souza <edgar.b.dsouza@gmail.com>
138+#This program is free software: you can redistribute it and/or modify it
139+#under the terms of the GNU General Public License version 3, as published
140+#by the Free Software Foundation.
141+#
142+#This program is distributed in the hope that it will be useful, but
143+#WITHOUT ANY WARRANTY; without even the implied warranties of
144+#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
145+#PURPOSE. See the GNU General Public License for more details.
146+#
147+#You should have received a copy of the GNU General Public License along
148+#with this program. If not, see <http://www.gnu.org/licenses/>.
149+### END LICENSE
150+
151+import modulefinder
152+from acire import distropkgutils as dpu
153+import webbrowser
154+import pygtk
155+pygtk.require('2.0')
156+import gtk
157+
158+infoURL = "https://wiki.ubuntu.com/PythonSnippets_Dependencies"
159+
160+#Grab a list of packages immediately upon import of this module.
161+dp_utils = dpu.DistroPkgUtils()
162+python_pkgs = dp_utils.get_pkg_list()
163+#Other global vars hold data while waiting for user response through UI
164+candidates = []
165+event_box = None
166+
167+def check_script(filename, container_vbox):
168+ """Entry point for checking a script, to be called when snippet is
169+ loaded from file into Acire's GTKSourceView.
170+ filename - path+filename to the Python script to be checked.
171+ container_vbox - the right-hand pane vbox containing the editor viewport
172+ and other controls (more controls are built and packed in below).
173+ """
174+ global python_pkgs, candidates, event_box
175+
176+ missing_in_main = []
177+ candidate_pkgs = []
178+ candidates = candidate_pkgs
179+
180+ modfinder = modulefinder.ModuleFinder()
181+ modfinder.run_script(filename)
182+ missing, maybe = modfinder.any_missing_maybe()
183+ #Due to bad docs, next 3 lines copied from modulefinder.report() and modded
184+ if missing:
185+ for name in missing:
186+ mods = modfinder.badmodules[name].keys()
187+ if "__main__" in mods:
188+ missing_in_main.append(name)
189+
190+ #Python's intelligent iteration will just skip the loop(s) below if:
191+ #a) There are no modulenames in missing_in_main, OR
192+ #b) There are no python_pkgs (pkg-mgt function for curr distro n/avble)
193+ for mod in missing_in_main:
194+ for pkg in python_pkgs:
195+ if mod in pkg:
196+ candidate_pkgs.append(pkg)
197+ #print "Candidate package for module %s: %s" % (mod, pkg)
198+
199+ #Build the GUI if there are missing modulenames - even if there
200+ #aren't any, or sufficient, candidates for installation - because if
201+ #not, it will look like the dependency was not detected.
202+ if len(missing_in_main) > 0:
203+ evbox, alert_label, resolve_button, builder = build_ui()
204+ event_box = evbox #Put in global var to use in remove_eventbox()
205+ msg = alert_label.get_text()
206+ #If num-missing-modules == num-installation-candidates, then we
207+ #probably have a solution and can propose installation.
208+ if len(missing_in_main) == len(candidate_pkgs):
209+ #Store the candidate packages list globally, for use in the
210+ #resolve_button handler "resolve_deps()".
211+ candidates = candidate_pkgs
212+ #Replace placeholder in label text with package list.
213+ msg = msg % (", ".join(candidate_pkgs))
214+ else:
215+ #Incomplete or no solution: offer to take user to wiki page.
216+ msg = "This snippet uses modules that aren't installed on your"
217+ msg += "system, but we cannot find packages to install.\n"
218+ msg += "Click the More Info button to visit a wiki page."
219+ resolve_button.set_text(" More _Info ")
220+ alert_label.set_text(msg)
221+ resolve_button.connect("clicked", resolve_deps)
222+ container_vbox.pack_start(evbox, False, False, 3)
223+ container_vbox.reorder_child(evbox, 0)
224+ evbox.show_all()
225+
226+def resolve_deps(widget, data=None):
227+ """Click event handler for resolve_button. """
228+ global python_pkgs, candidates, dp_utils, event_box, infoURL
229+
230+ #If there are installation candidates, check_script() found that number of
231+ #candidates matched number of missing modules... install the list of pkgs.
232+ if len(candidates) > 0:
233+ dp_utils.install_pkgs(candidates)
234+ #Refresh the list of uninstalled Python packages.
235+ python_pkgs = dp_utils.get_pkg_list()
236+ else:
237+ #check_script couldn't find a 'complete solution': either the packages
238+ #available < num-missing-modules, or there were zero candidate pkgs.
239+ webbrowser.open_new_tab(infoURL)
240+ remove_eventbox()
241+
242+def remove_eventbox():
243+ global event_box
244+ if event_box is not None:
245+ event_box.hide()
246+ event_box.destroy()
247+ event_box = None
248+
249+def build_ui():
250+ ui = '''<interface>
251+ <object class="GtkEventBox" id="alert_eventbox">
252+ <property name="visible">True</property>
253+ <child>
254+ <object class="GtkHBox" id="alert_hbox">
255+ <child>
256+ <object class="GtkLabel" id="alert_label">
257+ <property name="visible">True</property>
258+ <property name="wrap">True</property>
259+ <property name="xpad">5</property>
260+ <property name="ypad">5</property>
261+ <property name="label">This snippet uses modules that are not installed on your system. Click the Install button to install these packages: %s</property>
262+ <property name="track_visited_links">False</property>
263+ <attributes>
264+ <attribute name="weight" value="semibold"/>
265+ <attribute name="gravity" value="north"/>
266+ <attribute name="gravity-hint" value="natural"/>
267+ <attribute name="style" value="normal"/>
268+ <attribute name="gravity" value="east"/>
269+ </attributes>
270+ </object>
271+ <packing>
272+ <property name="fill">True</property>
273+ <property name="position">0</property>
274+ </packing>
275+ </child>
276+ <child>
277+ <object class="GtkButton" id="resolve_button">
278+ <property name="label" translatable="yes"> _Install </property>
279+ <property name="visible">True</property>
280+ <property name="width_request">100</property>
281+ <property name="can_focus">True</property>
282+ <property name="receives_default">True</property>
283+ <property name="use_underline">True</property>
284+ <signal name="clicked" handler="resolve_deps"/>
285+ </object>
286+ <packing>
287+ <property name="expand">False</property>
288+ <property name="fill">False</property>
289+ <property name="padding">3</property>
290+ <property name="pack_type">end</property>
291+ <property name="position">1</property>
292+ </packing>
293+ </child>
294+ </object>
295+ </child>
296+ </object>
297+ </interface>'''
298+ # Create a GTKBuilder instance
299+ builder = gtk.Builder()
300+ # Add a UI description
301+ builder.add_from_string(ui)
302+ evbox = builder.get_object("alert_eventbox")
303+ evbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffff00"))
304+ alert_label = builder.get_object("alert_label")
305+ resolve_button = builder.get_object("resolve_button")
306+ return evbox, alert_label, resolve_button, builder
307+
308+
309+if __name__ == "__main__":
310+ print "Import this as a module and run utils.check_script('filename.py')"
311+ print "to examine the script for dependency modules."
312+ print "See check_script() docstring for returned dict contents."
313
314=== added file 'acire/distropkgutils.py'
315--- acire/distropkgutils.py 1970-01-01 00:00:00 +0000
316+++ acire/distropkgutils.py 2010-05-06 10:56:27 +0000
317@@ -0,0 +1,243 @@
318+#! /usr/bin/env python
319+#coding=utf-8
320+### BEGIN LICENSE
321+# Copyright (C) 2010 Edgar D'Souza <edgar.b.dsouza@gmail.com> (initial version)
322+#This program is free software: you can redistribute it and/or modify it
323+#under the terms of the GNU General Public License version 3, as published
324+#by the Free Software Foundation.
325+#
326+#This program is distributed in the hope that it will be useful, but
327+#WITHOUT ANY WARRANTY; without even the implied warranties of
328+#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
329+#PURPOSE. See the GNU General Public License for more details.
330+#
331+#You should have received a copy of the GNU General Public License along
332+#with this program. If not, see <http://www.gnu.org/licenses/>.
333+### END LICENSE
334+
335+###
336+###
337+###
338+###
339+###
340+### PLEASE READ README.dep_checker.txt before hacking - thanks! :-)
341+###
342+###
343+###
344+###
345+###
346+###
347+
348+import subprocess
349+import sys
350+import os
351+
352+try:
353+ from acire import utils
354+except:
355+ #Required when doing singleton-testing of this file (exec it alone)
356+ sys.path.insert(0, os.getcwd())
357+ import utils
358+
359+
360+class DistroPkgUtils():
361+ """Class to abstract distro-specific libraries/commands so that calling
362+ code can call generically-named methods of this class without bothering
363+ about which distro is being used.
364+ """
365+ def __init__(self, dummydistname_testingonly=None):
366+ #Store distro name in lower case (or a dummy name, for testing)
367+ if dummydistname_testingonly is None:
368+ self.distname_lower = utils.distro_name()
369+ else:
370+ self.distname_lower = dummydistname_testingonly
371+ #print "Distro name: ", self.distname_lower
372+
373+ #Pick up distro-specific functions from this module
374+ thismodule = sys.modules[__name__]
375+ getter_name = "__get_pkg_list_%s" % self.distname_lower
376+ as_superuser_name = "__as_superuser_%s" % self.distname_lower
377+ #print "Func names sought: ", getter_name, as_superuser_name
378+ try:
379+ getter_func = getattr(thismodule, getter_name)
380+ #print "getter_func: ", getter_func
381+ if callable(getter_func):
382+ #Override the default empty-list member of this class with
383+ #the function containing the actual code.
384+ self.get_pkg_list = getter_func
385+
386+ #the _as_superuser function has a default implementation in this
387+ #class, but can be overridden, so:
388+ if hasattr(thismodule, as_superuser_name):
389+ as_superuser_func = getattr(thismodule, as_superuser_name)
390+ #print "as_superuser_func: ", as_superuser_func
391+ if callable(as_superuser_func):
392+ self.__as_superuser = as_superuser_func
393+ except Exception, e:
394+ print "Exception while trying to access functions for %s in %s" % (self.distname_lower, __name__)
395+ print "Exception was: %s" % str(e)
396+
397+ def get_pkg_list():
398+ """Default implementation of this function returns an empty list of
399+ packages, if there is no distro-specific function found."""
400+ return []
401+
402+ def __as_superuser(self, pkglist):
403+ """Shells to a gksu invocation of install_packages.py, waiting till
404+ it's done. To override, create a _as_superuser_DISTRONAME() below;
405+ __init__() will find it and replace this with your specific
406+ implementation."""
407+ #Spawn a subprocess with gksu, to run the install_packages.py script.
408+ try:
409+ cwd = os.path.dirname(os.path.abspath(__file__))
410+ print "cwd: ", cwd
411+ cc_args = ["/usr/bin/gksu", "--", "python",
412+ "install_packages.py", "-d", "-i"]
413+ for item in pkglist:
414+ cc_args.append('%s' % item)
415+ print "cc_args: ", cc_args
416+ #check_call waits for command to complete before returning.
417+ subprocess.check_call(cc_args, cwd=cwd)
418+ except subprocess.CalledProcessError, cpe:
419+ print "CalledProcessError: %s" % repr(cpe)
420+ raise
421+
422+ def install_pkgs(self, pkglist):
423+ """Method accepts the list of packages to be installed, then calls
424+ _as_superuser to run install_packages.py with elevated privileges.
425+ That script does its own distro detection and calls the appropriate
426+ function with the package list passed on the command line."""
427+ self.__as_superuser(pkglist)
428+
429+#------------------------------------------------------------------------------
430+###Package-management listing functions for different distros.
431+###Some distros may have an optional privilege-elevation function too.
432+### Please read README.dep_checker.txt for explanation.
433+
434+###Unimplemented function stubs call the "utils.not_implemented()" function
435+### to display a message. Contributions welcome for your favorite distro!
436+###Distros taken from _supported_dists in platform module, have not added stubs
437+### for Mint, PCLinuxOS, Sabayon, Puppy, Arch and many others, since am not
438+### sure what platform.dist() returns on each.
439+###
440+###Function naming convention:
441+### NB: DISTRONAME => the lower-case version of the name returned
442+### by platform.linux_distribution()[0]
443+### - __get_pkg_list_DISTRONAME()
444+### Func to return list of uninstalled python library packages.
445+### Please adjust the filtering code for the package-naming conventions
446+### of the distro for which you're writing the function.
447+### Does not have GUI or other display of this operation.
448+### - (Optional) __as_superuser_DISTRONAME()
449+### Optional override of the default privilege-escalation mechanism
450+### implemented in the class above.
451+
452+def __get_pkg_list_ubuntu():
453+ """Run subprocess with aptitude search to fetch a list of package names
454+ with regex 'python-*'; filter it to keep those which are not installed.
455+ Returns: list of uninstalled python packages. """
456+
457+ cmd = "/usr/bin/aptitude search '^python-[a-z]*$' --disable-columns -F '%p\$%v'"
458+ proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
459+ pkg_list = proc.communicate()[0]
460+ #Convert to a list of packages, filtering to keep those where the Version
461+ #is "<none>" (i.e. not installed)
462+ python_uninst_pkgs = []
463+ for pkg in pkg_list.split("\n"):
464+ if len(pkg.strip()) > 0:
465+ parts = pkg.split("$")
466+ #print parts
467+ if parts[1].strip() == "<none>":
468+ python_uninst_pkgs.append(parts[0])
469+ return python_uninst_pkgs
470+
471+#------------------------------------------------------------------------------
472+
473+def __get_pkg_list_debian():
474+ """Returns a list of Python library packages that are not installed yet."""
475+ return __get_pkg_list_ubuntu() #Redirect to Ubuntu code - same family
476+
477+#------------------------------------------------------------------------------
478+
479+def __get_pkg_list_redhat():
480+ """Returns a list of Python library packages that are not installed yet."""
481+ utils.not_implemented(utils.funcname())
482+ return []
483+
484+#------------------------------------------------------------------------------
485+
486+def __get_pkg_list_centos():
487+ """Returns a list of Python library packages that are not installed yet."""
488+ return __get_pkg_list_redhat() #Redirect to RH code - same family
489+
490+#------------------------------------------------------------------------------
491+
492+def __get_pkg_list_fedora():
493+ """Returns a list of Python library packages that are not installed yet."""
494+ return __get_pkg_list_redhat() #Redirect to RH code - same family
495+
496+#------------------------------------------------------------------------------
497+
498+def __get_pkg_list_mandriva():
499+ """Returns a list of Python library packages that are not installed yet."""
500+ utils.not_implemented(utils.funcname())
501+ return []
502+
503+#------------------------------------------------------------------------------
504+
505+def __get_pkg_list_suse():
506+ """Returns a list of Python library packages that are not installed yet."""
507+ utils.not_implemented(utils.funcname())
508+ return []
509+
510+#------------------------------------------------------------------------------
511+
512+def __get_pkg_list_slackware():
513+ """Returns a list of Python library packages that are not installed yet."""
514+ utils.not_implemented(utils.funcname())
515+ return []
516+
517+#------------------------------------------------------------------------------
518+
519+def __get_pkg_list_gentoo():
520+ """Returns a list of Python library packages that are not installed yet."""
521+ utils.not_implemented(utils.funcname())
522+ return []
523+
524+#------------------------------------------------------------------------------
525+
526+def __get_pkg_list_yellowdog():
527+ """Returns a list of Python library packages that are not installed yet."""
528+ utils.not_implemented(utils.funcname())
529+ return []
530+
531+#------------------------------------------------------------------------------
532+
533+def __get_pkg_list_unitedlinux():
534+ """Returns a list of Python library packages that are not installed yet."""
535+ utils.not_implemented(utils.funcname())
536+ return []
537+
538+#------------------------------------------------------------------------------
539+
540+def __get_pkg_list_turbolinux():
541+ """Returns a list of Python library packages that are not installed yet."""
542+ utils.not_implemented(utils.funcname())
543+ return []
544+
545+#------------------------------------------------------------------------------
546+
547+def __get_pkg_list_rocks():
548+ """Returns a list of Python library packages that are not installed yet."""
549+ utils.not_implemented(utils.funcname())
550+ return []
551+
552+#------------------------------------------------------------------------------
553+
554+
555+### Module self-test (limited)
556+if __name__ == "__main__":
557+ for distro in ["fedora", "rocks", "turbolinux", "debian"]:
558+ dpu = DistroPkgUtils(distro)
559+ if len(dpu.get_pkg_list()) > 0:
560+ dpu.install_pkgs(["python-clutter"])
561\ No newline at end of file
562
563=== added file 'acire/install_packages.py'
564--- acire/install_packages.py 1970-01-01 00:00:00 +0000
565+++ acire/install_packages.py 2010-05-06 10:56:27 +0000
566@@ -0,0 +1,208 @@
567+#! /usr/bin/python
568+#coding=utf-8
569+### BEGIN LICENSE
570+# Copyright (C) 2010 Edgar D'Souza <edgar.b.dsouza@gmail.com>
571+#This program is free software: you can redistribute it and/or modify it
572+#under the terms of the GNU General Public License version 3, as published
573+#by the Free Software Foundation.
574+#
575+#This program is distributed in the hope that it will be useful, but
576+#WITHOUT ANY WARRANTY; without even the implied warranties of
577+#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
578+#PURPOSE. See the GNU General Public License for more details.
579+#
580+#You should have received a copy of the GNU General Public License along
581+#with this program. If not, see <http://www.gnu.org/licenses/>.
582+### END LICENSE
583+
584+import pygtk
585+pygtk.require("2.0")
586+import gtk
587+import subprocess
588+import os
589+import sys
590+import time
591+
592+# Check if we are working in the source tree or from the installed
593+# package and mangle the python path accordingly
594+if os.path.dirname(sys.argv[0]) != ".":
595+ if sys.argv[0][0] == "/":
596+ fullPath = os.path.dirname(sys.argv[0])
597+ else:
598+ fullPath = os.getcwd() + "/" + os.path.dirname(sys.argv[0])
599+else:
600+ fullPath = os.getcwd()
601+sys.path.insert(0, os.path.dirname(fullPath))
602+import utils
603+
604+#Helper function - writes debug messages to a log file.
605+def log(msg):
606+ filename = '/tmp/install_packages.log'
607+ dumpstr = "%s\n" % msg
608+ write_mode = 'a'
609+ tmpfile = open(filename, write_mode, 0)
610+ tmpfile.write(dumpstr)
611+ tmpfile.close()
612+
613+
614+def __install_pkgs_ubuntu(pkglist):
615+ """Install the packages passed as the pkglist parameter.
616+ python-apt must be installed (must be a dependency of the Acire package).
617+ """
618+ try:
619+ import apt.progress.gtk2
620+
621+ win = gtk.Window()
622+ win.connect("destroy", gtk.main_quit)
623+ win.set_size_request(450, 440)
624+ win.set_position(gtk.WIN_POS_CENTER_ALWAYS)
625+ win.set_title("Acire Module Installation")
626+
627+ vbox = gtk.VBox(False, 3)
628+ vbox.set_border_width(10)
629+
630+ progress = apt.progress.gtk2.GtkAptProgress()
631+ vbox.pack_start(progress)
632+
633+ hbox = gtk.HButtonBox()
634+ hbox.set_layout(gtk.BUTTONBOX_END)
635+ close_button = gtk.Button("Close", gtk.STOCK_CLOSE)
636+ close_button.connect("clicked", gtk.main_quit)
637+ hbox.add(close_button)
638+
639+ vbox.pack_end(hbox, True, True, 0)
640+
641+ win.add(vbox)
642+
643+ close_button.show()
644+ progress.show()
645+ vbox.show()
646+ win.show()
647+ cache = apt.cache.Cache(progress.open)
648+ for pkg in pkglist:
649+ cache[pkg].markInstall()
650+ progress.show_terminal(expanded=True)
651+ cache.commit(progress.fetch, progress.install)
652+ hbox.show()
653+ gtk.main()
654+ time.sleep(1)
655+ except Exception, e:
656+ log("%s: Exception: %s" % (utils.funcname(), repr(e)))
657+ time.sleep(1)
658+
659+#------------------------------------------------------------------------------
660+
661+def __install_pkgs_debian(pkglist):
662+ """Installs the packages passed as the pkglist parameter."""
663+ __install_pkgs_ubuntu(pkglist) #Redirect to Ubuntu code - same family
664+
665+#------------------------------------------------------------------------------
666+
667+def __install_pkgs_redhat(pkglist):
668+ """Installs the packages passed as the pkglist parameter."""
669+ utils.not_implemented(utils.funcname(), True)
670+
671+#------------------------------------------------------------------------------
672+
673+def __install_pkgs_centos(pkglist):
674+ """Installs the packages passed as the pkglist parameter."""
675+ __install_pkgs_redhat(pkglist) #Redirect to RH code - same family
676+
677+#------------------------------------------------------------------------------
678+
679+def __install_pkgs_fedora(pkglist):
680+ """Installs the packages passed as the pkglist parameter."""
681+ __install_pkgs_redhat(pkglist) #Redirect to RH code - same family
682+
683+#------------------------------------------------------------------------------
684+
685+def __install_pkgs_mandriva(pkglist):
686+ """Installs the packages passed as the pkglist parameter."""
687+ utils.not_implemented(utils.funcname(), True)
688+
689+#------------------------------------------------------------------------------
690+
691+def __install_pkgs_suse(pkglist):
692+ """Installs the packages passed as the pkglist parameter."""
693+ utils.not_implemented(utils.funcname(), True)
694+
695+#------------------------------------------------------------------------------
696+
697+def __install_pkgs_slackware(pkglist):
698+ """Installs the packages passed as the pkglist parameter."""
699+ utils.not_implemented(utils.funcname(), True)
700+
701+#------------------------------------------------------------------------------
702+
703+def __install_pkgs_gentoo(pkglist):
704+ """Installs the packages passed as the pkglist parameter."""
705+ utils.not_implemented(utils.funcname(), True)
706+
707+#------------------------------------------------------------------------------
708+
709+def __install_pkgs_yellowdog(pkglist):
710+ """Installs the packages passed as the pkglist parameter."""
711+ utils.not_implemented(utils.funcname(), True)
712+
713+#------------------------------------------------------------------------------
714+
715+def __install_pkgs_unitedlinux(pkglist):
716+ """Installs the packages passed as the pkglist parameter."""
717+ utils.not_implemented(utils.funcname(), True)
718+
719+#------------------------------------------------------------------------------
720+
721+def __install_pkgs_turbolinux(pkglist):
722+ """Installs the packages passed as the pkglist parameter."""
723+ utils.not_implemented(utils.funcname(), True)
724+
725+#------------------------------------------------------------------------------
726+
727+def __install_pkgs_rocks(pkglist):
728+ """Installs the packages passed as the pkglist parameter."""
729+ utils.not_implemented(utils.funcname(), True)
730+
731+#------------------------------------------------------------------------------
732+#------------------------------------------------------------------------------
733+
734+
735+import sys
736+import optparse
737+
738+if __name__ == "__main__":
739+ parser = optparse.OptionParser()
740+ parser.add_option("-i","--install",action="store_true",dest="instaction")
741+ parser.add_option("-d","--debug",action="store",type="string",dest="debug")
742+
743+ (options, args) = parser.parse_args()
744+
745+ log("Starting run at: %s" % time.asctime())
746+
747+ # Make sure we have our mandatory argument (at least one package)
748+ if len(args) == 0:
749+ print "This file is not for general usage, but meant for use from"
750+ print "Acire's distropkgutils library. It should be invoked with "
751+ print "a list of packages to install."
752+ sys.exit(1)
753+
754+ log("Args: ")
755+ log(str(args))
756+
757+ #Choose distro-specific install function from this module
758+ distname_lower = utils.distro_name()
759+ log("Detected distro %s" % distname_lower)
760+
761+ thismodule = sys.modules[__name__]
762+ inst_func_name = "__install_pkgs_%s" % distname_lower
763+ try:
764+ inst_func = getattr(thismodule, inst_func_name)
765+ if callable(inst_func):
766+ log("Calling install function: %s" % inst_func.__name__)
767+ inst_func(args)
768+ time.sleep(0.25)
769+ except Exception, e:
770+ log("Exception while trying to access install function for %s in %s" % (distname_lower, __name__))
771+ log("Exception was: %s" % str(e))
772+ time.sleep(0.25)
773+ sys.exit(1)
774+
775\ No newline at end of file
776
777=== added file 'acire/utils.py'
778--- acire/utils.py 1970-01-01 00:00:00 +0000
779+++ acire/utils.py 2010-05-06 10:56:27 +0000
780@@ -0,0 +1,47 @@
781+#! /usr/bin/env python
782+#coding=utf-8
783+### BEGIN LICENSE
784+# Copyright (C) 2010 Edgar D'Souza <edgar.b.dsouza@gmail.com>
785+#This program is free software: you can redistribute it and/or modify it
786+#under the terms of the GNU General Public License version 3, as published
787+#by the Free Software Foundation.
788+#
789+#This program is distributed in the hope that it will be useful, but
790+#WITHOUT ANY WARRANTY; without even the implied warranties of
791+#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
792+#PURPOSE. See the GNU General Public License for more details.
793+#
794+#You should have received a copy of the GNU General Public License along
795+#with this program. If not, see <http://www.gnu.org/licenses/>.
796+### END LICENSE
797+
798+import pygtk
799+pygtk.require("2.0")
800+import gtk
801+import sys
802+import platform
803+import string
804+
805+def not_implemented(function_name_string, show_dialog=False):
806+ """Utility function for outputting info message that the (pkg-mgt) function
807+ whose name is passed as the sole param is not yet implemented."""
808+
809+ msg = "%s: Not implemented yet, please contribute!" % function_name_string
810+
811+ print msg
812+
813+ if show_dialog:
814+ md = gtk.MessageDialog(None, gtk.DIALOG_DESTROY_WITH_PARENT,
815+ gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, msg)
816+ md.run()
817+ md.destroy()
818+
819+def funcname():
820+ """Utility function, returns the name of the function
821+ that calls this one."""
822+ return sys._getframe(1).f_code.co_name
823+
824+def distro_name():
825+ p = platform.linux_distribution()
826+ return string.lower(p[0])
827+
828\ No newline at end of file
829
830=== modified file 'bin/acire'
831--- bin/acire 2010-03-26 19:41:43 +0000
832+++ bin/acire 2010-05-06 10:56:27 +0000
833@@ -48,6 +48,7 @@
834
835 from acire import AboutAcireDialog, PreferencesAcireDialog
836 from acire.acireconfig import getdatapath
837+from acire import depchecker
838
839 # Set up translations
840 import gettext
841@@ -103,14 +104,14 @@
842 self.terminal_expander = self.builder.get_object("terminal_expander")
843 self.status_label = self.builder.get_object("status_label")
844 self.docs_box = self.builder.get_object("docs_box")
845+ self.right_pane_vbox = self.builder.get_object("vbox4")
846
847 # set up source view
848
849 self.editor_buffer = gtksourceview2.Buffer()
850 self.editor_view = gtksourceview2.View(self.editor_buffer)
851 self.editor_view.set_show_line_numbers(True)
852-
853-
854+
855 # read system's monospace font, fallback to 'monospace 10'
856 self.gconf = gconf.client_get_default()
857 gconf_mono_font = self.gconf.get_string("/desktop/gnome/interface/monospace_font_name")
858@@ -251,6 +252,15 @@
859 self.location_label.set_text(self.current_filename)
860 self.description_label.set_text(self.snippetsdata[self.current_filename]["description"])
861
862+ ###Snippet modules dependency checker (depchecker) invocation
863+ # Remove the eventbox for missing modules - it will be added again if
864+ # needed for the snippet that is just loaded.
865+ depchecker.remove_eventbox()
866+
867+ # Check the script for missing modules, alert user.
868+ check_results = depchecker.check_script(self.current_filename, self.right_pane_vbox)
869+ ###Snippet modules dependency checker (depchecker) invocation -- ENDS
870+
871 # update docs
872
873 ## first delete any existing docs buttons:

Subscribers

People subscribed via source and target branches

to all changes: