Hello community,

here is the log from the commit of package zeroinstall-injector for 
openSUSE:Factory checked in at 2013-02-14 21:19:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/zeroinstall-injector (Old)
 and      /work/SRC/openSUSE:Factory/.zeroinstall-injector.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "zeroinstall-injector", Maintainer is ""

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/zeroinstall-injector/zeroinstall-injector.changes    
    2013-01-17 13:18:13.000000000 +0100
+++ 
/work/SRC/openSUSE:Factory/.zeroinstall-injector.new/zeroinstall-injector.changes
   2013-02-14 21:19:36.000000000 +0100
@@ -1,0 +2,6 @@
+Wed Feb 13 10:39:33 UTC 2013 - tal...@gmail.com
+
+- Updated to 1.15. For a list of changes, see:
+  http://thread.gmane.org/gmane.comp.file-systems.zero-install.devel/6686
+
+-------------------------------------------------------------------

Old:
----
  0install-1.14.tar.bz2

New:
----
  0install-1.15.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ zeroinstall-injector.spec ++++++
--- /var/tmp/diff_new_pack.Q9kvEY/_old  2013-02-14 21:19:37.000000000 +0100
+++ /var/tmp/diff_new_pack.Q9kvEY/_new  2013-02-14 21:19:37.000000000 +0100
@@ -26,9 +26,9 @@
 %endif
 
 Name:           zeroinstall-injector
-Version:        1.14
+Version:        1.15
 Release:        0
-%define source_version 1.14
+%define source_version 1.15
 Summary:        Decentralised cross-distribution software installation
 License:        LGPL-2.1+
 Group:          System/Management

