This commit is contained in:
Ertuğrul Erata
2019-08-17 16:17:19 +03:00
9 changed files with 195 additions and 29 deletions
+59
View File
@@ -0,0 +1,59 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2014, marcin.bojara (at) gmail.com
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import gettext
import optparse
import pisi.cli.command as command
import pisi.context as ctx
import pisi.db
import pisi.util as util
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.gettext
class ListOrphaned(command.Command, metaclass=command.autocommand):
__doc__ = _("""List orphaned packages
Usage: list-orphaned
Lists packages installed as dependency, but no longer needed by any other installed package.
""")
def __init__(self, args):
super(ListOrphaned, self).__init__(args)
self.installdb = pisi.db.installdb.InstallDB()
name = ("list-orphaned", "lo")
def options(self):
group = optparse.OptionGroup(self.parser, _("list-orphaned options"))
group.add_option("-a", "--all", action="store_true",
default=False, help=_("Show all packages without reverse dependencies"))
group.add_option("-x", "--exclude", action="append",
default=None, help=_("Ignore packages and components whose basenames match pattern."))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
orphaned = self.installdb.get_no_rev_deps() if self.options.all else self.installdb.get_orphaned()
if self.options.exclude:
orphaned = pisi.blacklist.exclude(orphaned, ctx.get_option('exclude'))
if orphaned:
ctx.ui.info(_("Orphaned packages:"))
ctx.ui.info(util.format_by_columns(sorted(orphaned)))
else: ctx.ui.info(_("No orphaned packages"))
+20 -15
View File
@@ -10,55 +10,59 @@
# Please read the COPYING file. # Please read the COPYING file.
# #
import sys
import optparse
import gettext import gettext
__trans = gettext.translation('pisi', fallback=True) import optparse
_ = __trans.gettext import sys
import pisi import pisi
import pisi.cli import pisi.cli
import pisi.cli.command as command
import pisi.cli.addrepo import pisi.cli.addrepo
import pisi.cli.blame import pisi.cli.blame
import pisi.cli.build import pisi.cli.build
import pisi.cli.check import pisi.cli.check
import pisi.cli.clean import pisi.cli.clean
import pisi.cli.command as command
import pisi.cli.configurepending import pisi.cli.configurepending
import pisi.cli.deletecache import pisi.cli.deletecache
import pisi.cli.delta import pisi.cli.delta
import pisi.cli.disablerepo
import pisi.cli.emerge import pisi.cli.emerge
import pisi.cli.enablerepo
import pisi.cli.fetch import pisi.cli.fetch
import pisi.cli.graph import pisi.cli.graph
import pisi.cli.history
import pisi.cli.index import pisi.cli.index
import pisi.cli.info import pisi.cli.info
import pisi.cli.install import pisi.cli.install
import pisi.cli.history
import pisi.cli.listnewest
import pisi.cli.listavailable import pisi.cli.listavailable
import pisi.cli.listcomponents import pisi.cli.listcomponents
import pisi.cli.listinstalled import pisi.cli.listinstalled
import pisi.cli.listnewest
import pisi.cli.listorphaned
import pisi.cli.listpending import pisi.cli.listpending
import pisi.cli.listrepo import pisi.cli.listrepo
import pisi.cli.listsources import pisi.cli.listsources
import pisi.cli.listupgrades import pisi.cli.listupgrades
import pisi.cli.rebuilddb import pisi.cli.rebuilddb
import pisi.cli.remove import pisi.cli.remove
import pisi.cli.removeorphaned
import pisi.cli.removerepo import pisi.cli.removerepo
import pisi.cli.enablerepo
import pisi.cli.disablerepo
import pisi.cli.searchfile
import pisi.cli.search import pisi.cli.search
import pisi.cli.searchfile
import pisi.cli.updaterepo import pisi.cli.updaterepo
import pisi.cli.upgrade import pisi.cli.upgrade
#FIXME: why does this has to be imported last # FIXME: why does this has to be imported last
import pisi.cli.help import pisi.cli.help
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.gettext
class ParserError(pisi.Exception): class ParserError(pisi.Exception):
pass pass
class PreParser(optparse.OptionParser): class PreParser(optparse.OptionParser):
"""consumes any options, and finds arguments from command line""" """consumes any options, and finds arguments from command line"""
@@ -68,11 +72,11 @@ class PreParser(optparse.OptionParser):
def error(self, msg): def error(self, msg):
raise ParserError(msg) raise ParserError(msg)
def parse_args(self, args=None): def parse_args(self, args=None, values=None):
self.opts = [] self.opts = []
self.rargs = self._get_args(args) self.rargs = self._get_args(args)
self._process_args() self._process_args()
return (self.opts, self.args) return self.opts, self.args
def _process_args(self): def _process_args(self):
args = [] args = []
@@ -81,6 +85,7 @@ class PreParser(optparse.OptionParser):
first_arg = False first_arg = False
while rargs: while rargs:
arg = rargs[0] arg = rargs[0]
def option(): def option():
if not self.allow_interspersed_args and first_arg: if not self.allow_interspersed_args and first_arg:
self.error(_('Options must precede non-option arguments')) self.error(_('Options must precede non-option arguments'))
@@ -118,7 +123,7 @@ class PisiCLI(object):
self.parser = PreParser(version="%prog " + pisi.__version__) self.parser = PreParser(version="%prog " + pisi.__version__)
try: try:
opts, args = self.parser.parse_args(args=orig_args) opts, args = self.parser.parse_args(args=orig_args)
if len(args)==0: # more explicit than using IndexError if len(args) == 0: # more explicit than using IndexError
if 'version' in opts: if 'version' in opts:
self.parser.print_version() self.parser.print_version()
sys.exit(0) sys.exit(0)
+55
View File
@@ -0,0 +1,55 @@
# -*- coding:utf-8 -*-
#
# Copyright (C) 2014, marcin.bojara (at) gmail.com
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import gettext
import optparse
import pisi.api
import pisi.cli.command as command
import pisi.context as ctx
import pisi.db
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.gettext
class RemoveOrphaned(command.PackageOp, metaclass=command.autocommand):
__doc__ = _("""Remove orphaned packages
Usage: remove-orphaned
Remove all orphaned packages from the system.
""")
def __init__(self,args):
super(RemoveOrphaned, self).__init__(args)
self.installdb = pisi.db.installdb.InstallDB()
name = ("remove-orphaned", "ro")
def options(self):
group = optparse.OptionGroup(self.parser, _("remove-orphaned options"))
super(RemoveOrphaned, self).options(group)
group.add_option("-x", "--exclude", action="append",
default=None, help=_("When removing orphaned, ignore packages and components whose basenames match pattern."))
self.parser.add_option_group(group)
def run(self):
self.init(database = True, write = False)
orphaned = self.installdb.get_orphaned()
if ctx.get_option('exclude'):
orphaned = pisi.blacklist.exclude(orphaned, ctx.get_option('exclude'))
pisi.api.remove(orphaned)
+3 -2
View File
@@ -36,8 +36,9 @@ def is_char_valid(char):
def is_method_missing(exception): def is_method_missing(exception):
"""Tells if exception is about missing method in COMAR script""" """Tells if exception is about missing method in COMAR script"""
if exception._dbus_error_name in ("tr.org.pardus.comar.python.missing", if exception.get_dbus_name() in ("tr.org.pardus.comar.python.missing", "tr.org.pardus.comar.Missing"):
"tr.org.pardus.comar.Missing"): return True
if exception.__context__.get_dbus_name() in ("tr.org.pardus.comar.python.missing", "tr.org.pardus.comar.Missing"):
return True return True
return False return False
+1
View File
@@ -95,6 +95,7 @@ class Constants(metaclass=Singleton):
self.__c.needs_reboot = "needsreboot" self.__c.needs_reboot = "needsreboot"
self.__c.files_db = "files.db" self.__c.files_db = "files.db"
self.__c.repos = "repos" self.__c.repos = "repos"
self.__c.installed_extra = "installedextra"
#file/directory permissions #file/directory permissions
self.__c.umask = 0o022 self.__c.umask = 0o022
+22
View File
@@ -67,6 +67,15 @@ class InstallDB(lazydb.LazyDB):
def init(self): def init(self):
self.installed_db = self.__generate_installed_pkgs() self.installed_db = self.__generate_installed_pkgs()
self.rev_deps_db = self.__generate_revdeps() self.rev_deps_db = self.__generate_revdeps()
self.installed_extra = self.__generate_installed_extra()
def __generate_installed_extra(self):
ie = []
ie_path = os.path.join(ctx.config.info_dir(), ctx.const.installed_extra)
if os.path.isfile(ie_path):
with open(ie_path) as ie_file:
ie.extend(ie_file.read().strip().split("\n"))
return ie
def __generate_installed_pkgs(self): def __generate_installed_pkgs(self):
def split_name(dirname): def split_name(dirname):
@@ -249,6 +258,19 @@ class InstallDB(lazydb.LazyDB):
return rev_deps return rev_deps
def get_orphaned(self):
"""
get list of packages installed as extra dependency,
but without reverse dependencies now.
"""
return [x for x in self.installed_extra if not self.get_rev_deps(x)]
def get_no_rev_deps(self):
"""
get installed packages list which haven't reverse dependencies.
"""
return [x for x in self.installed_db if not self.get_rev_deps(x)]
def pkg_dir(self, pkg, version, release): def pkg_dir(self, pkg, version, release):
return pisi.util.join_path(ctx.config.packages_dir(), pkg + '-' + version + '-' + release) return pisi.util.join_path(ctx.config.packages_dir(), pkg + '-' + version + '-' + release)
+3 -1
View File
@@ -20,10 +20,12 @@ import string
# lower borks for international locales. What we want is ascii lower. # lower borks for international locales. What we want is ascii lower.
lower_map = str.maketrans(string.ascii_uppercase, string.ascii_lowercase) lower_map = str.maketrans(string.ascii_uppercase, string.ascii_lowercase)
class Singleton(object): class Singleton(object):
_the_instances = {} _the_instances = {}
def __new__(type): def __new__(type):
if not type.__name__ in Singleton._the_instances: if type.__name__ not in Singleton._the_instances:
Singleton._the_instances[type.__name__] = object.__new__(type) Singleton._the_instances[type.__name__] = object.__new__(type)
return Singleton._the_instances[type.__name__] return Singleton._the_instances[type.__name__]
+19 -3
View File
@@ -27,7 +27,8 @@ import pisi.pgraph as pgraph
import pisi.ui as ui import pisi.ui as ui
import pisi.db import pisi.db
def install_pkg_names(A, reinstall = False):
def install_pkg_names(A, reinstall=False, extra=False):
"""This is the real thing. It installs packages from """This is the real thing. It installs packages from
the repository, trying to perform a minimum number of the repository, trying to perform a minimum number of
installs""" installs"""
@@ -78,7 +79,8 @@ def install_pkg_names(A, reinstall = False):
if ctx.get_option('dry_run'): if ctx.get_option('dry_run'):
return True return True
if set(order) - A_0: extra_packages = set(order) - A_0
if extra_packages:
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')): if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
return False return False
@@ -91,10 +93,18 @@ def install_pkg_names(A, reinstall = False):
conflicts = operations.helper.check_conflicts(order, packagedb) conflicts = operations.helper.check_conflicts(order, packagedb)
paths = [] paths = []
extra_paths = {}
for x in order: for x in order:
ctx.ui.info(util.colorize(_("Downloading %d / %d") % (order.index(x)+1, len(order)), "yellow")) ctx.ui.info(util.colorize(_("Downloading %d / %d") % (order.index(x)+1, len(order)), "yellow"))
install_op = atomicoperations.Install.from_name(x) install_op = atomicoperations.Install.from_name(x)
paths.append(install_op.package_fname) paths.append(install_op.package_fname)
if x in extra_packages or (extra and x in A):
extra_paths[install_op.package_fname] = x
elif reinstall and x in installdb.installed_extra:
installdb.installed_extra.remove(x)
with open(os.path.join(ctx.config.info_dir(), ctx.const.installed_extra), "w") as ie_file:
ie_file.write("\n".join(installdb.installed_extra) + ("\n" if installdb.installed_extra else ""))
# fetch to be installed packages but do not install them. # fetch to be installed packages but do not install them.
if ctx.get_option('fetch_only'): if ctx.get_option('fetch_only'):
@@ -107,6 +117,12 @@ def install_pkg_names(A, reinstall = False):
ctx.ui.info(util.colorize(_("Installing %d / %d") % (paths.index(path)+1, len(paths)), "yellow")) ctx.ui.info(util.colorize(_("Installing %d / %d") % (paths.index(path)+1, len(paths)), "yellow"))
install_op = atomicoperations.Install(path) install_op = atomicoperations.Install(path)
install_op.install(False) install_op.install(False)
try:
with open(os.path.join(ctx.config.info_dir(), ctx.const.installed_extra), "a") as ie_file:
ie_file.write("%s\n" % extra_paths[path])
installdb.installed_extra.append(extra_paths[path])
except KeyError:
pass
return True return True
@@ -199,7 +215,7 @@ def install_pkg_files(package_URIs, reinstall = False):
ctx.ui.info(util.format_by_columns(sorted(extra_packages))) ctx.ui.info(util.format_by_columns(sorted(extra_packages)))
if not ctx.ui.confirm(_('Do you want to continue?')): if not ctx.ui.confirm(_('Do you want to continue?')):
raise pisi.Error(_('External dependencies not satisfied')) raise pisi.Error(_('External dependencies not satisfied'))
install_pkg_names(extra_packages, reinstall=True) install_pkg_names(extra_packages, reinstall=True, extra=True)
class PackageDB: class PackageDB:
def get_package(self, key, repo = None): def get_package(self, key, repo = None):
+13 -8
View File
@@ -10,19 +10,20 @@
# Please read the COPYING file. # Please read the COPYING file.
# #
import gettext
import sys import sys
import gettext import pisi
import pisi.atomicoperations as atomicoperations
import pisi.context as ctx
import pisi.db
import pisi.pgraph as pgraph
import pisi.ui as ui
import pisi.util as util
__trans = gettext.translation('pisi', fallback=True) __trans = gettext.translation('pisi', fallback=True)
_ = __trans.gettext _ = __trans.gettext
import pisi
import pisi.context as ctx
import pisi.atomicoperations as atomicoperations
import pisi.pgraph as pgraph
import pisi.util as util
import pisi.ui as ui
import pisi.db
def remove(A, ignore_dep = False, ignore_safety = False): def remove(A, ignore_dep = False, ignore_safety = False):
"""remove set A of packages from system (A is a list of package names)""" """remove set A of packages from system (A is a list of package names)"""
@@ -81,6 +82,10 @@ in the respective order to satisfy dependencies:
for x in order: for x in order:
if installdb.has_package(x): if installdb.has_package(x):
atomicoperations.remove_single(x) atomicoperations.remove_single(x)
if x in installdb.installed_extra:
installdb.installed_extra.remove(x)
with open(os.path.join(ctx.config.info_dir(), ctx.const.installed_extra), "w") as ie_file:
ie_file.write("\n".join(installdb.installed_extra) + ("\n" if installdb.installed_extra else ""))
else: else:
ctx.ui.info(_('Package %s is not installed. Cannot remove.') % x) ctx.ui.info(_('Package %s is not installed. Cannot remove.') % x)