Python подпроцесс "объект не имеет атрибута" fileno "" ошибка - PullRequest
7 голосов
/ 22 апреля 2009

Этот код генерирует «AttributeError: объект Popen» не имеет атрибута «fileno» при запуске с Python 2.5.1

Код:

def get_blame(filename): 
    proc = []
    proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE))
    proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE)
    proc.append(Popen(['tr', r"'\040'", r"';'"], stdin=proc[-1]), stdout=PIPE)
    proc.append(Popen(['cut', r"-d", r"\;", '-f', '3'], stdin=proc[-1]), stdout=PIPE)
    return proc[-1].stdout.read()

Stack:

function walk_folder in blame.py at line 55
print_file(os.path.join(os.getcwd(), filename), path)

function print_file in blame.py at line 34
users = get_blame(filename)

function get_blame in blame.py at line 20
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE)

function __init__ in subprocess.py at line 533
(p2cread, p2cwrite,

function _get_handles in subprocess.py at line 830
p2cread = stdin.fileno()

Этот код должен работать с описаниями документов Python это использование .

Ответы [ 5 ]

10 голосов
/ 22 апреля 2009

Три вещи

Во-первых, ваши () ошибаются.

Во-вторых, результатом subprocess.Popen() является объект процесса, а не файл.

proc = []
proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE))
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE)

Значение proc[-1] - это не файл, а процесс, который содержит файл.

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE))

В-третьих, не делайте все это tr и cut мусор в оболочке, некоторые вещи могут быть медленнее. Напишите обработку tr и cut в Python - это быстрее и проще.

3 голосов
/ 22 апреля 2009

В сценарии есть несколько странных вещей,

  • Почему вы храните каждый процесс в списке? Разве не было бы намного более удобочитаемым просто использовать переменные? Удаление всех .append()s выявляет синтаксическую ошибку, несколько раз вы передавали stdout = PIPE в аргументы append вместо Popen:

    proc.append(Popen(...), stdout=PIPE)
    

    Таким образом, прямая перезапись (все еще с ошибками, о которых я упомяну через секунду) станет ..

    def get_blame(filename): 
        blame = Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE)
        tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame, stdout=PIPE)
        tr2 = Popen(['tr', r"'\040'", r"';'"], stdin=tr1), stdout=PIPE)
        cut = Popen(['cut', r"-d", r"\;", '-f', '3'], stdin=tr2, stdout=PIPE)
        return cut.stdout.read()
    
  • В каждой последующей команде вы передаете объект Popen, не , который обрабатывает stdout. Из раздела «Замена конвейера оболочки» в документации по подпроцессам вы делаете ..

    p1 = Popen(["dmesg"], stdout=PIPE)
    p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
    

    .. тогда как вы делали эквивалент stdin=p1.

    Строка tr1 = (в приведенном выше переписанном коде) станет такой:

    tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame.stdout, stdout=PIPE)
    
  • Вам не нужно экранировать команды / аргументы с подпроцессом, поскольку подпроцесс не запускает команду в какой-либо оболочке (если вы не укажете shell=True). См. Раздел Security документации по подпроцессам.

    Вместо ..

    proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE))
    

    .. можете смело делать ..

    Popen(['svn', 'blame', filename], stdout=PIPE)
    
  • Как предложил С.Лотт, не используйте подпроцесс для упрощения работы с текстом в Python (команды tr / cut). Во-первых, tr / cut и т. Д. Не очень переносимы (разные версии имеют разные аргументы), а также их довольно сложно прочитать (я понятия не имею, что делают tr и cut)

    Если бы я переписал команду, я, вероятно, сделал бы что-то вроде ..

    def get_blame(filename): 
        blame = Popen(['svn', 'blame', filename], stdout=PIPE)
        output = blame.communicate()[0] # preferred to blame.stdout.read()
        # process commands output:
        ret = []
        for line in output.split("\n"):
            split_line = line.strip().split(" ")
            if len(split_line) > 2:
                rev = split_line[0]
                author = split_line[1]
                line = " ".join(split_line[2:])
    
                ret.append({'rev':rev, 'author':author, 'line':line})
    
        return ret
    
1 голос
/ 22 апреля 2009

Вы хотите стандартный вывод процесса, поэтому замените ваш stdin=proc[-1] на stdin=proc[-1].stdout

Кроме того, вам нужно переместить парен, он должен следовать после аргумента stdout.

 proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE)

должно быть:

 proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE))

Исправьте остальные append вызовы таким же образом.

0 голосов
/ 22 апреля 2009

Как сказал С.Лотт, обработка текста на Python лучше.

Но если вы хотите использовать утилиты cmdline, вы можете сохранить их для чтения, используя shell=True:

cmdline = r"svn blame %s | tr -s '\040' | tr '\040' ';' | cut -d \; -f 3" % shellquote(filename)
return Popen(cmdline, shell=True, stdout=PIPE).communicate()[0]
0 голосов
/ 22 апреля 2009

выглядит как синтаксическая ошибка. кроме первого добавления, остальные ошибочны (обзор скобок).

...