++++++ 0install-1.14.tar.bz2 -> 0install-1.15.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/0install.1 new/0install-1.15/0install.1
--- old/0install-1.14/0install.1        2013-01-14 12:33:10.000000000 +0100
+++ new/0install-1.15/0install.1        2013-02-12 16:35:03.000000000 +0100
@@ -1,4 +1,4 @@
-.TH 0INSTALL 1 "2012" "Thomas Leonard" ""
+.TH 0INSTALL 1 "2013" "Thomas Leonard" ""
 .SH NAME
 0install \(em a decentralised software installation system
 
@@ -240,7 +240,7 @@
 to create shortcuts to run your programs.
 
 .PP
-All options for `select' and `download' can also be used for `run'. In
+All options for `select' can also be used for `run' except for \fB\-\-xml\fP. 
In
 addition, these options are available:
 
 .TP
@@ -374,7 +374,7 @@
 is displayed.
 
 .SS 0install --version
-This can be used (without any command) the get version of 0install itself:
+This can be used (without any command) the get version of 0install itself.
 
 .SH APPLICATIONS
 
@@ -460,10 +460,9 @@
 
 .B $ 0install run \-\-wrapper="strace \-e open" http://myprog \-\-help
 
-If your program is interpreted (e.g. a Python program), and you wish to debug
-the interpreter running it, you can do it like this:
+To run the application under the gdb debugger:
 
-.B $ 0install run \-\-wrapper="gdb \-\-args python" http://myprog \-\-help
+.B $ 0install run \-\-wrapper="gdb \-\-args" http://myprog \-\-help
 
 .SH FILES
 
@@ -510,7 +509,7 @@
 
 .SH LICENSE
 .PP
-Copyright (C) 2012 Thomas Leonard.
+Copyright (C) 2013 Thomas Leonard.
 
 .PP
 You may redistribute copies of this program under the terms of the GNU Lesser 
General Public License.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/ZeroInstall.xml 
new/0install-1.15/ZeroInstall.xml
--- old/0install-1.14/ZeroInstall.xml   2013-01-14 12:33:10.000000000 +0100
+++ new/0install-1.15/ZeroInstall.xml   2013-02-12 16:35:03.000000000 +0100
@@ -46,11 +46,13 @@
 
     <restricts interface="http://repo.roscidus.com/python/python"; 
version="2.6..!3 | 3.2.2.."/>
 
+    <requires interface="http://repo.roscidus.com/python/python-gobject"; 
os="POSIX"/>
+
     <requires 
interface="http://0install.net/2012/interfaces/0install-runenv-cli.xml"; 
os="Windows">
       <environment insert="runenv.cli.template" mode="replace" 
name="ZEROINSTALL_CLI_TEMPLATE"/>
       <version not-before="1.12.1"/>
     </requires>
 
-    <implementation id="." released="2013-01-14" version="1.14"/>
+    <implementation id="." released="2013-02-12" version="1.15"/>
   </group>
 </interface>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/setup.py new/0install-1.15/setup.py
--- old/0install-1.14/setup.py  2013-01-14 12:33:10.000000000 +0100
+++ new/0install-1.15/setup.py  2013-02-12 16:35:03.000000000 +0100
@@ -103,6 +103,9 @@
                install.finalize_options(self)  # super.finalize_options()
                if self.home:
                        self.__config_dir = os.path.join(self.home, '.config')
+               elif self.user:
+                       from site import USER_BASE
+                       self.__config_dir = os.path.join(USER_BASE, 'etc/xdg')
                elif self.prefix == '/usr':
                        self.__config_dir = os.path.join(self.root or '/', 
'etc/xdg')
                else:
@@ -118,6 +121,13 @@
                if self.home:
                        self.run_command('adjust_scripts_for_home')
 
+if '--home' in sys.argv:
+       zsh_functions_dir = '.zsh'
+elif '--install-layout=deb' in sys.argv:
+       zsh_functions_dir = 'share/zsh/vendor-completions'
+else:
+       zsh_functions_dir = 'share/zsh/site-functions'
+
 setup(name="zeroinstall-injector",
       version=zeroinstall.version,
       description="The Zero Install Injector (0launch)",
@@ -128,7 +138,7 @@
       data_files = [('man/man1', ['0launch.1', '0alias.1', 
'0store-secure-add.1', '0store.1', '0desktop.1', '0install.1']),
                    ('share/applications', 
['share/applications/zeroinstall-add.desktop', 
'share/applications/zeroinstall-manage.desktop']),
                    ('share/bash-completion/completions', 
['share/bash-completion/completions/0install']),
-                   ('share/zsh/site-functions', 
['share/zsh/site-functions/_0install']),
+                   (zsh_functions_dir, ['share/zsh/site-functions/_0install']),
                    ('share/desktop-directories', 
['share/desktop-directories/zeroinstall.directory']),
                    ('share/icons/hicolor/24x24/apps', 
['share/icons/24x24/zeroinstall.png']),
                    ('share/icons/hicolor/48x48/apps', 
['share/icons/48x48/zeroinstall.png']),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/runnable/ArgList.xml 
new/0install-1.15/tests/runnable/ArgList.xml
--- old/0install-1.14/tests/runnable/ArgList.xml        1970-01-01 
01:00:00.000000000 +0100
+++ new/0install-1.15/tests/runnable/ArgList.xml        2013-02-12 
16:35:03.000000000 +0100
@@ -0,0 +1,31 @@
+<?xml version="1.0" ?>
+<interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface";>
+  <name>Runnable</name>
+  <summary>test script that needs a runner</summary>
+
+  <implementation id="test" local-path="." version="1">
+    <command name="run" path="script">
+      <runner interface='./Runner.xml' command='runme'>
+       <arg>arg-for-runner</arg>
+       <for-each item-from="RUNNER_ARGS">
+         <arg>-X</arg>
+         <arg>${item}</arg>
+       </for-each>
+      </runner>
+      <arg>command-arg</arg>
+      <for-each item-from="COMMAND_ARGS" separator=",">
+       <arg>${item}</arg>
+      </for-each>
+      <for-each item-from="__NOT_SET__" separator=",">
+       <arg>${foo}</arg>
+      </for-each>
+      <arg>--</arg>
+    </command>
+
+    <environment name='COMMAND_ARGS' value='ca1' separator=',' mode='append'/>
+    <environment name='COMMAND_ARGS' value='ca2' separator=',' mode='append'/>
+
+    <environment name='RUNNER_ARGS' value='ra1' mode='append'/>
+    <environment name='RUNNER_ARGS' value='ra2' mode='append'/>
+  </implementation>
+</interface>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/testdownload.py 
new/0install-1.15/tests/testdownload.py
--- old/0install-1.14/tests/testdownload.py     2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/tests/testdownload.py     2013-02-12 16:35:03.000000000 
+0100
@@ -15,7 +15,7 @@
 from zeroinstall.injector import model, gpg, download, trust, background, 
arch, selections, qdom, run
 from zeroinstall.injector.requirements import Requirements
 from zeroinstall.injector.driver import Driver
-from zeroinstall.zerostore import Store, NotStored
+from zeroinstall.zerostore import NotStored
 from zeroinstall.support import basedir, tasks, ro_rmtree
 from zeroinstall.injector import fetch
 import data
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/testescaping.py 
new/0install-1.15/tests/testescaping.py
--- old/0install-1.14/tests/testescaping.py     2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/tests/testescaping.py     2013-02-12 16:35:03.000000000 
+0100
@@ -1,9 +1,8 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
-import basetest
 from basetest import BaseTest
-import sys, os, re
+import sys, re
 import unittest
 
 sys.path.insert(0, '..')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/testinstall.py 
new/0install-1.15/tests/testinstall.py
--- old/0install-1.14/tests/testinstall.py      2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/tests/testinstall.py      2013-02-12 16:35:03.000000000 
+0100
@@ -5,7 +5,7 @@
 
 sys.path.insert(0, '..')
 from zeroinstall import cmd, logger, apps, alias
-from zeroinstall.injector import model, selections, qdom, handler, gpg, 
config, background
+from zeroinstall.injector import model, selections, qdom, handler, gpg, config
 
 mydir = os.path.dirname(__file__)
 
@@ -515,6 +515,18 @@
                assert "--- 2012-01-01" in out, out
                assert not err, err
 
+               # select detects changes
+               new_local = old_local.replace('0.1', '0.1-pre2')
+               with open(os.path.join(app.path, "selections.xml"), 'w') as 
stream:
+                       stream.write(new_local)
+               out, err = self.run_0install(['show', 'local-app'])
+               assert "Version: 0.1-pre2" in out, out
+               assert not err, err
+               out, err = self.run_0install(['select', 'local-app'])
+               assert "Local.xml: 0.1-pre2 -> 0.1" in out, out
+               assert "(note: use '0install update' instead to save the 
changes)" in out, out
+               assert not err, err
+
                assert 'local-app' in self.complete(['man'], 2)
                assert 'local-app' in self.complete(['destroy'], 2)
                self.assertEqual('', self.complete(['destroy', ''], 3))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/testlaunch.py 
new/0install-1.15/tests/testlaunch.py
--- old/0install-1.14/tests/testlaunch.py       2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/tests/testlaunch.py       2013-02-12 16:35:03.000000000 
+0100
@@ -13,7 +13,6 @@
 from zeroinstall import SafeException
 from zeroinstall.support import tasks
 from zeroinstall.injector import run, cli, namespaces, qdom, selections
-from zeroinstall.zerostore import Store
 from zeroinstall.injector.requirements import Requirements
 from zeroinstall.injector.driver import Driver
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/testmodel.py 
new/0install-1.15/tests/testmodel.py
--- old/0install-1.14/tests/testmodel.py        2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/tests/testmodel.py        2013-02-12 16:35:03.000000000 
+0100
@@ -13,6 +13,11 @@
 
 mydir = os.path.dirname(os.path.abspath(__file__))
 
+class DummyImpl:
+       def __init__(self, version, distro):
+               self.version = model.parse_version(version)
+               self.distro_name = distro
+
 class TestModel(BaseTest):
        def testLevels(self):
                assert model.network_offline in model.network_levels
@@ -252,6 +257,7 @@
                        assert False
                except model.SafeException as ex:
                        assert "Bad interface name 'CommandMissing.xml'" in 
str(ex), ex
+                       assert "/tests/CommandMissing.xml' either" in str(ex), 
ex
 
                # file:absolute
                
model.canonical_iface_uri('file://{path}/Command.xml'.format(path = mydir))
@@ -356,5 +362,19 @@
                fail('.2', "Invalid version format in '.2': invalid literal for 
int() with base 10: ''")
                fail('0.2-hi', "Invalid version modifier in '0.2-hi': 'hi'")
 
+       def testRestrictions(self):
+               v6 = DummyImpl("6", "RPM")
+               v7 = DummyImpl("7", "Gentoo")
+
+               r = model.VersionExpressionRestriction('!7')
+               self.assertEqual('<restriction: version !7>', repr(r))
+               assert r.meets_restriction(v6)
+               assert not r.meets_restriction(v7)
+
+               r = model.DistributionRestriction('RPM Debian')
+               self.assertEqual('<restriction: distro RPM|Debian>', repr(r))
+               assert r.meets_restriction(v6)
+               assert not r.meets_restriction(v7)
+
 if __name__ == '__main__':
        unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/testpackagekit.py 
new/0install-1.15/tests/testpackagekit.py
--- old/0install-1.14/tests/testpackagekit.py   2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/tests/testpackagekit.py   2013-02-12 16:35:03.000000000 
+0100
@@ -139,6 +139,8 @@
        return FakePackageKit()
 
 class TestPackageKit(BaseTest):
+       name = 'TestPackageKit'
+
        def setUp(self):
                BaseTest.setUp(self)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/testrun.py 
new/0install-1.15/tests/testrun.py
--- old/0install-1.14/tests/testrun.py  2013-01-14 12:33:10.000000000 +0100
+++ new/0install-1.15/tests/testrun.py  2013-02-12 16:35:03.000000000 +0100
@@ -17,6 +17,7 @@
 
 mydir = os.path.abspath(os.path.dirname(__file__))
 local_0launch = os.path.join(os.path.dirname(mydir), '0launch')
+arglist = os.path.join(mydir, 'runnable', 'ArgList.xml')
 runnable = os.path.join(mydir, 'runnable', 'Runnable.xml')
 runexec = os.path.join(mydir, 'runnable', 'RunExec.xml')
 recursive_runner = os.path.join(mydir, 'runnable', 'RecursiveRunner.xml')
@@ -95,6 +96,19 @@
                        sys.stdout = old_stdout
                assert 'runner-arg' in out, out
 
+       def testArgList(self):
+               d = Driver(requirements = Requirements(arglist), config = 
self.config)
+               self.config.handler.wait_for_blocker(d.solve_with_downloads())
+               old_stdout = sys.stdout
+               try:
+                       sys.stdout = StringIO()
+                       run.execute_selections(d.solver.selections, [], dry_run 
= True, stores = self.config.stores)
+                       out = sys.stdout.getvalue()
+               finally:
+                       sys.stdout = old_stdout
+               assert 'arg-for-runner -X ra1 -X ra2' in out, out
+               assert 'command-arg ca1 ca2' in out, out
+
        def testWrapper(self):
                p = Driver(requirements = Requirements(runnable), config = 
self.config)
                self.config.handler.wait_for_blocker(p.solve_with_downloads())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/tests/testsolver.py 
new/0install-1.15/tests/testsolver.py
--- old/0install-1.14/tests/testsolver.py       2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/tests/testsolver.py       2013-02-12 16:35:03.000000000 
+0100
@@ -1,4 +1,6 @@
 #!/usr/bin/env python
+from __future__ import print_function
+
 from basetest import BaseTest, BytesIO
 import sys, os, locale
 import unittest
@@ -285,15 +287,20 @@
                        s.solve_for(r)
                        assert not s.ready, s.selections.selections
 
+                       if expected_error != str(s.get_failure_reason()):
+                               print(s.get_failure_reason())
+
                        self.assertEqual(expected_error, 
str(s.get_failure_reason()))
 
                        return s
 
+               # No implementations
                s = test("", "",
                        "Can't find all required implementations:\n" +
                        "- http://localhost/top.xml -> (problem)\n" +
                        "    No known implementations at all")
 
+               # No retrieval method
                s = test("<implementation version='1' id='1'><requires 
interface='{diag}'/></implementation>".format(diag = diag_uri),
                         "",
                         "Can't find all required implementations:\n" +
@@ -301,6 +308,7 @@
                         "    No usable implementations:\n" +
                         "      1: No retrieval methods")
 
+               # No run command
                s = test("""<implementation version='1' id='1'>
                                <archive href='http://localhost:3000/foo.tgz' 
size='100'/>
                                <requires interface='{diag}'>
@@ -313,6 +321,22 @@
                         "    Rejected candidates:\n" +
                         "      1: No run command")
 
+               # Failing distribution requirement
+               s = test("""<implementation version='1' id='1' main='foo'>
+                               <archive href='http://localhost:3000/foo.tgz' 
size='100'/>
+                               <requires interface='{diag}' 
distribution='foo'/>
+                            </implementation>""".format(diag = diag_uri),
+                        """<implementation version='5' id='diag-5'>
+                               <archive href='http://localhost:3000/diag.tgz' 
size='100'/>
+                            </implementation>
+                        """,
+                        "Can't find all required implementations:\n"
+                        "- http://localhost/diagnostics.xml -> (problem)\n"
+                        "    http://localhost/top.xml 1 requires distro foo\n"
+                        "    No usable implementations satisfy the 
restrictions\n"
+                        "- http://localhost/top.xml -> 1 (1)")
+
+               # Failing version requirement on library
                s = test("""<implementation version='1' id='1' main='foo'>
                                <archive href='http://localhost:3000/foo.tgz' 
size='100'/>
                                <requires interface='{diag}' 
version='100..!200'/>
@@ -327,6 +351,23 @@
                         "    No usable implementations satisfy the 
restrictions\n"
                         "- http://localhost/top.xml -> 1 (1)")
 
+               # Failing version requires on root
+               s = test("""<implementation version='1' id='1' main='foo'>
+                               <archive href='http://localhost:3000/foo.tgz' 
size='100'/>
+                               <requires interface='{diag}'/>
+                            </implementation>""".format(diag = diag_uri),
+                        """<implementation version='5' id='diag-5'>
+                               <archive href='http://localhost:3000/diag.tgz' 
size='100'/>
+                               <restricts interface='{top_uri}' 
version='100..!200'/>
+                            </implementation>
+                        """.format(top_uri = top_uri),
+                        "Can't find all required implementations:\n"
+                        "- http://localhost/diagnostics.xml -> (problem)\n"
+                        "    Rejected candidates:\n"
+                        "      diag-5: requires http://localhost/top.xml 
version 100..!200\n"
+                        "- http://localhost/top.xml -> 1 (1)")
+
+               # Parse error in version restriction
                logger.setLevel(logging.ERROR)
                s = test("""<implementation version='1' id='1' main='foo'>
                                <archive href='http://localhost:3000/foo.tgz' 
size='100'/>
@@ -343,6 +384,7 @@
                         "- http://localhost/top.xml -> 1 (1)")
                logger.setLevel(logging.WARNING)
 
+               # Old-style version restriction
                s = test("""<implementation version='1' id='1' main='foo'>
                                <archive href='http://localhost:3000/foo.tgz' 
size='100'/>
                                <requires interface='{diag}'>
@@ -359,6 +401,7 @@
                         "    No usable implementations satisfy the 
restrictions\n"
                         "- http://localhost/top.xml -> 1 (1)")
 
+               # Mismatched machine types
                s = test("""<group>
                              <requires interface='{diag}'/>
                              <implementation version='1' id='1' main='foo' 
arch='Windows-i486'>
@@ -377,6 +420,7 @@
                         "      diag-5: Can't use x86_64 with selection of 
Top-level (i486)\n"
                         "- http://localhost/top.xml -> 1 (1)")
 
+               # Only show the first five unusable reasons
                s = test("""<group>
                              <requires interface='{diag}'/>
                              <implementation version='1' id='1' main='foo'>
@@ -403,6 +447,7 @@
                         "      ...\n"
                         "- http://localhost/top.xml -> 1 (1)")
 
+               # Only show the first five rejection reasons
                s = test("""<group>
                              <requires interface='{diag}'>
                                <version before='6'/>
@@ -436,6 +481,7 @@
                         "      ...\n"
                         "- http://localhost/top.xml -> 1 (1)")
 
+               # Justify why a particular version can't be used
                iface = self.config.iface_cache.get_interface(diag_uri)
                impl = 
self.config.iface_cache.get_feed(diag_uri).implementations['diag-5']
                r = Requirements(top_uri)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/0launch-gui/gui.py 
new/0install-1.15/zeroinstall/0launch-gui/gui.py
--- old/0install-1.14/zeroinstall/0launch-gui/gui.py    2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/0launch-gui/gui.py    2013-02-12 
16:35:03.000000000 +0100
@@ -5,7 +5,7 @@
 from zeroinstall.support import tasks
 from zeroinstall.injector import handler, download
 
-version = '1.14'
+version = '1.15'
 
 class GUIHandler(handler.Handler):
        dl_callbacks = None             # Download -> [ callback ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/__init__.py 
new/0install-1.15/zeroinstall/__init__.py
--- old/0install-1.14/zeroinstall/__init__.py   2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/zeroinstall/__init__.py   2013-02-12 16:35:03.000000000 
+0100
@@ -13,7 +13,7 @@
 @var _: a function for translating strings using the zero-install domain (for 
use internally by Zero Install)
 """
 
-version = '1.14'
+version = '1.15'
 
 import sys, logging
 if sys.version_info[0] > 2:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/apps.py 
new/0install-1.15/zeroinstall/apps.py
--- old/0install-1.14/zeroinstall/apps.py       2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/zeroinstall/apps.py       2013-02-12 16:35:03.000000000 
+0100
@@ -6,7 +6,7 @@
 # Copyright (C) 2012, Thomas Leonard
 # See the README file for details, or visit http://0install.net.
 
-from zeroinstall import _, SafeException, logger, DryRun
+from zeroinstall import _, SafeException, logger
 from zeroinstall.support import basedir, portable_rename
 from zeroinstall.injector import namespaces, selections, qdom, model
 import re, os, time, tempfile
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/cmd/select.py 
new/0install-1.15/zeroinstall/cmd/select.py
--- old/0install-1.14/zeroinstall/cmd/select.py 2013-01-14 12:33:10.000000000 
+0100
+++ new/0install-1.15/zeroinstall/cmd/select.py 2013-02-12 16:35:03.000000000 
+0100
@@ -11,7 +11,8 @@
 
 from zeroinstall import _, logger
 from zeroinstall.cmd import UsageError
-from zeroinstall.injector import model, selections, requirements
+from zeroinstall.injector import model, selections
+from zeroinstall.injector.requirements import Requirements
 from zeroinstall.injector.driver import Driver
 from zeroinstall.support import tasks
 
@@ -37,13 +38,15 @@
        add_generic_select_options(parser)
        parser.add_option("", "--xml", help=_("write selected versions as 
XML"), action='store_true')
 
-def get_selections(config, options, iface_uri, select_only, download_only, 
test_callback):
+def get_selections(config, options, iface_uri, select_only, download_only, 
test_callback, requirements = None):
        """Get selections for iface_uri, according to the options passed.
        Will switch to GUI mode if necessary.
        @param options: options from OptionParser
        @param iface_uri: canonical URI of the interface
        @param select_only: return immediately even if the selected versions 
aren't cached
        @param download_only: wait for stale feeds, and display GUI button as 
Download, not Run
+       @param requirements: requirements to use; if None, requirements come 
from options (since 1.15)
+       @type requirements: Requirements
        @return: the selected versions, or None if the user cancels
        @rtype: L{selections.Selections} | None
        """
@@ -63,10 +66,11 @@
                                tasks.wait_for_blocker(blocker)
                return maybe_selections
 
-       r = requirements.Requirements(iface_uri)
-       r.parse_options(options)
+       if requirements is None:
+               requirements = Requirements(iface_uri)
+               requirements.parse_options(options)
 
-       return get_selections_for(r, config, options, select_only, 
download_only, test_callback)
+       return get_selections_for(requirements, config, options, select_only, 
download_only, test_callback)
 
 def get_selections_for(requirements, config, options, select_only, 
download_only, test_callback):
        """Get selections for given requirements.
@@ -158,33 +162,36 @@
 
        app = config.app_mgr.lookup_app(args[0], missing_ok = True)
        if app is not None:
-               sels = app.get_selections()
+               old_sels = app.get_selections()
 
-               r = app.get_requirements()
-               do_select = r.parse_update_options(options)
-               iface_uri = sels.interface
+               requirements = app.get_requirements()
+               changes = requirements.parse_update_options(options)
+               iface_uri = old_sels.interface
 
-               if not do_select and r.extra_restrictions and not options.xml:
+               if requirements.extra_restrictions and not options.xml:
                        print("User-provided restrictions in force:")
-                       for uri, expr in r.extra_restrictions.items():
+                       for uri, expr in 
requirements.extra_restrictions.items():
                                print("  {uri}: {expr}".format(uri = uri, expr 
= expr))
                        print()
        else:
                iface_uri = model.canonical_iface_uri(args[0])
-               do_select = True
+               requirements = None
+               changes = False
 
-       if do_select or options.gui:
-               sels = get_selections(config, options, iface_uri,
-                                       select_only = True, download_only = 
False, test_callback = None)
-               if not sels:
-                       sys.exit(1)     # Aborted by user
+       sels = get_selections(config, options, iface_uri,
+                               select_only = True, download_only = False, 
test_callback = None, requirements = requirements)
+       if not sels:
+               sys.exit(1)     # Aborted by user
 
        if options.xml:
                show_xml(sels)
        else:
                show_human(sels, config.stores)
-               if app is not None and do_select:
-                       print(_("(use '0install update' to save the new 
parameters)"))
+               if app is not None:
+                       from zeroinstall.cmd import whatchanged
+                       changes = whatchanged.show_changes(old_sels.selections, 
sels.selections) or changes
+                       if changes:
+                               print(_("(note: use '0install update' instead 
to save the changes)"))
 
 def show_xml(sels):
        doc = sels.toDOM()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/gtkui/gtkutils.py 
new/0install-1.15/zeroinstall/gtkui/gtkutils.py
--- old/0install-1.14/zeroinstall/gtkui/gtkutils.py     2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/gtkui/gtkutils.py     2013-02-12 
16:35:03.000000000 +0100
@@ -85,7 +85,7 @@
        def __init__(self, dialog):
                tasks.Blocker.__init__(self, dialog.get_title())
                a = None
-               def response(d, resp):
+               def response(d, resp, self = self):     # (PyGTK GC bug)
                        self.response = resp
                        d.disconnect(a)
                        self.trigger()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/injector/distro.py 
new/0install-1.15/zeroinstall/injector/distro.py
--- old/0install-1.14/zeroinstall/injector/distro.py    2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/injector/distro.py    2013-02-12 
16:35:03.000000000 +0100
@@ -17,8 +17,10 @@
 _zeroinstall_regexp = '(?:%s)(?:-(?:pre|rc|post|)(?:%s))*' % (_dotted_ints, 
_dotted_ints)
 
 # This matches the interesting bits of distribution version numbers
-# (first matching group is for Java-style 6b17 syntax, or "major")
-_version_regexp = '(?:[a-z])?({ints}b)?({zero})(-r{ints})?'.format(zero = 
_zeroinstall_regexp, ints = _dotted_ints)
+# (first matching group is for Java-style 6b17 or 7u9 syntax, or "major")
+_version_regexp = '(?:[a-z])?({ints}[bu])?({zero})(-r{ints})?'.format(zero = 
_zeroinstall_regexp, ints = _dotted_ints)
+
+_PYTHON_URI = 'http://repo.roscidus.com/python/python'
 
 # We try to do updates atomically without locking, but we don't worry too much 
about
 # duplicate entries or being a little out of sync with the on-disk copy.
@@ -135,7 +137,10 @@
        Sub-classes should specialise this to integrate with the package 
managers of
        particular distributions. This base class ignores the native package 
manager.
        @since: 0.28
+       @ivar name: the default value for Implementation.distro_name for our 
implementations
+       @type name: str
        """
+
        _packagekit = None
 
        def get_package_info(self, package, factory):
@@ -214,19 +219,35 @@
                                if impl.installed:
                                        self.installed_fixup(impl)
 
-               if master_feed.url == 'http://repo.roscidus.com/python/python' 
and os.name != "nt" and all(not impl.installed for impl in 
feed.implementations.values()):
+               if master_feed.url == _PYTHON_URI and os.name != "nt":
                        # Hack: we can support Python on platforms with 
unsupported package managers
                        # by adding the implementation of Python running us now 
to the list.
                        python_version = '.'.join([str(v) for v in 
sys.version_info if isinstance(v, int)])
                        impl_id = 'package:host:python:' + python_version
                        assert impl_id not in feed.implementations
-                       impl = model.DistributionImplementation(feed, impl_id, 
self)
+                       impl = model.DistributionImplementation(feed, impl_id, 
self, distro_name = 'host')
                        impl.installed = True
                        impl.version = model.parse_version(python_version)
                        impl.main = sys.executable
                        impl.upstream_stability = model.packaged
                        impl.machine = host_machine     # (hopefully)
                        feed.implementations[impl_id] = impl
+               elif master_feed.url == 
'http://repo.roscidus.com/python/python-gobject' and os.name != "nt":
+                       # Likewise, we know that there is a native 
python-gobject available for our Python
+                       from zeroinstall import gobject
+                       impl_id = 'package:host:python-gobject:' + 
'.'.join(str(x) for x in gobject.pygobject_version)
+                       assert impl_id not in feed.implementations
+                       impl = model.DistributionImplementation(feed, impl_id, 
self, distro_name = 'host')
+                       impl.installed = True
+                       impl.version = [list(gobject.pygobject_version)]
+                       impl.upstream_stability = model.packaged
+                       impl.machine = host_machine     # (hopefully)
+
+                       # Requires our version of Python too
+                       restriction_element = 
qdom.Element(namespaces.XMLNS_IFACE, 'restricts', {'interface': _PYTHON_URI, 
'distribution': 'host'})
+                       
impl.requires.append(model.process_depends(restriction_element, None))
+
+                       feed.implementations[impl_id] = impl
 
                return feed
 
@@ -263,7 +284,12 @@
                @since: 1.11"""
                pass
 
+       def get_score(self, distro_name):
+               return int(distro_name == self.name)
+
 class WindowsDistribution(Distribution):
+       name = 'Windows'
+
        def get_package_info(self, package, factory):
                def _is_64bit_windows():
                        p = sys.platform
@@ -304,8 +330,7 @@
                        reg_path = 
r"SOFTWARE\JavaSoft\{part}\{win_version}".format(part = part, win_version = 
win_version)
                        (java32_home, java64_home) = _read_hklm_reg(reg_path, 
"JavaHome")
 
-                       for (home, arch) in [(java32_home, 'i486'),
-                                            (java64_home, 'x86_64')]:
+                       for (home, arch) in [(java32_home, 'i486'), 
(java64_home, 'x86_64')]:
                                if os.path.isfile(home + r"\bin\java.exe"):
                                        impl = 
factory('package:windows:%s:%s:%s' % (package, zero_version, arch))
                                        impl.machine = arch
@@ -313,6 +338,31 @@
                                        impl.upstream_stability = model.packaged
                                        impl.main = home + r"\bin\java.exe"
 
+               def find_netfx(win_version, zero_version):
+                       reg_path = r"SOFTWARE\Microsoft\NET Framework 
Setup\NDP\{win_version}".format(win_version = win_version)
+                       (netfx32_install, netfx64_install) = 
_read_hklm_reg(reg_path, "Install")
+
+                       for (install, arch) in [(netfx32_install, 'i486'), 
(netfx64_install, 'x86_64')]:
+                               impl = factory('package:windows:%s:%s:%s' % 
(package, zero_version, arch))
+                               impl.installed = (install == 1)
+                               impl.machine = arch
+                               impl.version = model.parse_version(zero_version)
+                               impl.upstream_stability = model.packaged
+                               impl.main = "" # .NET executables do not need a 
runner on Windows but they need one elsewhere
+
+               def find_netfx_release(win_version, release_version, 
zero_version):
+                       reg_path = r"SOFTWARE\Microsoft\NET Framework 
Setup\NDP\{win_version}".format(win_version = win_version)
+                       (netfx32_install, netfx64_install) = 
_read_hklm_reg(reg_path, "Install")
+                       (netfx32_release, netfx64_release) = 
_read_hklm_reg(reg_path, "Release")
+
+                       for (install, release, arch) in [(netfx32_install, 
netfx32_release, 'i486'), (netfx64_install, netfx64_release, 'x86_64')]:
+                               impl = factory('package:windows:%s:%s:%s' % 
(package, zero_version, arch))
+                               impl.installed = (install == 1 and release != 
'' and release >= release_version)
+                               impl.machine = arch
+                               impl.version = model.parse_version(zero_version)
+                               impl.upstream_stability = model.packaged
+                               impl.main = "" # .NET executables do not need a 
runner on Windows but they need one elsewhere
+
                if package == 'openjdk-6-jre':
                        find_java("Java Runtime Environment", "1.6", '6')
                elif package == 'openjdk-6-jdk':
@@ -321,12 +371,22 @@
                        find_java("Java Runtime Environment", "1.7", '7')
                elif package == 'openjdk-7-jdk':
                        find_java("Java Development Kit", "1.7", '7')
-
-       def get_score(self, disto_name):
-               return int(disto_name == 'Windows')
+               elif package == 'netfx':
+                       find_netfx("v2.0.50727", '2.0')
+                       find_netfx("v3.0", '3.0')
+                       find_netfx("v3.5", '3.5')
+                       find_netfx("v4\\Full", '4.0')
+                       find_netfx_release("v4\\Full", 378389, '4.5')
+                       find_netfx("v5", '5.0')
+               elif package == 'netfx-client':
+                       find_netfx("v4\\Client", '4.0')
+                       find_netfx_release("v4\\Client", 378389, '4.5')
 
 class DarwinDistribution(Distribution):
        """@since: 1.11"""
+
+       name = 'Darwin'
+
        def get_package_info(self, package, factory):
                def java_home(version, arch):
                        null = os.open(os.devnull, os.O_WRONLY)
@@ -366,7 +426,7 @@
 
                def find_program(file):
                        if os.path.isfile(file) and os.access(file, os.X_OK):
-                               program_version = get_version(file)
+                               program_version = 
try_cleanup_distro_version(get_version(file))
                                impl = factory('package:darwin:%s:%s' % 
(package, program_version), True)
                                if impl:
                                        impl.installed = True
@@ -380,9 +440,6 @@
                elif package == 'gnupg2':
                        find_program("/usr/local/bin/gpg2")
 
-       def get_score(self, disto_name):
-               return int(disto_name == 'Darwin')
-
 class CachedDistribution(Distribution):
        """For distributions where querying the package database is slow (e.g. 
requires running
        an external command), we cache the results.
@@ -488,6 +545,8 @@
 class DebianDistribution(Distribution):
        """A dpkg-based distribution."""
 
+       name = 'Debian'
+
        cache_leaf = 'dpkg-status.cache'
 
        def __init__(self, dpkg_status):
@@ -585,9 +644,6 @@
                impl.commands["run"] = 
model.Command(qdom.Element(namespaces.XMLNS_IFACE, 'command',
                        {'path': java_bin, 'name': 'run'}), None)
 
-       def get_score(self, disto_name):
-               return int(disto_name == 'Debian')
-
        def _get_dpkg_info(self, package):
                installed_cached_info = self.dpkg_cache.get(package)
                if installed_cached_info == None:
@@ -635,6 +691,8 @@
 class RPMDistribution(CachedDistribution):
        """An RPM-based distribution."""
 
+       name = 'RPM'
+
        cache_leaf = 'rpm-status.cache'
 
        def generate_cache(self):
@@ -709,12 +767,11 @@
                                # OpenSUSE uses 1.6 to mean 6
                                del impl.version[0][0]
 
-       def get_score(self, disto_name):
-               return int(disto_name == 'RPM')
-
 class SlackDistribution(Distribution):
        """A Slack-based distribution."""
 
+       name = 'Slack'
+
        def __init__(self, packages_dir):
                self._packages_dir = packages_dir
 
@@ -738,12 +795,11 @@
                # Add any uninstalled candidates found by PackageKit
                self.packagekit.get_candidates(package, factory, 
'package:slack')
 
-       def get_score(self, disto_name):
-               return int(disto_name == 'Slack')
-
 class ArchDistribution(Distribution):
        """An Arch Linux distribution."""
 
+       name = 'Arch'
+
        def __init__(self, packages_dir):
                self._packages_dir = os.path.join(packages_dir, "local")
 
@@ -776,10 +832,8 @@
                # Add any uninstalled candidates found by PackageKit
                self.packagekit.get_candidates(package, factory, 'package:arch')
 
-       def get_score(self, disto_name):
-               return int(disto_name == 'Arch')
-
 class GentooDistribution(Distribution):
+       name = 'Gentoo'
 
        def __init__(self, pkgdir):
                self._pkgdir = pkgdir
@@ -823,10 +877,8 @@
                # Add any uninstalled candidates found by PackageKit
                self.packagekit.get_candidates(package, factory, 
'package:gentoo')
 
-       def get_score(self, disto_name):
-               return int(disto_name == 'Gentoo')
-
 class PortsDistribution(Distribution):
+       name = 'Ports'
 
        def __init__(self, pkgdir):
                self._pkgdir = pkgdir
@@ -858,10 +910,9 @@
                        impl.version = model.parse_version(version)
                        impl.machine = machine
 
-       def get_score(self, disto_name):
-               return int(disto_name == 'Ports')
-
 class MacPortsDistribution(CachedDistribution):
+       name = 'MacPorts'
+
        def __init__(self, db_status_file):
                super(MacPortsDistribution, self).__init__(db_status_file)
                self.darwin = DarwinDistribution()
@@ -914,16 +965,18 @@
                        if machine != '*':
                                impl.machine = machine
 
-       def get_score(self, disto_name):
+       def get_score(self, distro_name):
                # We support both sources of packages.
                # In theory, we should route 'Darwin' package names to 
DarwinDistribution, and
                # Mac Ports names to MacPortsDistribution. But since we only 
use Darwin for Java,
                # having one object handle both is OK.
-               return int(disto_name in ('Darwin', 'MacPorts'))
+               return int(distro_name in ('Darwin', 'MacPorts'))
 
 class CygwinDistribution(CachedDistribution):
        """A Cygwin-based distribution."""
 
+       name = 'Cygwin'
+
        cache_leaf = 'cygcheck-status.cache'
 
        def generate_cache(self):
@@ -956,9 +1009,6 @@
                        if machine != '*':
                                impl.machine = machine
 
-       def get_score(self, disto_name):
-               return int(disto_name == 'Cygwin')
-
 
 _host_distribution = None
 def get_host_distribution():
@@ -986,9 +1036,8 @@
                                _host_distribution = GentooDistribution(_pkg_db)
                        elif sys.platform.startswith("freebsd"):
                                _host_distribution = PortsDistribution(_pkg_db)
-               elif os.path.isfile(_macports_db) \
-                       and sys.prefix.startswith("/opt/local"):
-                               _host_distribution = 
MacPortsDistribution(_macports_db)
+               elif os.path.isfile(_macports_db):
+                       _host_distribution = MacPortsDistribution(_macports_db)
                elif os.path.isfile(_cygwin_log) and sys.platform == "cygwin":
                        _host_distribution = CygwinDistribution(_cygwin_log)
                elif os.access(dpkg_db_status, os.R_OK) \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/injector/driver.py 
new/0install-1.15/zeroinstall/injector/driver.py
--- old/0install-1.14/zeroinstall/injector/driver.py    2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/injector/driver.py    2013-02-12 
16:35:03.000000000 +0100
@@ -11,7 +11,7 @@
 from zeroinstall import _, logger
 import os
 
-from zeroinstall.injector import arch, model
+from zeroinstall.injector import arch
 from zeroinstall.injector.model import network_offline
 from zeroinstall.support import tasks
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/injector/fetch.py 
new/0install-1.15/zeroinstall/injector/fetch.py
--- old/0install-1.14/zeroinstall/injector/fetch.py     2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/injector/fetch.py     2013-02-12 
16:35:03.000000000 +0100
@@ -5,7 +5,7 @@
 # Copyright (C) 2009, Thomas Leonard
 # See the README file for details, or visit http://0install.net.
 
-from zeroinstall import _, NeedDownload, logger
+from zeroinstall import _, logger
 import os, sys
 
 from zeroinstall import support
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/injector/handler.py 
new/0install-1.15/zeroinstall/injector/handler.py
--- old/0install-1.14/zeroinstall/injector/handler.py   2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/injector/handler.py   2013-02-12 
16:35:03.000000000 +0100
@@ -184,9 +184,9 @@
                @type exception: L{SafeException}
                @param tb: optional traceback
                @since: 0.25"""
-               logger.warn("%s", str(exception) or type(exception))
-               #import traceback
-               #traceback.print_exception(exception, None, tb)
+               import logging
+               logger.warn("%s", str(exception) or type(exception),
+                               exc_info = (exception, None, tb) if 
logger.isEnabledFor(logging.INFO) else None)
        
 class ConsoleHandler(Handler):
        """A Handler that displays progress on stdout (a tty).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/injector/model.py 
new/0install-1.15/zeroinstall/injector/model.py
--- old/0install-1.14/zeroinstall/injector/model.py     2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/injector/model.py     2013-02-12 
16:35:03.000000000 +0100
@@ -191,6 +191,10 @@
                        r = ImpossibleRestriction(msg)
                dependency.restrictions.append(r)
 
+       distro = item.getAttribute('distribution')
+       if distro:
+               dependency.restrictions.append(DistributionRestriction(distro))
+
        for e in item.childNodes:
                if e.uri != XMLNS_IFACE: continue
                if e.name in binding_names:
@@ -226,6 +230,9 @@
                """
                raise NotImplementedError(_("Abstract"))
 
+       def __str__(self):
+               return "missing __str__ on %s" % type(self)
+
        def __repr__(self):
                return "<restriction: %s>" % self
 
@@ -237,6 +244,7 @@
                """@param version: the required version number
                @see: L{parse_version}; use this to pre-process the version 
number
                """
+               assert not isinstance(version, str), "Not parsed: " + version
                self.version = version
 
        def meets_restriction(self, impl):
@@ -308,6 +316,23 @@
        def __str__(self):
                return "<impossible: %s>" % self.reason
 
+class DistributionRestriction(Restriction):
+       """A restriction that can only be satisfied by an implementation
+       from the given distribution.
+       For example, a MacPorts Python library requires us to select the 
MacPorts
+       version of Python too.
+       @since: 1.15"""
+       distros = None
+
+       def __init__(self, distros):
+               self.distros = frozenset(distros.split(' '))
+
+       def meets_restriction(self, impl):
+               return impl.distro_name in self.distros
+
+       def __str__(self):
+               return "distro " + '|'.join(self.distros)
+
 class Binding(object):
        """Information about how the choice of a Dependency is made known
        to the application being run."""
@@ -801,14 +826,15 @@
        @ivar package_implementation: the <package-implementation> element that 
generated this impl (since 1.7)
        @type package_implementation: L{qdom.Element}
        @since: 0.28"""
-       __slots__ = ['distro', 'installed', 'package_implementation']
+       __slots__ = ['distro', 'installed', 'package_implementation', 
'distro_name']
 
-       def __init__(self, feed, id, distro, package_implementation = None):
+       def __init__(self, feed, id, distro, package_implementation = None, 
distro_name = None):
                assert id.startswith('package:')
                Implementation.__init__(self, feed, id)
                self.distro = distro
                self.installed = False
                self.package_implementation = package_implementation
+               self.distro_name = distro_name or distro.name
 
                if package_implementation:
                        for child in package_implementation.childNodes:
@@ -833,6 +859,8 @@
        @since: 0.28"""
        __slots__ = ['os', 'size', 'digests', 'local_path']
 
+       distro_name = '0install'
+
        def __init__(self, feed, id, local_path):
                """id can be a local path (string starting with /) or a 
manifest hash (eg "sha1=XXX")"""
                assert not id.startswith('package:'), id
@@ -1449,11 +1477,11 @@
                return path
 
        if '/' not in uri:
-               path = support.find_in_path(uri)
-               if path is not None:
+               alias_path = support.find_in_path(uri)
+               if alias_path is not None:
                        from zeroinstall import alias
                        try:
-                               alias.parse_script(path)
+                               alias.parse_script(alias_path)
                        except alias.NotAnAliasScript:
                                pass
                        else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/injector/run.py 
new/0install-1.15/zeroinstall/injector/run.py
--- old/0install-1.14/zeroinstall/injector/run.py       2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/injector/run.py       2013-02-12 
16:35:03.000000000 +0100
@@ -68,11 +68,22 @@
 
        return results
 
-def _process_args(args, element):
-       """Append each <arg> under <element> to args, performing $-expansion."""
+def _process_args(args, element, env = os.environ):
+       """Append each <arg> under <element> to args, performing $-expansion. 
Also, process <for-each> loops."""
        for child in element.childNodes:
-               if child.uri == namespaces.XMLNS_IFACE and child.name == 'arg':
-                       
args.append(Template(child.content).substitute(os.environ))
+               if child.uri != namespaces.XMLNS_IFACE: continue
+
+               if child.name == 'arg':
+                       args.append(Template(child.content).substitute(env))
+               elif child.name == 'for-each':
+                       array_var = child.attrs['item-from']
+                       separator = child.attrs.get('separator', os.pathsep)
+                       env_copy = env.copy()
+                       seq = env.get(array_var, None)
+                       if seq:
+                               for item in seq.split(separator):
+                                       env_copy['item'] = item
+                                       _process_args(args, child, env_copy)
 
 class Setup(object):
        """@since: 1.2"""
@@ -307,7 +318,7 @@
                user_command_element = qdom.Element(namespaces.XMLNS_IFACE, 
'command', {'path': main})
                if commands:
                        for child in commands[0].qdom.childNodes:
-                               if child.uri == namespaces.XMLNS_IFACE and 
child.name == 'arg':
+                               if child.uri == namespaces.XMLNS_IFACE and 
child.name in ('arg', 'for-each'):
                                        continue
                                user_command_element.childNodes.append(child)
                user_command = Command(user_command_element, None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/injector/solver.py 
new/0install-1.15/zeroinstall/injector/solver.py
--- old/0install-1.14/zeroinstall/injector/solver.py    2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/injector/solver.py    2013-02-12 
16:35:03.000000000 +0100
@@ -491,9 +491,13 @@
                                        logger.warn(_("Missing local feed; if 
it's no longer required, remove it with:") +
                                                        '\n0install remove-feed 
' + iface.uri + ' ' + f,
                                                {'feed': f, 'interface': iface, 
'exception': ex})
-                               except Exception as ex:
+                               except model.SafeException as ex:
                                        logger.warn(_("Failed to load feed 
%(feed)s for %(interface)s: %(exception)s"), {'feed': f, 'interface': iface, 
'exception': ex})
                                        #raise
+                               except Exception as ex:
+                                       import logging
+                                       logger.warn(_("Failed to load feed 
%(feed)s for %(interface)s: %(exception)s"), {'feed': f, 'interface': iface, 
'exception': ex},
+                                                       exc_info = True if 
logger.isEnabledFor(logging.INFO) else None)
 
                        impls.sort(key = lambda impl: self.get_rating(iface, 
impl, arch), reverse = True)
 
@@ -933,6 +937,20 @@
                                                                                
this_arch = i.machine,
                                                                                
other_name = example_machine_impl.feed.get_name(),
                                                                                
other_arch = example_machine_impl.machine)
+
+                                               if reason is None:
+                                                       # Check if our 
requirements conflict with an existing selection
+                                                       for dep in i.requires:
+                                                               if not 
isinstance(dep, model.InterfaceRestriction): continue
+                                                               dep_selection = 
sels.get(dep.interface)
+                                                               if 
dep_selection is not None:
+                                                                       for r 
in dep.restrictions:
+                                                                               
if not r.meets_restriction(dep_selection.impl):
+                                                                               
        reason = _("requires {iface} {reqs}").format(
+                                                                               
                        iface = dep.interface,
+                                                                               
                        reqs = ', '.join(str(r) for r in dep.restrictions))
+                                                                               
        break
+
                                                if reason is None:
                                                        var = 
self._iface_to_vars[iface].get(i, None)
                                                        if var is None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/support/escaping.py 
new/0install-1.15/zeroinstall/support/escaping.py
--- old/0install-1.14/zeroinstall/support/escaping.py   2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/support/escaping.py   2013-02-12 
16:35:03.000000000 +0100
@@ -11,7 +11,7 @@
 @since: 1.13
 """
 
-import os, sys, re
+import re
 
 # Copyright (C) 2012, Thomas Leonard
 # See the README file for details, or visit http://0install.net.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/0install-1.14/zeroinstall/zerostore/optimise.py 
new/0install-1.15/zeroinstall/zerostore/optimise.py
--- old/0install-1.14/zeroinstall/zerostore/optimise.py 2013-01-14 
12:33:10.000000000 +0100
+++ new/0install-1.15/zeroinstall/zerostore/optimise.py 2013-02-12 
16:35:03.000000000 +0100
@@ -86,7 +86,9 @@
                        logger.warn(_("Failed to read manifest file 
'%(manifest_path)s': %(exception)s"), {'manifest': manifest_path, 'exception': 
str(ex)})
                        continue
 
-               if alg == 'sha1': continue
+               if alg == 'sha1':
+                       ms.close()
+                       continue
 
                man_size += os.path.getsize(manifest_path)
 

-- 
To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org
For additional commands, e-mail: opensuse-commit+h...@opensuse.org

Reply via email to