Автор Тема: Удаление хвостов удаляемых пакетов по признаку ненужности другим пакетам  (Прочитано 4294 раз)

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 568
По хорошему надо бы посмотреть как списки пакетов получает, например Synaptic, может там попроще чего.

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 568
Графику и интерактивность делать не хочу.
Жаль, без этого будут часто выносится действительно нужные вещи. Хотя с другой стороны их же автоматом можно потом снова и установить. По-крайней мере это намного легче чем ручное разгребание пакетов в таких количествах.

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 566
Ничто не мешает прервать процесс удаления когда apt-get будет спрашивать подтверждения. Подправить параметры и повторить.А смотреть глазами что предлагается удалению нужно все равно.

На самом деле нужно программное api к базе установленных пакетов и их зависимостей. Для 2-го питона даже есть модуль для работы с apt - но он сломан - возникает exception  при import-е
« Последнее редактирование: 17.03.2017 22:02:37 от ASte »

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 568
Ничто не мешает прервать процесс удаления когда apt-get будет спрашивать подтверждения. Подправить параметры и повторить.А смотреть глазами что предлагается удалению нужно все равно.
Это, да. Но для тяжёлых приложений попавших на удаление будет много зависимостей. Хотя я уже вижу ручной обход. Для описанного выше примера с firefox:
Запросить такой же список пакетов для удаления firefox и убрать эти пакеты из первого списка. Пусть хоть так, это здорово облегчит задачу очистки системы. Да и для проверки алгоритма вполне уже пригодно. Может ещё какие казусы всплывут. Блеклист в будущем ещё можно расширить, чтобы не вынести вещи без которых система вообще умрёт.

На самом деле нужно программное api к базе установленных пакетов и их зависимостей. Для 2-го питона даже есть модуль для работы с apt - но он сломан - возникает exception  при import-е
Заглянул мельком в Synaptic, он, кажется, так и делает. Деревья зависимостей строит вполне быстро.

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 568
А кстати, можно же сделать чтобы читался определённый файл исключений и добавлялся в блэклист. Тогда после первого прохода можно будет добавить туда сохраняемое и запустить всё это хозяйство ещё раз.

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 566
Версия скрипта с "белым списком"
Для случая с firefox приведенного выше:
$./DepTree.py  firefox-vimperator firefox+

#!/usr/bin/python3


import subprocess
import sys


def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)


def get_dependency(package):
    if package == "":
        return None
    resp = subprocess.check_output(["rpm", "--requires", "--qf", "'%{REQUIRENAME}'", "-q", package],
                                   universal_newlines=True)
    s = str(resp)
    #    eprint(s)
    if s and len(s.strip()) > 0:
        return s.replace("'", "").split(sep="\n")
    else:
        return None


def get_all_packages():
    resp = subprocess.check_output(["rpm", "-qa", "--qf", "'%{name}\n'"], universal_newlines=True)
    s = str(resp)
    return s.replace("'", "").split("\n")


def get_whats_req(package):
    args = ["/bin/sh", "-c",
            "rpm --test -e " + package +
            " 2>&1 >/dev/null |  egrep  -o -e 'для.+' | sed 's/для //' | sed 's/-[^-]*-[^-]*$//'"]

    resp = subprocess.check_output(args, universal_newlines=True, stderr=None)

    s = str(resp).strip("\n")
    if s and len(s.strip()) > 0:
        return s.replace("'", "").split(sep="\n")
    else:
        return None


candidats = {}
all_packages = get_all_packages()

blacklist = set()
whitelist = set()


def process_pckg_list(pckg_list):
    # eprint(blacklist)
    for package in pckg_list:
        try:
            dep = candidats[package]
            has_before = True
        except KeyError:
            dep = set()
            candidats[package] = dep
            has_before = False

        try:
            dep_list = get_dependency(package)
            if dep_list is None:
                return True
        except subprocess.CalledProcessError:
            eprint("error while processing :", package)
            # xtree.pop(package)
            return False

        for s in dep_list:
            s = s.strip()
            if len(s) == 0:
                continue
            if "(" in s:
                continue
            if "/" in s:
                continue
            if s in whitelist:
                continue

            try:
                pname = s.strip().split()[0].strip()
            except IndexError:
                eprint("index error:", s)
                continue
            if pname in blacklist:
                continue
            # print(package, "::", s,"->",pname)
            dep.add(pname)
            if pname not in candidats:
                status = process_pckg_list([pname])
                if not status:
                    try:
                        if not has_before:
                            eprint("blacklist:", pname)
                            blacklist.add(pname)
                            del candidats[pname]
                    except KeyError:
                        pass

    return True


