def testNoNeedDl(self): policy = Policy(foo_iface_uri, config=self.config) policy.freshness = 0 assert policy.need_download() policy = Policy(os.path.abspath('Foo.xml'), config=self.config) assert not policy.need_download() assert policy.ready
def testFeeds(self): self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="0" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <feed src='http://bar'/> </interface>""" % foo_iface_uri) self.cache_iface( 'http://bar', """<?xml version="1.0" ?> <interface last-modified="0" uri="http://bar" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <feed-for interface='%s'/> <name>Bar</name> <summary>Bar</summary> <description>Bar</description> <implementation version='1.0' id='sha1=123' main='dummy'> <archive href='foo' size='10'/> </implementation> </interface>""" % foo_iface_uri) policy = Policy(foo_iface_uri, config=self.config) policy.freshness = 0 policy.network_use = model.network_full recalculate(policy) assert policy.ready foo_iface = self.config.iface_cache.get_interface(foo_iface_uri) self.assertEquals('sha1=123', policy.implementation[foo_iface].id)
def build(): # Get the chosen versions src_policy = Policy(interface.uri, src=True) src_policy.freshness = 0 src_policy.recalculate() if not src_policy.ready: raise Exception( _('Internal error: required source components not found!')) root_iface = iface_cache.iface_cache.get_interface(src_policy.root) impl = src_policy.implementation[root_iface] min_version = impl.metadata.get(XMLNS_0COMPILE + ' min-version', our_min_version) # Check the syntax is valid and the version is high enough if model.parse_version(min_version) < model.parse_version( our_min_version): min_version = our_min_version # Do the whole build-and-register-feed c = Command() c.run(("0launch", '--message', _('Download the 0compile tool, to compile the source code'), '--not-before=' + min_version, "http://0install.net/2006/interfaces/0compile.xml", 'gui', '--no-prompt', interface.uri), add_feed)
def testDistro(self): with output_suppressed(): native_url = 'http://example.com:8000/Native.xml' # Initially, we don't have the feed at all... master_feed = self.config.iface_cache.get_feed(native_url) assert master_feed is None, master_feed trust.trust_db.trust_key( 'DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000') self.child = server.handle_requests( 'Native.xml', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B') policy = Policy(native_url, config=self.config) assert policy.need_download() solve = policy.solve_with_downloads() self.config.handler.wait_for_blocker(solve) tasks.check(solve) master_feed = self.config.iface_cache.get_feed(native_url) assert master_feed is not None assert master_feed.implementations == {} distro_feed_url = master_feed.get_distro_feed() assert distro_feed_url is not None distro_feed = self.config.iface_cache.get_feed(distro_feed_url) assert distro_feed is not None assert len( distro_feed.implementations) == 2, distro_feed.implementations
def setUp(self): www.HTTP_ROOT = '<HTTP_ROOT>' www.REPO_ROOT = '<REPO_ROOT>' for name, path in _ENVS.items(): os.environ[name] = os.path.join(_ROOT, path) if os.path.exists(os.environ['XDG_CACHE_HOME']): support.ro_rmtree(os.environ['XDG_CACHE_HOME']) shutil.rmtree(_ROOT, ignore_errors=True) for name, path in _ENVS.items(): os.makedirs(os.environ[name], mode=0700) reload(basedir) assert basedir.xdg_config_home == os.environ['XDG_CONFIG_HOME'] iface_cache.iface_cache.__init__() download._downloads = {} logger = logging.getLogger() for i in logger.handlers: logger.removeHandler(i) logging.basicConfig(filename=os.path.join(_ROOT, 'debug.log'), level=logging.DEBUG) self.logger = logging.getLogger('test') policy = Policy('') policy.network_use = model.network_full policy.freshness = 60 policy.save_config() self._child = None
def testBackground(self, verbose=False): p = Policy('http://example.com:8000/Hello.xml', config=self.config) self.import_feed(p.root, 'Hello.xml') p.freshness = 0 p.network_use = model.network_minimal p.solver.solve(p.root, arch.get_host_architecture()) assert p.ready, p.solver.get_failure_reason() @tasks. async def choose_download(registed_cb, nid, actions): try: assert actions == ['download', 'Download'], actions registed_cb(nid, 'download') except: import traceback traceback.print_exc() yield None global ran_gui ran_gui = False old_out = sys.stdout try: sys.stdout = StringIO() self.child = server.handle_requests('Hello.xml', '6FCF121BE2390E0B.gpg') my_dbus.system_services = { "org.freedesktop.NetworkManager": { "/org/freedesktop/NetworkManager": NetworkManager() } } my_dbus.user_callback = choose_download pid = os.getpid() old_exit = os._exit def my_exit(code): # The background handler runs in the same process # as the tests, so don't let it abort. if os.getpid() == pid: raise SystemExit(code) # But, child download processes are OK old_exit(code) from zeroinstall.injector import config key_info = config.DEFAULT_KEY_LOOKUP_SERVER config.DEFAULT_KEY_LOOKUP_SERVER = None try: try: os._exit = my_exit background.spawn_background_update(p, verbose) assert False except SystemExit as ex: self.assertEquals(1, ex.code) finally: os._exit = old_exit config.DEFAULT_KEY_LOOKUP_SERVER = key_info finally: sys.stdout = old_out assert ran_gui
def _manage_feeds(options, args): from zeroinstall.injector import writer from zeroinstall.injector.handler import Handler from zeroinstall.injector.policy import Policy handler = Handler(dry_run = options.dry_run) if not args: raise UsageError() for x in args: print _("Feed '%s':") % x + '\n' x = model.canonical_iface_uri(x) policy = Policy(x, handler) if options.offline: policy.network_use = model.network_offline feed = iface_cache.get_feed(x) if policy.network_use != model.network_offline and policy.is_stale(feed): blocker = policy.fetcher.download_and_import_feed(x, iface_cache.iface_cache) print _("Downloading feed; please wait...") handler.wait_for_blocker(blocker) print _("Done") interfaces = policy.get_feed_targets(x) for i in range(len(interfaces)): feed = interfaces[i].get_feed(x) if feed: print _("%(index)d) Remove as feed for '%(uri)s'") % {'index': i + 1, 'uri': interfaces[i].uri} else: print _("%(index)d) Add as feed for '%(uri)s'") % {'index': i + 1, 'uri': interfaces[i].uri} print while True: try: i = raw_input(_('Enter a number, or CTRL-C to cancel [1]: ')).strip() except KeyboardInterrupt: print raise SafeException(_("Aborted at user request.")) if i == '': i = 1 else: try: i = int(i) except ValueError: i = 0 if i > 0 and i <= len(interfaces): break print _("Invalid number. Try again. (1 to %d)") % len(interfaces) iface = interfaces[i - 1] feed = iface.get_feed(x) if feed: iface.extra_feeds.remove(feed) else: iface.extra_feeds.append(model.Feed(x, arch = None, user_override = True)) writer.save_interface(iface) print '\n' + _("Feed list for interface '%s' is now:") % iface.get_name() if iface.feeds: for f in iface.feeds: print "- " + f.uri else: print _("(no feeds)")
def testConstraints(self): self.cache_iface( 'http://bar', """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="http://bar" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Bar</name> <summary>Bar</summary> <description>Bar</description> <implementation id='sha1=100' version='1.0'> <archive href='foo' size='10'/> </implementation> <implementation id='sha1=150' stability='developer' version='1.5'> <archive href='foo' size='10'/> </implementation> <implementation id='sha1=200' version='2.0'> <archive href='foo' size='10'/> </implementation> </interface>""") self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <group main='dummy'> <requires interface='http://bar'> <version/> </requires> <implementation id='sha1=123' version='1.0'> <archive href='foo' size='10'/> </implementation> </group> </interface>""" % foo_iface_uri) policy = Policy(foo_iface_uri, config=self.config) policy.network_use = model.network_full policy.freshness = 0 #logger.setLevel(logging.DEBUG) recalculate(policy) #logger.setLevel(logging.WARN) foo_iface = self.config.iface_cache.get_interface(foo_iface_uri) bar_iface = self.config.iface_cache.get_interface('http://bar') assert policy.implementation[bar_iface].id == 'sha1=200' dep = policy.implementation[foo_iface].dependencies['http://bar'] assert len(dep.restrictions) == 1 restriction = dep.restrictions[0] restriction.before = model.parse_version('2.0') recalculate(policy) assert policy.implementation[bar_iface].id == 'sha1=100' restriction.not_before = model.parse_version('1.5') recalculate(policy) assert policy.implementation[bar_iface].id == 'sha1=150'
def _check_for_updates(old_policy, verbose): from zeroinstall.injector.policy import Policy from zeroinstall.injector.config import load_config iface_cache = old_policy.config.iface_cache root_iface = iface_cache.get_interface(old_policy.root).get_name() background_config = load_config( BackgroundHandler(root_iface, old_policy.root)) policy = Policy(config=background_config, requirements=old_policy.requirements) info(_("Checking for updates to '%s' in a background process"), root_iface) if verbose: policy.handler.notify("Zero Install", _("Checking for updates to '%s'...") % root_iface, timeout=1) network_state = policy.handler.get_network_state() if network_state != _NetworkState.NM_STATE_CONNECTED: info( _("Not yet connected to network (status = %d). Sleeping for a bit..." ), network_state) import time time.sleep(120) if network_state in (_NetworkState.NM_STATE_DISCONNECTED, _NetworkState.NM_STATE_ASLEEP): info(_("Still not connected to network. Giving up.")) sys.exit(1) else: info(_("NetworkManager says we're on-line. Good!")) policy.freshness = 0 # Don't bother trying to refresh when getting the interface refresh = policy.refresh_all() # (causes confusing log messages) tasks.wait_for_blocker(refresh) # We could even download the archives here, but for now just # update the interfaces. if not policy.need_download(): if verbose: policy.handler.notify("Zero Install", _("No updates to download."), timeout=1) sys.exit(0) policy.handler.notify("Zero Install", _("Updates ready to download for '%s'.") % root_iface, timeout=1) _exec_gui(policy.root, '--refresh', '--systray') sys.exit(1)
def testBadConfig(self): path = basedir.save_config_path(namespaces.config_site, namespaces.config_prog) glob = os.path.join(path, 'global') assert not os.path.exists(glob) stream = file(glob, 'w') stream.write('hello!') stream.close() logger.setLevel(logging.ERROR) Policy(foo_iface_uri, config=self.config) logger.setLevel(logging.WARN)
def __init__(self, feed, seed=None): _Link.__init__(self) self.force = False iface_uri = model.canonical_iface_uri(feed) self.policy = Policy(iface_uri) self.policy.solver.record_details = True if seed is not None: self.policy.network_use = seed.policy.network_use self.policy.handler = seed.policy.handler self.force = seed.force
def testRecipeUnpack(self): old_out = sys.stdout try: sys.stdout = StringIO() self.child = server.handle_requests(('doubly_packed.tar')) policy = Policy(os.path.abspath('Unpack.xml'), config=self.config) try: download_and_execute(policy, []) assert False except model.SafeException, ex: if "HelloWorld/Missing" not in str(ex): raise ex finally: sys.stdout = old_out
def testRecipeFailure(self): old_out = sys.stdout try: sys.stdout = StringIO() self.child = server.handle_requests('*') policy = Policy(os.path.abspath('Recipe.xml'), config=self.config) try: download_and_execute(policy, []) assert False except download.DownloadError as ex: if "Connection" not in str(ex): raise finally: sys.stdout = old_out
def testAutopackage(self): old_out = sys.stdout try: sys.stdout = StringIO() self.child = server.handle_requests('HelloWorld.autopackage') policy = Policy(os.path.abspath('Autopackage.xml'), config=self.config) try: download_and_execute(policy, []) assert False except model.SafeException as ex: if "HelloWorld/Missing" not in str(ex): raise finally: sys.stdout = old_out
def testNoArchives(self): self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <implementation id='sha1=123' version='1.0' main='dummy'/> </interface>""" % foo_iface_uri) policy = Policy(foo_iface_uri, config=self.config) policy.freshness = 0 recalculate(policy) assert not policy.ready
def testRecipe(self): old_out = sys.stdout try: sys.stdout = StringIO() self.child = server.handle_requests( ('HelloWorld.tar.bz2', 'dummy_1-1_all.deb')) policy = Policy(os.path.abspath('Recipe.xml'), config=self.config) try: download_and_execute(policy, []) assert False except model.SafeException as ex: if "HelloWorld/Missing" not in str(ex): raise ex finally: sys.stdout = old_out
def testAcceptKey(self): with output_suppressed(): self.child = server.handle_requests( 'Hello', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B', 'HelloWorld.tgz') policy = Policy('http://localhost:8000/Hello', config=self.config) assert policy.need_download() sys.stdin = Reply("Y\n") try: download_and_execute(policy, ['Hello'], main='Missing') assert 0 except model.SafeException as ex: if "HelloWorld/Missing" not in str(ex): raise
def testRejectKey(self): with output_suppressed(): self.child = server.handle_requests( 'Hello', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B') policy = Policy('http://localhost:8000/Hello', config=self.config) assert policy.need_download() sys.stdin = Reply("N\n") try: download_and_execute(policy, ['Hello']) assert 0 except model.SafeException as ex: if "has no usable implementations" not in str(ex): raise ex if "Not signed with a trusted key" not in str( policy.handler.ex): raise ex self.config.handler.ex = None
def testNoMain(self): tmp = tempfile.NamedTemporaryFile() tmp.write("""<?xml version="1.0" ?> <interface xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <implementation version='1.0' id='/bin'/> </interface>""") tmp.flush() policy = Policy(tmp.name, config=self.config) try: download_and_execute(policy, ['Hello']) assert 0 except model.SafeException as ex: assert "library" in str(ex), ex tmp.close()
def testMirrors(self): old_out = sys.stdout try: sys.stdout = StringIO() getLogger().setLevel(ERROR) trust.trust_db.trust_key( 'DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000') self.child = server.handle_requests( server.Give404('/Hello.xml'), 'latest.xml', '/0mirror/keys/6FCF121BE2390E0B.gpg') policy = Policy('http://example.com:8000/Hello.xml', config=self.config) self.config.feed_mirror = 'http://example.com:8000/0mirror' refreshed = policy.solve_with_downloads() policy.handler.wait_for_blocker(refreshed) assert policy.ready finally: sys.stdout = old_out
def testCycle(self): self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <group> <requires interface='%s'/> <implementation id='sha1=123' version='1.0'> <archive href='foo' size='10'/> </implementation> </group> </interface>""" % (foo_iface_uri, foo_iface_uri)) policy = Policy(foo_iface_uri, config=self.config) policy.freshness = 0 recalculate(policy)
def testReplay(self): old_out = sys.stdout try: sys.stdout = StringIO() getLogger().setLevel(ERROR) iface = self.config.iface_cache.get_interface( 'http://example.com:8000/Hello.xml') mtime = int(os.stat('Hello-new.xml').st_mtime) self.config.iface_cache.update_feed_from_network( iface.uri, file('Hello-new.xml').read(), mtime + 10000) trust.trust_db.trust_key( 'DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000') self.child = server.handle_requests( server.Give404('/Hello.xml'), 'latest.xml', '/0mirror/keys/6FCF121BE2390E0B.gpg', 'Hello.xml') policy = Policy('http://example.com:8000/Hello.xml', config=self.config) self.config.feed_mirror = 'http://example.com:8000/0mirror' # Update from mirror (should ignore out-of-date timestamp) refreshed = policy.fetcher.download_and_import_feed( iface.uri, self.config.iface_cache) policy.handler.wait_for_blocker(refreshed) # Update from upstream (should report an error) refreshed = policy.fetcher.download_and_import_feed( iface.uri, self.config.iface_cache) try: policy.handler.wait_for_blocker(refreshed) raise Exception("Should have been rejected!") except model.SafeException as ex: assert "New feed's modification time is before old version" in str( ex) # Must finish with the newest version self.assertEquals( 1235911552, self.config.iface_cache._get_signature_date(iface.uri)) finally: sys.stdout = old_out
def testBestUnusable(self): self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <implementation id='sha1=123' version='1.0' arch='odd-weird' main='dummy'/> </interface>""" % foo_iface_uri) policy = Policy(foo_iface_uri, config=self.config) policy.network_use = model.network_offline recalculate(policy) assert not policy.ready, policy.implementation try: download_and_execute(policy, []) assert False except model.SafeException as ex: assert "has no usable implementations" in str(ex), ex
def testNeedDL(self): self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="0" uri="%s" main='ThisBetterNotExist' xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <implementation version='1.0' id='sha1=123'> <archive href='http://foo/foo.tgz' size='100'/> </implementation> </interface>""" % foo_iface_uri) policy = Policy(foo_iface_uri, config=self.config) policy.freshness = 0 policy.network_use = model.network_full recalculate(policy) assert policy.need_download() assert policy.ready
def testSymlink(self): old_out = sys.stdout try: sys.stdout = StringIO() self.child = server.handle_requests( ('HelloWorld.tar.bz2', 'HelloSym.tgz')) policy = Policy(os.path.abspath('RecipeSymlink.xml'), config=self.config) try: download_and_execute(policy, []) assert False except model.SafeException as ex: if 'Attempt to unpack dir over symlink "HelloWorld"' not in str( ex): raise self.assertEquals( None, basedir.load_first_cache('0install.net', 'implementations', 'main')) finally: sys.stdout = old_out
def testUnknownAlg(self): self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <implementation main='.' id='unknown=123' version='1.0'> <archive href='http://foo/foo.tgz' size='100'/> </implementation> </interface>""" % foo_iface_uri) self.config.fetcher = fetch.Fetcher(self.config) policy = Policy(foo_iface_uri, config=self.config) policy.freshness = 0 try: assert policy.need_download() download_and_execute(policy, []) except model.SafeException as ex: assert 'Unknown digest algorithm' in str(ex)
def testDLfeed(self): self.cache_iface( foo_iface_uri, """<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <feed src='http://example.com'/> </interface>""" % foo_iface_uri) policy = Policy(foo_iface_uri, config=self.config) policy.network_use = model.network_full policy.freshness = 0 assert policy.need_download() feed = self.config.iface_cache.get_feed(foo_iface_uri) feed.feeds = [model.Feed('/BadFeed', None, False)] logger.setLevel(logging.ERROR) assert policy.need_download() # Triggers warning logger.setLevel(logging.WARN)
def testAbsMain(self): tmp = tempfile.NamedTemporaryFile(prefix='test-') tmp.write("""<?xml version="1.0" ?> <interface last-modified="1110752708" uri="%s" xmlns="http://zero-install.sourceforge.net/2004/injector/interface"> <name>Foo</name> <summary>Foo</summary> <description>Foo</description> <group main='/bin/sh'> <implementation id='.' version='1'/> </group> </interface>""" % foo_iface_uri) tmp.flush() policy = Policy(tmp.name, config=self.config) try: downloaded = policy.solve_and_download_impls() if downloaded: policy.handler.wait_for_blocker(downloaded) run.execute_selections(policy.solver.selections, [], stores=policy.config.stores) assert False except SafeException as ex: assert 'Command path must be relative' in str(ex), ex
def testSource(self): iface_cache = self.config.iface_cache foo = iface_cache.get_interface('http://foo/Binary.xml') self.import_feed(foo.uri, 'Binary.xml') foo_src = iface_cache.get_interface('http://foo/Source.xml') self.import_feed(foo_src.uri, 'Source.xml') compiler = iface_cache.get_interface('http://foo/Compiler.xml') self.import_feed(compiler.uri, 'Compiler.xml') self.config.freshness = 0 self.config.network_use = model.network_full p = Policy('http://foo/Binary.xml', config=self.config) tasks.wait_for_blocker(p.solve_with_downloads()) assert p.implementation[foo].id == 'sha1=123' # Now ask for source instead p.requirements.source = True p.requirements.command = 'compile' tasks.wait_for_blocker(p.solve_with_downloads()) assert p.solver.ready, p.solver.get_failure_reason() assert p.implementation[foo].id == 'sha1=234' # The source assert p.implementation[ compiler].id == 'sha1=345' # A binary needed to compile it
def handle(config, options, args, add_ok=True, remove_ok=False): if options.offline: config.network_use = model.network_offline if len(args) == 2: iface = config.iface_cache.get_interface( model.canonical_iface_uri(args[0])) feed_url = args[1] if find_feed_import(iface, feed_url): raise SafeException( _('Interface %(interface)s already has feed %(feed)s') % { 'interface': iface.uri, 'feed': feed_url }) feed = config.iface_cache.get_feed(feed_url) if not feed: blocker = config.fetcher.download_and_import_feed( feed_url, config.iface_cache) tasks.wait_for_blocker(blocker) iface.extra_feeds.append( model.Feed(feed_url, arch=None, user_override=True)) writer.save_interface(iface) return elif len(args) != 1: raise UsageError() x = args[0] print _("Feed '%s':") % x + '\n' x = model.canonical_iface_uri(x) policy = Policy(x, config=config) feed = config.iface_cache.get_feed(x) if policy.network_use != model.network_offline and policy.is_stale(feed): blocker = config.fetcher.download_and_import_feed( x, config.iface_cache) tasks.wait_for_blocker(blocker) candidate_interfaces = policy.get_feed_targets(x) assert candidate_interfaces interfaces = [] for i in range(len(candidate_interfaces)): iface = candidate_interfaces[i] if find_feed_import(iface, x): if remove_ok: print _("%(index)d) Remove as feed for '%(uri)s'") % { 'index': i + 1, 'uri': iface.uri } interfaces.append(iface) else: if add_ok: print _("%(index)d) Add as feed for '%(uri)s'") % { 'index': i + 1, 'uri': iface.uri } interfaces.append(iface) if not interfaces: if remove_ok: raise SafeException( _("%(feed)s is not registered as a feed for %(interface)s") % { 'feed': x, 'interface': candidate_interfaces[0] }) else: raise SafeException( _("%(feed)s already registered as a feed for %(interface)s") % { 'feed': x, 'interface': candidate_interfaces[0] }) print while True: try: i = raw_input( _('Enter a number, or CTRL-C to cancel [1]: ')).strip() except KeyboardInterrupt: print raise SafeException(_("Aborted at user request.")) if i == '': i = 1 else: try: i = int(i) except ValueError: i = 0 if i > 0 and i <= len(interfaces): break print _("Invalid number. Try again. (1 to %d)") % len(interfaces) iface = interfaces[i - 1] feed_import = find_feed_import(iface, x) if feed_import: iface.extra_feeds.remove(feed_import) else: iface.extra_feeds.append(model.Feed(x, arch=None, user_override=True)) writer.save_interface(iface) print '\n' + _("Feed list for interface '%s' is now:") % iface.get_name() if iface.extra_feeds: for f in iface.extra_feeds: print "- " + f.uri else: print _("(no feeds)")