Запустив данную команду после каждого изменения в файле


Я хотел бы услышать некоторые комментарии на этот небольшой скрипт, который (я надеюсь) уменьшает повторяющиеся задачи, такие как обновление страницы, компиляции и так далее до минимума.

# -*- coding: utf-8 -*-
# Author: Jacek 'Sabr' Karolak (j.karolak@sabr.pl)
# Watch [files] for change, and after every change run given [command]
#
#
# Example watchdog script: safari.py
#  #!/usr/bin/env python
#  # -*- coding: utf-8 -*-
#
#  from loop import Loop
#
#  if __name__ == "__main__":
#      Loop(["osascript", "-e", """tell application "Safari"
#              do JavaScript "window.location.reload()" in front document
#          end tell"""]).run()
#   EOF
#
# Make safari.py executable (chmox +x safari.py) or use it with python.
# python safari.py FILES_TO_WATCH
# Whenever FILES_TO_WATCH change, refresh Safari in background :-)
#

__all__ = ["Loop"]

import argparse, subprocess
from os import getcwd, listdir, stat
from os.path import exists
from time import sleep


class Loop(object):

    def __init__(self, cmd):
        self._command = cmd
        self._files_to_watch = {}

    def _watchdog(self):
        '''Check wheter any file in self._files_to_watch changed,
        if so fire self._command'''

        check_file = lambda f: stat(f).st_mtime
        files = self._files_to_watch

        any_file_changed = False

        while True:

            # Check each file for st_mtime change (modification)
            for f in files.keys():
                actual_mtime = check_file(f)
                if not files[f] == actual_mtime:
                    any_file_changed = f
                    files[f] = actual_mtime

            if any_file_changed:
                # run command
                print('File: \'{}\' changed since last check.'\
                        .format(any_file_changed))
                any_file_changed = False
                subprocess.call(self._command)

            # sleep before next check
            sleep(0.5)

    def _set_files_to_watch(self, files):
        '''Process args files wheter they exists and include current directory
        content if requested.'''
        if '.' in files:
            files.remove('.')
            # combine all other given files with current working directory
            # content, without dot files
            files += [f for f in listdir(getcwd())\
                    if not f.startswith('.')]

        # make f list unique
        files = set(files)

        # check rights (in order to perform system stat) and wheter they exist
        for f in files:
            if not exists(f):
                msg = 'file \'{}\' does not exists, or I don\'t\
                        have access rights.'.format(f)
                raise IOError(msg)

        # save files to watch in instance variable
        self._files_to_watch = dict.fromkeys(files)

        # set modification times
        for file_key in self._files_to_watch.keys():
            self._files_to_watch[file_key] = stat(file_key).st_mtime


    def run(self):
        '''Parses command line arguments, processes files list,
        and fires watchdog.'''
        parser = argparse.ArgumentParser(description='Checkes wheter given \
                files change, when they do, runs given command.')
        parser.add_argument('files', metavar='F', type=str, nargs='+',\
                help='list files to process, if you add . in list it will\
                watch also all non dot files in current dir.\
                If you want to have all non dot files and some dot ones use:\
                \n.file1 .file2 .file2 . .file3 it will combine specified dot\
                files and all others.')

        args = parser.parse_args()

        self._set_files_to_watch(args.files)

        print('Started watching...')
        self._watchdog()


if __name__ == '__main__':
    pass


424
2
задан 13 ноября 2011 в 04:11 Источник Поделиться
Комментарии
1 ответ

class Loop(object):

Петли не очень описательное имя для этого объекта. Что-то вроде сторожевой или FileWatcher бы больше смысла

    def __init__(self, cmd):
self._command = cmd
self._files_to_watch = {}

Это сопоставление имен файлов по времени модификации, но название не отражает этого. Я предлагаю называть _file_modification_times.

    def _watchdog(self):
'''Check wheter any file in self._files_to_watch changed,
if so fire self._command'''

check_file = lambda f: stat(f).st_mtime

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

        files = self._files_to_watch

Что значит скопировать _files_to_watch в локальную переменную помочь?

        any_file_changed = False

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

        while True:

# Check each file for st_mtime change (modification)
for f in files.keys():
actual_mtime = check_file(f)
if not files[f] == actual_mtime:
any_file_changed = f
files[f] = actual_mtime

if any_file_changed:

Более простой подход:

new_modification_times = dict( (filename, os.stat(filename).st_mtime) for filename in files)
if new_modication_times != files:
self._file_modification_times = new_modification_times

В общем, если вы окажетесь желая набор булевых флагов, это знак, вы не делаете вещи, подходящие для Python путь.

                # run command
print('File: \'{}\' changed since last check.'\
.format(any_file_changed))
any_file_changed = False
subprocess.call(self._command)

# sleep before next check
sleep(0.5)

def _set_files_to_watch(self, files):
'''Process args files wheter they exists and include current directory
content if requested.'''
if '.' in files:
files.remove('.')

Эта особая интерпретация '.' это может в конечном итоге быть запутанным.

            # combine all other given files with current working directory
# content, without dot files
files += [f for f in listdir(getcwd())\
if not f.startswith('.')]

# make f list unique
files = set(files)

Поскольку вы помещаете их в словарь позже, uniquifiying здесь довольно бессмысленно.

        # check rights (in order to perform system stat) and wheter they exist
for f in files:
if not exists(f):
msg = 'file \'{}\' does not exists, or I don\'t\
have access rights.'.format(f)
raise IOError(msg)

А потом prechecking этого, почему бы вам не просто дайте ОС.стат() жаловаться, когда он пытается получить доступ к файлам

        # save files to watch in instance variable
self._files_to_watch = dict.fromkeys(files)

# set modification times
for file_key in self._files_to_watch.keys():
self._files_to_watch[file_key] = stat(file_key).st_mtime

Нет никакой пользы для предварительного заполнения здесь словарь. Вместо этого, вы можете создать пустой словарь и перебирать файлы вместо .ключи(). А еще лучше, вы можете использовать словарь уловка, которую я использовал выше, чтобы заполнить его. Вы можете поместить его в функцию и вызвать ее в обоих местах.

    def run(self):
'''Parses command line arguments, processes files list,
and fires watchdog.'''
parser = argparse.ArgumentParser(description='Checkes wheter given \
files change, when they do, runs given command.')
parser.add_argument('files', metavar='F', type=str, nargs='+',\
help='list files to process, if you add . in list it will\
watch also all non dot files in current dir.\
If you want to have all non dot files and some dot ones use:\
\n.file1 .file2 .file2 . .file3 it will combine specified dot\
files and all others.')

args = parser.parse_args()

self._set_files_to_watch(args.files)

print('Started watching...')
self._watchdog()

Вы, вероятно, следует положить командной строки разбора логики, где-то помимо этом классе. Это делает это тяжелее, чтобы повторно использовать этот класс, если он смешивает разбора командной строки с фактической логики. Я бы парсить командную строку в функцию main отдельно и затем назвать это.

if __name__ == '__main__':
pass

Почему?

4
ответ дан 13 ноября 2011 в 07:11 Источник Поделиться