LINUX.ORG.RU

[Slackware] What's new in /etc ?

 


0

0

Апдейт к www.linux.org.ru/view-message.jsp?msgid=4068051

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import sys
import os
import stat
import glob
import difflib
import string
import subprocess

from PyQt4 import QtGui, QtCore

from pygments import highlight
from pygments.lexers import DiffLexer
from pygments.formatters import HtmlFormatter

CWD = os.getcwd()
EDITOR = 'gvim -f'
PATCH = 'patch -p0'
DIR = '/etc'

def usage():
    print "Usage: %s directory" % sys.argv[0]


# find all *.new files in /etc
def findnew (top = '.', depthfirst = True):
    os.chdir(top)
    names = glob.glob('*.new')
    if not depthfirst:
        yield top, names
    for name in names:
        try:
            st = os.lstat(os.path.join(top, name))
        except os.error:
            continue
        if stat.S_ISDIR(st.st_mode):
            for (newtop, children) in findnew (os.path.join(top, name), depthfirst):
                yield newtop, children
    if depthfirst:
        yield top, names


class MainWindow(QtGui.QWidget):
    newlist = None # QListWidget
    diff    = None # QTextEdit
    edit    = None # QPushButton
    accept  = None # QPushButton
    reject  = None # QPushButton
    patch   = None # QPushButton

    newfile = None # /etc/file.new
    oldfile = None # /etc/file

    @QtCore.pyqtSlot()
    def on_partial_diff(self):
        cursor = self.diff.textCursor()
        start  = cursor.selectionStart()
        end    = cursor.selectionEnd()

        # starting and ending line numbers for the selection
        start_line = self.diff.document().findBlock(start).firstLineNumber()
        end_line   = self.diff.document().findBlock(end).firstLineNumber()

        fulldiff  = self.diff.toPlainText().split('\n');
        max_lines = len(fulldiff) - 1

        # two first lines
        partdiff = fulldiff[0] + '\n' + fulldiff[1] + '\n'
        
        # looking for start of diff block (@@ -16,7 +16,7 @@)
        if start_line < 2:
            start_line = 2
        else:
            while fulldiff[start_line][0:2] != '@@':
                start_line = start_line - 1

        # looking for end of diff block (next @@ ... @@ or EOF)
        if end_line <= start_line:
            end_line = start_line + 1
        
        while end_line < max_lines:
            if fulldiff[end_line][0:2] == '@@':
                break
            end_line = end_line + 1
        
        for l in range(start_line, end_line):
            partdiff = partdiff + fulldiff[l] + '\n'
      
        # apply partial patch
        pipe = subprocess.Popen(PATCH, shell=True, stdin=subprocess.PIPE)
        pipe.stdin.writelines(partdiff)
        pipe.stdin.close()
        
        self.reloaddiff(self.newfile)

    def enable_buttons(self):
        n = self.newlist.count()
        self.edit.setEnabled(n > 0)
        self.accept.setEnabled(n > 0)
        self.reject.setEnabled(n > 0)


    @QtCore.pyqtSlot()
    def on_selection_changed(self):
        self.patch.setEnabled(
                self.diff.textCursor().selectionStart() != self.diff.textCursor().selectionEnd()
                )
        

    @QtCore.pyqtSlot()
    def on_accept(self):
        os.rename(self.newfile, self.oldfile)
        self.newlist.takeItem(self.newlist.currentRow())
        self.enable_buttons()

    @QtCore.pyqtSlot()
    def on_reject(self):
        os.remove(self.newfile)
        self.newlist.takeItem(self.newlist.currentRow())
        self.enable_buttons()

    @QtCore.pyqtSlot()
    def on_edit(self):
        self.accept.setEnabled(False)
        self.newlist.setEnabled(False)

        p = subprocess.Popen(EDITOR + ' ' + self.oldfile, shell=True)
        sts = os.waitpid(p.pid, 0)

        self.reloaddiff(self.newfile)
        self.newlist.setEnabled(True)
        self.accept.setEnabled(True)

    @QtCore.pyqtSlot(QtCore.QString)
    def reloaddiff(self, newfile):
        self.diff.clear()
        self.newfile = str(newfile)
        self.oldfile = self.newfile[0:self.newfile.find('.new')]
        if self.newfile:
            tolines    = open(self.newfile, 'U').readlines()
            fromlines  = open(self.oldfile, 'U').readlines()
            difflines  = difflib.unified_diff(fromlines, tolines, self.oldfile, self.newfile)
            difftext   = string.join(difflines, '')

            self.edit.setToolTip(EDITOR + ' ' + self.oldfile)
            self.reject.setToolTip('Remove ' + self.newfile)
            self.accept.setToolTip(self.newfile + ' -> ' + self.oldfile)
            self.accept.setEnabled(bool(difftext))

            colored_rtf  = highlight(difftext, DiffLexer(), HtmlFormatter(full=True))
            self.diff.setText(colored_rtf)

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        mvbox  = QtGui.QVBoxLayout() # main layout
        split  = QtGui.QSplitter(QtCore.Qt.Horizontal, self) # left-right splitter
        
        rpart  = QtGui.QWidget(split) # right side
        lpart  = QtGui.QWidget(split) # left side

        rvbox  = QtGui.QVBoxLayout() # right side layout
        lvbox  = QtGui.QVBoxLayout() # left side layout
        rpart.setLayout(rvbox)
        lpart.setLayout(lvbox)

        self.diff = QtGui.QTextEdit() # to show diff
        self.diff.setReadOnly(True)
       
        self.newlist = QtGui.QListWidget() # to show all *.new files
       # Load all *.new files  
        for (basepath, children) in findnew(DIR):
            for child in children:
                self.newlist.addItem(os.path.join(basepath, child))
        os.chdir(CWD)
        
        quit        = QtGui.QPushButton('&Quit', self)
        self.accept = QtGui.QPushButton('&Accept', self)
        self.reject = QtGui.QPushButton('&Reject', self)
        self.edit   = QtGui.QPushButton('&Edit old...', self)
        self.patch  = QtGui.QPushButton('A&pply selection', self)
        self.patch.setEnabled(False)
        self.patch.setToolTip('Patch using block with selected text')

        lvbox.addWidget(self.diff) # diff is on the left side
        lvbox.addWidget(self.patch) # "apply patch" button (can be invisible)
        buttons = QtGui.QHBoxLayout() # buttons are below it
        lvbox.addLayout(buttons) # buttons layout
        buttons.addWidget(self.accept)
        buttons.addWidget(self.reject)
        buttons.addWidget(self.edit)

        rvbox.addWidget(self.newlist) # file list is on the right side
        rvbox.addWidget(quit) # quit button is below it

        mvbox.addWidget(split) # split is the main widget of main window
        split.addWidget(lpart) # add the left side on the split
        split.addWidget(rpart) # add the right side on the split

        self.resize(640, 480)
        self.setWindowTitle("What\'s new in %s ?" % DIR)
        self.setLayout(mvbox)

        self.connect(quit, QtCore.SIGNAL('clicked()'), QtGui.qApp, QtCore.SLOT('quit()'))
        self.connect(self.edit, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT('on_edit()'))
        self.connect(self.accept, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT('on_accept()'))
        self.connect(self.reject, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT('on_reject()'))
        self.connect(self.diff, QtCore.SIGNAL('selectionChanged()'), self, QtCore.SLOT('on_selection_changed()'))
        self.connect(self.newlist, QtCore.SIGNAL('currentTextChanged(QString)'),
                             self, QtCore.SLOT('reloaddiff(QString)'))
        
        self.newlist.setCurrentRow(0)
        self.enable_buttons()


