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

Proposed by Ed S
Status: Needs review
Proposed branch: lp:~edgar-b-dsouza/acire/snippet_dependency_res_1
Merge into: lp:acire
Diff against target: 518 lines (+480/-0)
5 files modified
acire/SnippetdependenciesDialog.py (+167/-0)
acire/utils.py (+138/-0)
bin/acire (+15/-0)
data/ui/SnippetdependenciesDialog.ui (+151/-0)
data/ui/snippetdependencies_dialog.xml (+9/-0)
To merge this branch: bzr merge lp:~edgar-b-dsouza/acire/snippet_dependency_res_1
Reviewer Review Type Date Requested Status
Ed S (community) Needs Information
Jono Bacon Needs Fixing
Review via email: mp+21924@code.launchpad.net

Description of the change

First-cut implementation of "snippet module dependency resolution", using only on-the-system software repositories.

Still pending ideas of how to query Launchpad for PPAs that supply packages used by snippets, that are not in official repos.

Had to switch code last-minute from using dpkg-query to using 'aptitude search' since dpkg-query was shortchanging me on the results :-( wonder if that's a bug!

To post a comment you must log in.
Revision history for this message
Jono Bacon (jonobacon) wrote :

Thanks for doing this work, Ed! I merged it into my local copy and I see you added some dialogs but I don't see how they are exposed in the UI, so I could not test it. How do I see it working?

Also, I think we need to think carefully about an elegant way of exposing this in the UI. My thinking of what could be cool would be to put a yellow bar at the top of the source view that says:

  This snippet requires additional Python modules to run. Click here to install them...

You would then click the yellow area and dialog box would inform you of what needs installing and grab them from the repos.

As for supporting third-party PPAs for modules, I think this is out of scope for a first cut: I would not bother focusing on that - just support the main repos.

One final consideration is that I would like to ensure this is as pluggable as possible for different distros: I don't want to make Acire depend on Ubuntu. So it could be useful to detect which distro Acire is running on and then have a plugin system that implements how Acire interacts with different distros. This means that if someone runs on Fedora as an example, the module search will use yum.

Thanks for your hard work, Ed!

review: Needs Fixing
Revision history for this message
Ed S (edgar-b-dsouza) wrote :

Hi, Jono.

The checking is hooked into bin/acire in the snippet_selected() function, just below your new docs_box code - search for:
"check_results = utils.check_script"

See it working:
a) Module with installation candidate package:
E.g. python-clutter (if installed on your system, you could temporarily uninstall it to test?). When you visit a Clutter snippet, SnippetdependenciesDialog should pop up and prompt you to install it.

b) Module with no installation candidate package:
In my case, that was Zeitgeist. If you have a PPA for Zeitgeist, and can temp-uninstall the package, temp-remove the PPA, and restart Acire to refresh the list of available packages - and then visit a Zeitgeist snippet. The Info Page button should take you to https://wiki.ubuntu.com/PythonSnippets_Dependencies (which I put up yesterday, please read/edit it?).

elegant way of exposing this in the UI:
I like your idea. I'd thought of notifying in statusbar, but that was too unobtrusive. Your yellow bar is hard for the user to miss. I'll look for a GTK control to use, and will propose a subsequent feature branch merge.

third-party PPAs for modules out of scope for a first cut:
Yes, I agree :-) that's why I called it a first cut :-). Like I asked in question #104601, (for a later feature branch), I need some pointers/ideas on how to go about that. OTOH, if you think the Wiki page I mentioned is a viable stopgap for some time, that's cool too.

as pluggable as possible for different distros:
Yes, I'd thought of that but wanted to first focus on feature addition; I find it a little complex to simultaneously add in such abstractions. If you like, I can work on that earlier (let me know) to:
- move the distro-specific functions into separate module scripts
- add distro-sniff code in the generic 'utils.py'
- use the distro-sniff to import the relevant pkg-handling module script

Happy to put in the work :-) and am hoping I may feature in authors/Help > About at some point...

Thanks,
Ed.

Revision history for this message
Jono Bacon (jonobacon) wrote :

Thanks Ed for the continued work on this: this is shaping up really nicely!

First, in terms of the UI, why don't you first add a GTK label at the top of the source view which says which modules are required and a button that allows someone to kick off the installation process.

In terms of supporting multiple distros, I love that you have kept the logic separated out into utils.py and it could be useful to first get it running for Ubuntu, we can then focus in on supporting other distributions and merge that logic into utils.py too.

I think for PPA element, the wiki page is ideal: to be quite frank, I am unlikely to accept a snippet if it is not installable from the archive anyway. :-)

Does this sound OK?

Thanks Ed, you rock the house. :-)

Revision history for this message
Ed S (edgar-b-dsouza) wrote :

Jono: sounds good, but I have a couple of questions.

Add GTK label & button - OK, will do that:

Q1. ...but show the Snippetdependencies dialog on click of the button, right? Or do you want all the relevant code from that .py file pulled in to clutter up bin/acire? :-)

Q2. Add them right away and push updated code to the same feature branch? Is that possible without messing up the review process? Or push it to another feature branch and propose that for merge instead? Sorry, still quite a n00b :-)

Re PPA - well, if that's dropped, I'm relieved :-P since it was quite a hurdle anyway.

Revision history for this message
Jono Bacon (jonobacon) wrote :

Hi Ed!