def main():
    args = sys.argv
    args.pop(0)
    args1 = set(args.copy())
    for p in args:
        if p[-1] == '+':
            whitelist.add(p[0:-1])
            try:
                args1.remove(p)
            except KeyError:
                pass
            continue
        deps = get_whats_req(p)
        if deps:
            args1 = args1.union(set(deps))

    args1 -= whitelist
    eprint(args1)
    eprint(whitelist)

    process_pckg_list(args1)
    pass_count = 1

    class NeedPassException(Exception):
        pass

    dep_cache = {}

    while True:
        pass_count += 1
        need_next_pass = False
        try:
            for p in candidats:
                try:
                    deps = dep_cache[p]
                except KeyError:
                    try:
                        deps = get_whats_req(p)
                        dep_cache[p] = deps
                    except subprocess.CalledProcessError:
                        dep_cache[p] = []
                        continue
                if deps is None:
                    continue

                for r in deps:
                    r = r.strip()
                    if len(r) == 0:
                        continue
                    if r == p:
                        continue
                    if r not in all_packages:
                        continue
                    if r not in candidats or r in whitelist:
                        # noinspection PyUnusedLocal
                        need_next_pass = True
                        xlist = candidats[p]
                        for c1 in xlist:
                            try:
                                del candidats[c1]
                            except KeyError:
                                pass
                        try:
                            del candidats[p]
                        except KeyError:
                            pass

                        raise NeedPassException
                    pass
                pass
        except NeedPassException:
            continue
        if not need_next_pass:
            break
    pass
    result = ''
    eprint("Всего найдено пакетов для удаления:", len(candidats))
    for s in candidats.keys():
        result = result + s + " "
    print(result)


if __name__ == "__main__":
    main()
« Последнее редактирование: 18.03.2017 17:44:00 от ASte »

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 568
Я правильно понял: firefox-vimperator firefox+
тут firefox-vimperator удаляется,
firefox+ из удаления со всеми пакетами от которых зависит исключен?

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 568
Хмм... Получаю ошибку.
$ ./DepTree.py firefox-vimperator firefox+
Traceback (most recent call last):
  File "./DepTree.py", line 48, in <module>
    all = getAllpackages()
  File "./DepTree.py", line 29, in getAllpackages
    return s.replace("'", "").split(sep="\n")
TypeError: split() takes no keyword arguments

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 566
А кстати, можно же сделать чтобы читался определённый файл исключений
В принципе можно и так.

Сейчас было бы хорошо,  чтобы интересующиеся - особенно "глазастые", кто может по списку пакетов заметить сразу что-то подозрительно лишнее или что-то, не попавшее в список, что должно было туда попасть,  "погоняли" бы текущую версию на разных вариантах чтобы проверить алгоритм и если нужно внести в него правки.
А потом уже можно будет и рюшечек навесить.

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 566
Легко ошибка может быть. Посмотрю.
Но у меня не воспроизводится...
Это моя вторая программка на python-е...
« Последнее редактирование: 17.03.2017 23:34:40 от ASte »

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 566
Попробуйте заменить в 29-ю строку на такую
return s.replace("'", "").split("\n")

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 566
Я правильно понял: firefox-vimperator firefox+
тут firefox-vimperator удаляется,
firefox+ из удаления со всеми пакетами от которых зависит исключен?
Да, оно его записывает в белый список и исключает из списка кандидатов если он туда попал.
Соответственно он и его зависимости считаются "внешними" и если он тоже от чего-то удаляемого зависит то это "удаляемое" тоже вычеркнется из списка кандидатов на удаление.

Кстати, если есть еще какие-то дополнения у которых firefox в зависимостях, он его должны "удержать" и без белого списка.
« Последнее редактирование: 17.03.2017 23:42:47 от ASte »

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 568
Теперь так:
$ ./DepTree1.py firefox
Traceback (most recent call last):
  File "./DepTree1.py", line 205, in <module>
    main()
  File "./DepTree1.py", line 108, in main
    args1 = set(args.copy())
AttributeError: 'list' object has no attribute 'copy'
У меня тут может более старая версия питона (или не полностью установлен), надо на другой машине проверить.
« Последнее редактирование: 17.03.2017 23:53:28 от stranger573 »

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 568
С аргументами кстати, даже удобнее,чем с файлом если их конечно можно несколько забивать.

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 566
Тут другая проблема есть:
$ apt-cache depends firefox-vimperator
firefox-vimperator-3.8.4-alt2.git20150201
  Требует: firefox >= 3.0
    firefox-esr-45.8.0-alt0.M80P.1
  Требует: <vim>
    vim-enhanced-4:8.0.381-alt0.M80P.1
    vim-console-4:8.0.381-alt0.M80P.1
    vim-X11-4:8.0.381-alt0.M80P.1
  Требует: </usr/share/vim/vimfiles/ftdetect>
    vim-common-4:8.0.381-alt0.M80P.1
    puppet-4.8.1-alt0.M80P.1
  Требует: </usr/share/vim/vimfiles/syntax>
    vim-common-4:8.0.381-alt0.M80P.1
    puppet-4.8.1-alt0.M80P.1
$ rpm -q firefox-vimperator -R
firefox >= 3.0
vim 
/usr/share/vim/vimfiles/ftdetect 
/usr/share/vim/vimfiles/syntax 
rpmlib(PayloadIsLzma) 

Но rpm отрабатывает мгновенно, а apt-cache - примерно 2-3 сек..
А поскольку вызовов много при исследовании зависимостей то 2-3 сек. выливаются в минуты...