if len(sys.argv) > 2:
    usage()
    sys.exit(1)
elif len(sys.argv) == 2:
    DIR = sys.argv[1]


app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.show()

sys.exit(app.exec_())
☆☆

что нового в программе:

* Можно указать рабочую директорию: ./newslack /mnt/host/etc

* Можно применить частичный патч: надо выделить часть текста, и будут применены блоки, охваченные выделением.

ip1981 ☆☆
() автор топика
Ответ на: комментарий от ptah_alexs

# ls -lh /etc/.git
итого 202K
drwxr-xr-x 2 root root 48 2008-07-13 17:58 branches/
-rw-r--r-- 1 root root 31 2009-09-16 18:36 COMMIT_EDITMSG
-rw-r--r-- 1 root root 92 2008-07-13 17:58 config
-rw-r--r-- 1 root root 58 2008-07-13 17:58 description
-rw-r--r-- 1 root root 23 2008-07-13 17:58 HEAD
drwxr-xr-x 2 root root 368 2008-07-13 17:58 hooks/
-rw-r--r-- 1 root root 178K 2009-09-21 21:35 index
drwxr-xr-x 2 root root 72 2008-07-13 17:58 info/
drwxr-xr-x 3 root root 96 2008-07-13 17:59 logs/
drwxr-xr-x 260 root root 6,1K 2008-12-18 22:20 objects/
drwxr-xr-x 4 root root 96 2008-07-13 17:58 refs/