Q1 - my recommendation would be the following use case:

  1. User loads a snippet that does not have a module installed.
  2. Above the source view a little bar appears (like the plugin missing bar in Firefox) where it says what module(s) are missing and a button called 'Install'.
  3. User clicks the 'Install' button, a dialog box pops up to confirm what will be installed, the user clicks the 'OK' button, is asked for password and then a dialog displays progress of the installation.
  4. The bar disappears from the snippet.

I would prefer that the code for this feature does not clutter up bin/acire if possible. Probably best is that the logic lives in a file called moduleinstaller.py and there is a separate .py file with the dialog box.

Sound doable?

Q2 - I would like you to sync your branch to the current lp:acire branch and work in your worn branch. We can then test it until it is working and then I will merge it into lp:acire. :-)

Glad the PPA side of things is dropped: let's keep the core feature our focus.

Thanks, Ed, you rock! :-)

Revision history for this message
Ed S (edgar-b-dsouza) wrote :

On Wed, Mar 24, 2010 at 12:37 PM, Jono Bacon <email address hidden> wrote:
>  1. User loads a snippet that does not have a module installed.
>  2. Above the source view a little bar appears (like the plugin missing bar in Firefox) where it says what module(s) are missing and a button called 'Install'.
>  3. User clicks  the 'Install' button, a dialog box pops up to confirm what will be installed, the user clicks the 'OK' button, is asked for password and then a dialog displays progress of the installation.
>  4. The bar disappears from the snippet.
>
> I would prefer that the code for this feature does not clutter up bin/acire if possible. Probably best is that the logic lives in a file called moduleinstaller.py and there is a separate .py file with the dialog box.
>
> Sound doable?

Yes, but it will take me some time to do the changes, especially
replacing the gnome-terminal with progress dialog - need to research
for how to do that. Give me a few days.

> Q2 - I would like you to sync your branch to the current lp:acire branch and work in your worn branch. We can then test it until it is working and then I will merge it into lp:acire. :-)

I'll get the stuff above working first as you have asked, then update
my local repo to tip and meld in my changes, then push to a feature
branch on LP.

You didn't answer - overwrite/update this same feature branch which
I've proposed for merge, or push to a new one?

Thanks
Ed.

Revision history for this message
Jono Bacon (jonobacon) wrote :

Hi Ed!

No worries about it taking time: today I am going to try and get 0.5 released (which is the code currently in lp:acire). I don't plan on any changes to Acire in the next few weeks. This will provide a stable target for you to hack against.

As for branches: I don't mind if you use the same branch or a new one, so long as when the work is complete I have a branch that I can cleanly merge into lp:acire.

Ed, you rock, seriously! Also, we have 149 snippets, and Acire becomes more and more useful every day! :-)

Revision history for this message
Ed S (edgar-b-dsouza) wrote :

On Thu, Mar 25, 2010 at 11:58 PM, Jono Bacon <email address hidden> wrote:
> Hi Ed!
>
> No worries about it taking time: today I am going to try and get 0.5 released (which is the code currently in lp:acire). I don't plan on any changes to Acire in the next few weeks. This will provide a stable target for you to hack against.

Oh, good! Keeping on pulling revisions and merging code again is a bit
of a pain... yeah, I know that's how collaborative dev is done, but
still :)
Also, I could propose for merge a later feature branch (after you
agree we've got this dependency thing in and looking good) with some
quick changes I have in mind (below).

> As for branches: I don't mind if you use the same branch or a new one, so long as when the work is complete I have a branch that I can cleanly merge into lp:acire.

OK, thanks for clearing that up.

Yes, I understand... two-way merges are enough trouble for me, when it
gets more complicated... ugh! I've done my best to have each new
feature branch as a revision on tip; for Acire, where we don't have
many people working on it, this is easy to do, but for bigger teams...
project leader really has a tough time, I guess!

> Ed, you rock, seriously! Also, we have 149 snippets, and Acire becomes more and more useful every day! :-)

Heh - thanks! :-) If I apply for Ubuntu Membership, I'm going to ask
you to sign my page :-)

I have some quick ideas I'd like to try:
- add a pushbutton to clear the VTEterm. Also add a checkbox
"Auto-clear on each run". For the occasional snippet that does
terminal output, it can get confusing for the user to determine which
is the latest output, especially if user is modifying code in the
GTKSourceView.
- shift the Exec/Copy/SaveAs buttonbar below the SourceView - it's a
more logical place for it in the left-to-right, top-to-bottom flow for
the UI: load code in the sourceview using the combo and treeview,
(optionally) edit the code, hit Exec, see output in the VTETerm below.
- see if I can (at runtime) add the shortcuts for the three buttons
into their captions. Additionally, I'm thinking of changing the Copy
button shortcut... when I want to copy only a portion of the snippet
from the sourceview, I hit Ctrl-C and the button copies ALL the
snippet to the clipboard. I've been bitten several times (and I added
the shortcut! :-( ) -- I think other users might be annoyed too. Wish
we had more feedback.

Revision history for this message
Jono Bacon (jonobacon) wrote :

> Oh, good! Keeping on pulling revisions and merging code again is a bit
> of a pain... yeah, I know that's how collaborative dev is done, but
> still :)

:-)

> Also, I could propose for merge a later feature branch (after you
> agree we've got this dependency thing in and looking good) with some
> quick changes I have in mind (below).

Sounds good. :-)