ip1981 ☆☆
() автор топика

Поправка. Функцию findnew выкинуть нафих.

FIND   = "find %s -type f -name '*.new'"

...

       
       # Load all *.new files
        pipe = subprocess.Popen(FIND % DIR, shell=True, stdout=subprocess.PIPE)
        news = pipe.stdout.read().split('\n')
        for new in news:
            if new:
                self.newlist.addItem(new)
ip1981 ☆☆
() автор топика
Ответ на: комментарий от provaton

Присваивания с выравниваниями всегда выглядят лучше чем без. На ПЕП-8 положить.

Reset ★★★★★
()
Ответ на: комментарий от provaton

ПЕП-8 не рекомендует использовать больше одного пробела вокруг =

Нифига, там речь о коротких и длинных именах (сильно различающихся по длине)

http://www.python.org/dev/peps/pep-0008/

Yes:

          x = 1
          y = 2
          long_variable = 3

      No:

          x             = 1
          y             = 2
          long_variable = 3
ip1981 ☆☆
() автор топика
Ответ на: комментарий от ip1981

> Нифига, там речь о коротких и длинных именах (сильно различающихся по длине)

Не, там написано так:

Avoid extraneous whitespace in the following situations:

...поскипано...

- More than one space around an assignment (or other) operator to align it with another.

И потом твой пример. Про "сильно различающихся по длине" это уже ты придумал.

provaton ★★★★★
()
Ответ на: комментарий от provaton

ср. "x" и "long_variable"

На то примеры и даны, чтобы лучше донести смысел :-)

И вообще, если бы это было настолько важно, это внесли бы в спецификацию языка, как отступы :-)

ip1981 ☆☆
() автор топика
Ответ на: комментарий от provaton

> это дань уважения тем людям которым ты даешь читать свой код.

Ну, ПЕП тут ни при чём ;-) Уже было высказано мнение, что в этом случае с выравниеванием красивше, мне то же так кажется :-)

http://lh6.ggpht.com/_4J6r8bz5Mb8/Sr4AofKGhnI/AAAAAAAAASs/Pn1MUH79Qlw/s640/ne...

ip1981 ☆☆
() автор топика
Ответ на: комментарий от ip1981

Я еще немного покритикую.

Вместо string.join(a, '') лучше юзать ''.join(a).

Поиск текста красивше делается регекспами, а не if something.startswith(tralala)

запуск главного цикла хорошо бы запихнуть в if __name__ == '__main__'

provaton ★★★★★
()
Ответ на: комментарий от provaton

Благодарю :-)

Но

> Вместо string.join(a, '') лучше юзать ''.join(a).

Явное лучше неявного :-)

> Поиск текста красивше делается регекспами

Я программист старой закалки, и считаю, что регекспы помедленнее, чем strcmp :-)

> запуск главного цикла хорошо бы запихнуть в if __name__ == '__main__'

why?

ip1981 ☆☆
() автор топика
Ответ на: комментарий от ip1981

> Явное лучше неявного

А где тут неявное?

> Я программист старой закалки, и считаю, что регекспы помедленнее, чем strcmp :-)

Необоснованно, но спорить не буду.

> why

Потому что функции модуля может понадобится заимпортить из другого модуля, например для тестов.

provaton ★★★★★
()
Ответ на: комментарий от provaton

> Но имхо точное соблюдение пеп-8 это дань уважения тем людям которым ты даешь читать свой код.

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

true_admin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.