Just so you know, I just released 0.4 (http://aciresnippets.wordpress.com/2010/03/26/acire-0-4-released/) - I may merge a few small changes in there and bug fixes: a few people have a few things that are working on.
>
> Yes, I understand... two-way merges are enough trouble for me, when it
> gets more complicated... ugh! I've done my best to have each new
> feature branch as a revision on tip; for Acire, where we don't have
> many people working on it, this is easy to do, but for bigger teams...
> project leader really has a tough time, I guess!

Indeed! While there will be a few changes here and there, I recommend you regularly update your branch to ensure you don't need to rebase it much. :-)

> > Ed, you rock, seriously! Also, we have 149 snippets, and Acire becomes more
> and more useful every day! :-)
>
> Heh - thanks! :-) If I apply for Ubuntu Membership, I'm going to ask
> you to sign my page :-)

Bring it on!

> I have some quick ideas I'd like to try:
> - add a pushbutton to clear the VTEterm. Also add a checkbox
> "Auto-clear on each run". For the occasional snippet that does
> terminal output, it can get confusing for the user to determine which
> is the latest output, especially if user is modifying code in the
> GTKSourceView.

Another contributor is actually working on clearing the terminal. :-)

> - shift the Exec/Copy/SaveAs buttonbar below the SourceView - it's a
> more logical place for it in the left-to-right, top-to-bottom flow for
> the UI: load code in the sourceview using the combo and treeview,
> (optionally) edit the code, hit Exec, see output in the VTETerm below.

Yeah, I have been thinking about this too. We may want to fix this.

> - see if I can (at runtime) add the shortcuts for the three buttons
> into their captions. Additionally, I'm thinking of changing the Copy
> button shortcut... when I want to copy only a portion of the snippet
> from the sourceview, I hit Ctrl-C and the button copies ALL the
> snippet to the clipboard. I've been bitten several times (and I added
> the shortcut! :-( ) -- I think other users might be annoyed too. Wish
> we had more feedback.

Aha, good idea.

So maybe we can fix these issues after you land the dep checker?

Thanks, Ed!

Revision history for this message
Ed S (edgar-b-dsouza) wrote :

On Sat, Mar 27, 2010 at 12:38 AM, Jono Bacon <email address hidden> wrote:
> Just so you know, I just released 0.4 (http://aciresnippets.wordpress.com/2010/03/26/acire-0-4-released/) - I may merge a few small changes in there and bug fixes: a few people have a few things that are working on.

Nice. :-)

> Bring it on!

OK, at some point in the future :)

> Another contributor is actually working on clearing the terminal. :-)

Ah - OK, good. I'm curious to see if s/he finds any other way than
resetting the terminal, which is all I could figure out.
Er - if I might ask - is there a mailing list, or IRC channel or
something where contributors discuss?

>> - shift the Exec/Copy/SaveAs buttonbar below the SourceView
> Yeah, I have been thinking about this too. We may want to fix this.

Not a big change, maybe it can be done even before the dep checker.

>> - see if I can (at runtime) add the shortcuts for the three buttons
> Aha, good idea.
> So maybe we can fix these issues after you land the dep checker?

If they're not done by then, yes, I will.

Revision history for this message
Jono Bacon (jonobacon) wrote :

> > Another contributor is actually working on clearing the terminal. :-)
>
> Ah - OK, good. I'm curious to see if s/he finds any other way than
> resetting the terminal, which is all I could figure out.

This is now committed: it was using vte.reset() - it clears the terminal before running the snippet and works nicely. :-)

As I said before: I recommend you keep pulling from lp:acire to ensure your branch is updated. I have another few bugfixes I need to merge in.

> Er - if I might ask - is there a mailing list, or IRC channel or
> something where contributors discuss?

Nope: the project is pretty small right now, so I think we can just do this in a place such as this until we have more contributors and set up a list.

> >> - shift the Exec/Copy/SaveAs buttonbar below the SourceView
> > Yeah, I have been thinking about this too. We may want to fix this.
>
> Not a big change, maybe it can be done even before the dep checker.

Personally, I would recommend you focus on the dep checker and not mix features: just makes it easier for us to ensure we get a clean implementation. :-)

> >> - see if I can (at runtime) add the shortcuts for the three buttons
> > Aha, good idea.
> > So maybe we can fix these issues after you land the dep checker?
>
> If they're not done by then, yes, I will.

Sweet. :-)

Revision history for this message
Ed S (edgar-b-dsouza) wrote :

On Sat, Mar 27, 2010 at 10:44 AM, Jono Bacon <email address hidden> wrote:
> Personally, I would recommend you focus on the dep checker and not mix features: just makes it easier for us to ensure we get a clean implementation. :-)

Will do :)

Is it OK to use python-apt? Will the dependency be added when
packaging for the PPA?

Revision history for this message
Jono Bacon (jonobacon) wrote :

Yep. :-)

Revision history for this message
Jono Bacon (jonobacon) wrote :

Hi Ed,

I just wanted to let you know that I am finding more and more bug fixes that I need to fix in Acire, so I expect lp:acire to change more frequently. I just wanted to make sure I gave you a heads up this to keep syncing your branch with lp:acire.

We discussed this before, but I would still recommend you keep the vast majority of your logic in a separate source file (e.g. depchecker.py) and then just hook it into bin/acire to integrate it into the app. This will mean that the regular changes in bin/acire will not screw up your branch. :-)

Thanks again for all your hard work, Ed!

Revision history for this message
Ed S (edgar-b-dsouza) wrote :

On Mon, Mar 29, 2010 at 1:26 AM, Jono Bacon <email address hidden> wrote:
> Hi Ed,
>
> I just wanted to let you know that I am finding more and more bug fixes that I need to fix in Acire, so I expect lp:acire to change more frequently. I just wanted to make sure I gave you a heads up this to keep syncing your branch with lp:acire.
>
> We discussed this before, but I would still recommend you keep the vast majority of your logic in a separate source file (e.g. depchecker.py) and then just hook it into bin/acire to integrate it into the app. This will mean that the regular changes in bin/acire will not screw up your branch. :-)

Thanks for the heads-up, Jono... I tried updating the other day, and
Bazaar Explorer tells me I'm already on a "divergent" branch :-P so
what I'm doing is just what you say - keep as much of the new code as
possible outside bin/acire and its .ui file. That will make merging
easier for me just before uploading for merge proposal. I'm still
refactoring/writing new code/testing and debugging, so merge proposal
is a few days away yet (had some other things take up time, so haven't
put in as much time as I'd like on Acire). My paying work cycle starts
on the 8th (to the 21st) so I intend to have this feature merged (or
at least proposed) by then, since I'll most likely be unable to work
on the project at all during those days.

Thanks,
Ed.

Revision history for this message
Jono Bacon (jonobacon) wrote :

> Thanks for the heads-up, Jono... I tried updating the other day, and
> Bazaar Explorer tells me I'm already on a "divergent" branch :-P so
> what I'm doing is just what you say - keep as much of the new code as
> possible outside bin/acire and its .ui file. That will make merging
> easier for me just before uploading for merge proposal. I'm still
> refactoring/writing new code/testing and debugging, so merge proposal
> is a few days away yet (had some other things take up time, so haven't
> put in as much time as I'd like on Acire). My paying work cycle starts
> on the 8th (to the 21st) so I intend to have this feature merged (or
> at least proposed) by then, since I'll most likely be unable to work
> on the project at all during those days.

This all sounds great, Ed. What would also be great to factor into your design and code is to possibly have a depchecker.py file which deals with the general tasks of checking which modules a snippet uses and detecting the OS, and then loading a separate .py file which has a standard set of methods for dealing with the logic of searching for and installing packages. This means that we could then open it up to other distros to simply provide a .py file appropriate to them.

Just a thought.

Thanks, Ed!

Revision history for this message
Ed S (edgar-b-dsouza) wrote :

On Mon, Mar 29, 2010 at 9:25 AM, Jono Bacon <email address hidden> wrote:
> This all sounds great, Ed. What would also be great to factor into your design and code is to possibly have a depchecker.py file which deals with the general tasks of checking which modules a snippet uses and detecting the OS, and then loading a separate .py file which has a standard set of methods for dealing with the logic of searching for and installing packages. This means that we could then open it up to other distros to simply provide a .py file appropriate to them.

:-) Already working on that, Jono - I found that the built-in platform
module's linux_distribution() function returns a tuple of which the
first is the distro name; using that, I'm writing a
"distropkgutils.py" in which I'm building a class that (in __init__)
will grab selected functions (defined in the file), based on the
distro, and expose those as its methods - so depchecker.py can simply
call methods of this class for all pkg-mgt-related work (including
retrieving a list of uninstalled packages, which I've hard-coded with
aptitude search now) without having to "know" about the underlying
package mgt mechanism. I saw your point, that it's way better to do
this the portable way right now, rather than get it into trunk and
then do several revisions to make it distro-portable.
Of course, the functions for distros other than Ubuntu will be a
"not_implemented" message stub, which other contributors can flesh out
later (my Fedora 12 VM crashed yesterday :-( so I'll just focus on
Ubuntu right now).

Revision history for this message
Jono Bacon (jonobacon) wrote :

Sounds great, Ed! :-)

Revision history for this message
Ed S (edgar-b-dsouza) wrote :

Jono, quick question:
To present the Firefox-style yellow bar with button, I propose to use an event box which contains an hbox inside which is a label and button.

I'd earlier put this into the Acire .ui file using Glade, but was wondering whether I could ask you to give a specific name to the vbox "vbox4" (which contains the editor viewport, docs_box etc), such as "right_pane_vbox" (or better name); and if I could just pass a reference to this vbox to depchecker.check_script() in snippet_selected() in bin/acire -- then build the partial UI mentioned above, on the fly. Would this be OK, or would you prefer to have all the UI components in the .ui file - i.e. added via Glade?

If the latter, are you still hacking bin/acire and its .ui file? When do you expect to push an updated version to trunk, that I can pull and apply changes to?

review: Needs Information
Revision history for this message
Ed S (edgar-b-dsouza) wrote :

Jono, hope you have some time to review the new branch (lp:~edgar-b-dsouza/acire/snippet_dependency_res_2 ) which I pushed now, since Bazaar Explorer refused to push to this branch.

Unmerged revisions

44. By Ed S

First-cut implementation of snippet dependency detection and resolution. Still needs attention to querying LP for PPAs to add to system repos.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'acire/SnippetdependenciesDialog.py'
2--- acire/SnippetdependenciesDialog.py 1970-01-01 00:00:00 +0000
3+++ acire/SnippetdependenciesDialog.py 2010-03-23 11:35:27 +0000
4@@ -0,0 +1,167 @@
5+# -*- coding: utf-8 -*-
6+### BEGIN LICENSE
7+# Copyright (C) 2010 Edgar D'Souza <edgar.b.dsouza@gmail.com>
8+#This program is free software: you can redistribute it and/or modify it
9+#under the terms of the GNU General Public License version 3, as published
10+#by the Free Software Foundation.
11+#
12+#This program is distributed in the hope that it will be useful, but
13+#WITHOUT ANY WARRANTY; without even the implied warranties of
14+#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15+#PURPOSE. See the GNU General Public License for more details.
16+#
17+#You should have received a copy of the GNU General Public License along
18+#with this program. If not, see <http://www.gnu.org/licenses/>.
19+### END LICENSE
20+
21+import sys
22+import os
23+import gtk
24+import subprocess
25+import webbrowser
26+
27+from acire.acireconfig import getdatapath
28+
29+class SnippetdependenciesDialog(gtk.Dialog):
30+ __gtype_name__ = "SnippetdependenciesDialog"
31+
32+ def __init__(self):
33+ """__init__ - This function is typically not called directly.
34+ Creation of a SnippetdependenciesDialog requires reading the associated
35+ ui file and parsing the ui definition externally,
36+ and then calling SnippetdependenciesDialog.finish_initializing().
37+
38+ Use the convenience function NewSnippetdependenciesDialog to create
39+ a SnippetdependenciesDialog object.
40+
41+ """
42+ self.infoURL = "https://wiki.ubuntu.com/PythonSnippets_Dependencies"
43+
44+ def finish_initializing(self, builder):
45+ """finish_initalizing should be called after parsing the ui definition
46+ and creating a SnippetdependenciesDialog object with it in order to finish
47+ initializing the start of the new SnippetdependenciesDialog instance.
48+
49+ """
50+ #get a reference to the builder and set up the signals
51+ self.builder = builder
52+ self.builder.connect_signals(self)
53+
54+ # references to ui widgets
55+ self.missingmods_textview = self.builder.get_object("missingmods_textview")
56+ self.actions_label = self.builder.get_object("actions_label")
57+ self.ok_button = self.builder.get_object("ok_button")
58+ self.web_linkbutton = self.builder.get_object("web_linkbutton")
59+
60+ def set_messages(self, check_results):
61+ """
62+ In snippet_selected() in class AcireWindow, code calls
63+ utils.check_script() on current snippet. That returns a dictionary
64+ containing the following items:
65+ {'missing' : [], 'candidates' : [], 'commandline' : ""}
66+ 'missing' => list of packages judged as missing;
67+ 'candidates' => list of packages that could be installed; if this is
68+ not empty, then 'commandline' => apt-get install <pkgs> cmd line
69+ that can be exec'd in a VTEterm or Gnome-terminal, or copied by user
70+ to exec in own terminal.
71+ The check_results param to this function is the same dictionary. Code
72+ here has to handle various possibilities:
73+ a) Found missing deps, found candidate pkgs for all deps
74+ b) Found missing deps, found candidates for NOT all
75+ c) Found missing deps, found zero candidate pkgs
76+ The calling code is expected to check if there are no missing packages,
77+ and if so, this dialog won't be invoked.
78+ Intended procedure: if candidate packages found for ALL deps, then
79+ prompt user to run the install cmdline.
80+ If no candidates or partial candidates found, direct user to a resource
81+ on the Net (Ubuntu wiki page, self.infoURL). This is temporary
82+ until we find an efficient way to do LP queries for packages whose
83+ PPAs are NOT set up as repos on user's system - after which, this logic
84+ and dialog will change.
85+ """
86+ #Extract the dictionary members out into local vars
87+ missing = check_results['missing']
88+ candidates = check_results['candidates']
89+ commandline = check_results['commandline']
90+
91+ #Store command-line as an instance-level variable, against user
92+ #clicking the Install button.
93+ self.commandline = commandline
94+
95+ #The list of missing packages gets shown always.
96+ textbuffer = self.missingmods_textview.get_buffer()
97+ textbuffer.set_text("\n".join(missing))
98+
99+ if len(missing) == len(candidates):
100+ #Case a) Found missing deps, found candidate pkgs for all deps
101+ #TO-DO: Translator attention needed, please - next line.
102+ msgline1 = "You can install the required packages by clicking the Install button, which will run the command:"
103+ joiner = "%s\n\n%s"
104+ msg = joiner % (msgline1, commandline)
105+ self.actions_label.set_text(msg)
106+ self.ok_button.show()
107+ self.web_linkbutton.hide()
108+ else:
109+ #Cases (b) and (c) - prompt user to visit a site on the Net :-(
110+ #PPA query logic should handle this in a better way...
111+
112+ #TO-DO: Translator attention needed, please - next two lines.
113+ msgline1 = "Sorry, we could not find all required packages. You may need to add repositories in your package manager."
114+ msgline2 = "For more information/to post a query, please click the Info Page button to visit:"
115+ joiner = "%s\n%s\n%s"
116+ msg = joiner % (msgline1, msgline2, self.infoURL)
117+ self.actions_label.set_text(msg)
118+ self.ok_button.hide()
119+ self.web_linkbutton.show()
120+
121+
122+ def ok(self, widget, data=None):
123+ """ok - The user has clicked install_button
124+ Called before the dialog returns gtk.RESPONSE_OK from run().
125+
126+ """
127+ try:
128+ #print self.commandline
129+ md = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING,
130+ gtk.BUTTONS_CLOSE, "Please ensure that all package managers are closed, including Synaptic, Software Store, Update Manager etc.")
131+ md.run()
132+ md.destroy()
133+
134+ subprocess.check_call(["/usr/bin/gnome-terminal", "-e", self.commandline, "\n"])
135+ except subprocess.CalledProcessError, cpe:
136+ print "CalledProcessError: %s" % repr(cpe)
137+
138+ def goInfoURL(self, widget, data=None):
139+ """Take user to help/info page"""
140+ webbrowser.open_new_tab(self.infoURL)
141+
142+ def cancel(self, widget, data=None):
143+ """cancel - The user has elected cancel changes.
144+ Called before the dialog returns gtk.RESPONSE_CANCEL for run()
145+
146+ """
147+ pass
148+
149+def NewSnippetdependenciesDialog():
150+ """NewSnippetdependenciesDialog - returns a fully instantiated
151+ dialog-camel_case_nameDialog object. Use this function rather than
152+ creating SnippetdependenciesDialog instance directly.
153+
154+ """
155+
156+ #look for the ui file that describes the ui
157+ ui_filename = os.path.join(getdatapath(), 'ui', 'SnippetdependenciesDialog.ui')
158+ if not os.path.exists(ui_filename):
159+ ui_filename = None
160+
161+ builder = gtk.Builder()
162+ builder.add_from_file(ui_filename)
163+ dialog = builder.get_object("snippetdependencies_dialog")
164+ dialog.finish_initializing(builder)
165+ return dialog
166+
167+if __name__ == "__main__":
168+ dialog = NewSnippetdependenciesDialog()
169+ dialog.show()
170+ gtk.main()
171+
172
173=== added file 'acire/utils.py'
174--- acire/utils.py 1970-01-01 00:00:00 +0000
175+++ acire/utils.py 2010-03-23 11:35:27 +0000
176@@ -0,0 +1,138 @@
177+#! /usr/bin/env python
178+#coding=utf-8
179+### BEGIN LICENSE
180+# Copyright (C) 2010 Edgar D'Souza <edgar.b.dsouza@gmail.com>
181+#This program is free software: you can redistribute it and/or modify it
182+#under the terms of the GNU General Public License version 3, as published
183+#by the Free Software Foundation.
184+#
185+#This program is distributed in the hope that it will be useful, but
186+#WITHOUT ANY WARRANTY; without even the implied warranties of
187+#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
188+#PURPOSE. See the GNU General Public License for more details.
189+#
190+#You should have received a copy of the GNU General Public License along
191+#with this program. If not, see <http://www.gnu.org/licenses/>.
192+### END LICENSE
193+
194+import modulefinder
195+import subprocess
196+import pprint
197+
198+def get_pkg_list():
199+ """Run subprocess with aptitude search to fetch a list of package names
200+ with regex 'python-*'; filter it to keep those which are not installed.
201+ Returns: list of uninstalled python packages.
202+ Caveats:
203+ - depends on aptitude being available
204+ - obviously doesn't show candidate if repo (e.g. LP) not added to apt
205+ sources - how to deal with that??
206+ """
207+ ###BUG?? dpkg-query returns partial results, apparently only from main, but
208+ ###not from universe...? Synaptic shows python-clutter, but the commented
209+ ###code below does not return it - gah!
210+ ###Rewriting this with aptitude search for now, which appears to return
211+ ###python-clutter (and other pkgs in universe)
212+# cmd = "/usr/bin/dpkg-query -W --showformat='${Package}\$${Version} \n' 'python-*'"
213+# proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
214+# pkg_list = proc.communicate()[0]
215+# #Convert to a list of packages, filtering out those where the Version is
216+# #non-blank (i.e. installed)
217+# python_uninst_pkgs = []
218+# for pkg in pkg_list.split("\n"):
219+# if len(pkg.strip()) > 0:
220+# parts = pkg.split("$")
221+# #print parts
222+# if len(parts[1].strip()) == 0:
223+# python_uninst_pkgs.append(parts[0])
224+# return python_uninst_pkgs
225+
226+ cmd = "/usr/bin/aptitude search '^python-[a-z]*$' --disable-columns -F '%p\$%v'"
227+ proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
228+ pkg_list = proc.communicate()[0]
229+ #Convert to a list of packages, filtering to keep those where the Version
230+ #is "<none>" (i.e. not installed)
231+ python_uninst_pkgs = []
232+ for pkg in pkg_list.split("\n"):
233+ if len(pkg.strip()) > 0:
234+ parts = pkg.split("$")
235+ #print parts
236+ if parts[1].strip() == "<none>":
237+ python_uninst_pkgs.append(parts[0])
238+ return python_uninst_pkgs
239+
240+
241+#Grab a list of packages immediately upon import of this module.
242+python_pkgs = get_pkg_list()
243+
244+
245+def find_inst_candidates(modulename):
246+ """Uses the (from this module) global var python_pkgs to do a simple check to
247+ see if the modulename passed is 'in' a package name. If yes, adds it to a
248+ list that is returned.
249+ Crude, yes - but all I could think of right now. Better ideas gratefully
250+ welcomed - please file a bug with your suggestion or patch. Thanks!
251+ Already one irritant - transition/meta-packages are shown as multiple candidates
252+ along with the actual package to be installed. e.g. python-wxgtk, python-wxgtk2.8
253+ """
254+ global python_pkgs
255+
256+ candidates = []
257+ for pkg in python_pkgs:
258+ if modulename in pkg:
259+ candidates.append(pkg)
260+ print "candidate package: " + pkg
261+ return candidates
262+
263+
264+def check_script(filename):
265+ """Actual entry point for checking a script, to be called when snippet is
266+ loaded from file.
267+ filename - path/filename to the Python script to be checked.
268+ Returns dictionary of 3 items:
269+ - Detected missing modules list; key is "missing"
270+ - List of candidate packages for installation, if found, by matching
271+ against all "python-*" named packages in **currently configured
272+ repos on the system** (no concrete ideas for suggesting new repos)
273+ Dict key for this is "candidates"
274+ - IF (and only if) one or more candidate packages were found, then a
275+ suggested command-line for user to execute. If no candidates found,
276+ then a blank string is returned. Dict key: "commandline"
277+ """
278+
279+ modfinder = modulefinder.ModuleFinder()
280+ modfinder.run_script(filename)
281+ missing, maybe = modfinder.any_missing_maybe()
282+ missing_in_main = []
283+ candidate_pkgs = []
284+ #Due to bad docs, next 3 lines copied from modulefinder.report() and modded
285+ if missing:
286+ for name in missing:
287+ mods = modfinder.badmodules[name].keys()
288+ if "__main__" in mods:
289+ missing_in_main.append(name)
290+ #print "missing_in_main:" ,
291+ #pprint.pprint(missing_in_main)
292+ print
293+
294+ for mod in missing_in_main:
295+ c = find_inst_candidates(mod)
296+ if len(c) > 0:
297+ candidate_pkgs += c
298+ #print "candidate_pkgs:" ,
299+ #pprint.pprint(candidate_pkgs)
300+ #print
301+
302+ commandline = ""
303+ if len(candidate_pkgs) > 0:
304+ commandline = "sudo apt-get install %s"
305+ commandline = commandline % " ".join(candidate_pkgs)
306+ out_dict = {'missing' : missing_in_main,
307+ 'candidates' : candidate_pkgs,
308+ 'commandline' : commandline}
309+ return out_dict
310+
311+if __name__ == "__main__":
312+ print "Import this as a module and run utils.check_script('filename.py')"
313+ print "to examine the script for dependency modules."
314+ print "See check_script() docstring for returned dict contents."
315
316=== modified file 'bin/acire'
317--- bin/acire 2010-03-20 22:59:56 +0000
318+++ bin/acire 2010-03-23 11:35:27 +0000
319@@ -47,7 +47,9 @@
320 sys.path.insert(0, os.path.dirname(fullPath))
321
322 from acire import AboutAcireDialog, PreferencesAcireDialog
323+from acire import SnippetdependenciesDialog
324 from acire.acireconfig import getdatapath
325+from acire import utils
326
327 # Set up translations
328 import gettext
329@@ -284,6 +286,19 @@
330
331 self.docs_box.show()
332
333+ # Added by Ed S, on trial: check the script for modules missing
334+ # on this system, and try to suggest installation candidates.
335+ check_results = utils.check_script(self.current_filename)
336+ if len(check_results['missing']) > 0:
337+ sn_dep_dlg = SnippetdependenciesDialog.NewSnippetdependenciesDialog()
338+ sn_dep_dlg.set_messages(check_results)
339+ resp = sn_dep_dlg.run()
340+ sn_dep_dlg.hide()
341+ if gtk.RESPONSE_OK == resp:
342+ global python_pkgs
343+ python_pkgs = utils.get_pkg_list()
344+
345+
346 def run_snippet(self, widget, data=None):
347 """Run the currently selected snippet"""
348
349
350=== added file 'data/ui/SnippetdependenciesDialog.ui'
351--- data/ui/SnippetdependenciesDialog.ui 1970-01-01 00:00:00 +0000
352+++ data/ui/SnippetdependenciesDialog.ui 2010-03-23 11:35:27 +0000
353@@ -0,0 +1,151 @@
354+<?xml version="1.0"?>
355+<interface>
356+ <requires lib="gtk+" version="2.16"/>
357+ <!-- interface-requires snippetdependencies_dialog 1.0 -->
358+ <!-- interface-naming-policy project-wide -->
359+ <object class="SnippetdependenciesDialog" id="snippetdependencies_dialog">
360+ <property name="border_width">5</property>
361+ <property name="icon">../media/icon.png</property>
362+ <property name="type_hint">normal</property>
363+ <property name="has_separator">False</property>
364+ <child internal-child="vbox">
365+ <object class="GtkVBox" id="dialog-vbox1">
366+ <property name="visible">True</property>
367+ <property name="orientation">vertical</property>
368+ <property name="spacing">2</property>
369+ <child>
370+ <object class="GtkVBox" id="vbox1">
371+ <property name="visible">True</property>
372+ <property name="orientation">vertical</property>
373+ <child>
374+ <object class="GtkLabel" id="intro_label">
375+ <property name="visible">True</property>
376+ <property name="xalign">0.40000000596046448</property>
377+ <property name="yalign">0</property>
378+ <property name="xpad">5</property>
379+ <property name="ypad">5</property>
380+ <property name="label" translatable="yes" comments="Static text, informative, not changed at run-time">The snippet you have chosen appears to be missing one or more modules (libraries) that it needs in order to run.
381+
382+The missing module(s) are:</property>
383+ <attributes>
384+ <attribute name="weight" value="semibold"/>
385+ <attribute name="gravity" value="north"/>
386+ <attribute name="gravity-hint" value="natural"/>
387+ </attributes>
388+ </object>
389+ <packing>
390+ <property name="fill">False</property>
391+ <property name="position">0</property>
392+ </packing>
393+ </child>
394+ <child>
395+ <object class="GtkScrolledWindow" id="scrolledwindow1">
396+ <property name="visible">True</property>
397+ <property name="can_focus">True</property>
398+ <property name="hscrollbar_policy">automatic</property>
399+ <property name="vscrollbar_policy">automatic</property>
400+ <property name="window_placement">bottom-right</property>
401+ <child>
402+ <object class="GtkTextView" id="missingmods_textview">
403+ <property name="visible">True</property>
404+ <property name="can_focus">True</property>
405+ </object>
406+ </child>
407+ </object>
408+ <packing>
409+ <property name="position">1</property>
410+ </packing>
411+ </child>
412+ <child>
413+ <object class="GtkLabel" id="actions_label">
414+ <property name="visible">True</property>
415+ <property name="xalign">0</property>
416+ <property name="yalign">0</property>
417+ <property name="xpad">5</property>
418+ <property name="ypad">5</property>
419+ <property name="label" translatable="yes" comments="Changed at runtime by set_messages() in class SnippetdependenciesDialog in SnippetdependenciesDialog.py">[Changed at runtime by set_messages() in class SnippetdependenciesDialog in SnippetdependenciesDialog.py based on foll first-cut logic]
420+
421+If num missing modules == num candidate pkgs to install, then prompt to install.
422+
423+If not equal (could not find any / all pkgs to install), then refer user to site on Net to make enquiry.
424+</property>
425+ <attributes>
426+ <attribute name="weight" value="normal"/>
427+ <attribute name="gravity" value="north"/>
428+ <attribute name="gravity-hint" value="natural"/>
429+ </attributes>
430+ </object>
431+ <packing>
432+ <property name="fill">False</property>
433+ <property name="position">2</property>
434+ </packing>
435+ </child>
436+ </object>
437+ <packing>
438+ <property name="position">1</property>
439+ </packing>
440+ </child>
441+ <child internal-child="action_area">
442+ <object class="GtkHButtonBox" id="dialog-action_area1">
443+ <property name="visible">True</property>
444+ <property name="layout_style">end</property>
445+ <child>
446+ <object class="GtkButton" id="cancel_button">
447+ <property name="label">gtk-cancel</property>
448+ <property name="visible">True</property>
449+ <property name="can_focus">True</property>
450+ <property name="receives_default">True</property>
451+ <property name="use_stock">True</property>
452+ <signal name="clicked" handler="cancel"/>
453+ </object>
454+ <packing>
455+ <property name="expand">False</property>
456+ <property name="fill">False</property>
457+ <property name="position">0</property>
458+ </packing>
459+ </child>
460+ <child>
461+ <object class="GtkButton" id="ok_button">
462+ <property name="label" translatable="yes">_Install</property>
463+ <property name="can_focus">True</property>
464+ <property name="receives_default">True</property>
465+ <property name="no_show_all">True</property>
466+ <property name="use_underline">True</property>
467+ <signal name="clicked" handler="ok"/>
468+ </object>
469+ <packing>
470+ <property name="expand">False</property>
471+ <property name="fill">False</property>
472+ <property name="position">1</property>
473+ </packing>
474+ </child>
475+ <child>
476+ <object class="GtkButton" id="web_linkbutton">
477+ <property name="label" translatable="yes">Info Page</property>
478+ <property name="visible">True</property>
479+ <property name="can_focus">True</property>
480+ <property name="receives_default">True</property>
481+ <signal name="clicked" handler="goInfoURL"/>
482+ </object>
483+ <packing>
484+ <property name="expand">False</property>
485+ <property name="fill">False</property>
486+ <property name="position">2</property>
487+ </packing>
488+ </child>
489+ </object>
490+ <packing>
491+ <property name="expand">False</property>
492+ <property name="pack_type">end</property>
493+ <property name="position">0</property>
494+ </packing>
495+ </child>
496+ </object>
497+ </child>
498+ <action-widgets>
499+ <action-widget response="-6">cancel_button</action-widget>
500+ <action-widget response="-5">ok_button</action-widget>
501+ <action-widget response="0">web_linkbutton</action-widget>
502+ </action-widgets>
503+ </object>
504+</interface>
505
506=== added file 'data/ui/snippetdependencies_dialog.xml'
507--- data/ui/snippetdependencies_dialog.xml 1970-01-01 00:00:00 +0000
508+++ data/ui/snippetdependencies_dialog.xml 2010-03-23 11:35:27 +0000
509@@ -0,0 +1,9 @@
510+<glade-catalog name="snippetdependencies_dialog" domain="glade-3"
511+ depends="gtk+" version="1.0">
512+ <glade-widget-classes>
513+ <glade-widget-class title="sentence_name Dialog" name="SnippetdependenciesDialog"
514+ generic-name="snippetdependencies_dialog" parent="GtkDialog"
515+ icon-name="widget-gtk-dialog"/>
516+ </glade-widget-classes>
517+
518+</glade-catalog>

Subscribers

People subscribed via source and target branches

to all changes: