Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

subprocess différence de comportement entre python 2 et python 3

+2 votes

Je travaille actuellement sur le portage d'une application de python 2.7 à python 3.4.
Cependant, je rencontre actuellement un problème avec subprocess (du moins je pense que c'est la source) qui ne se comporte pas pareil en 2.7 et en 3.4.

Avant de passer au code problématique, je pense qu'il est important de vous donner un peu de contexte.
Il s'agit d'une application Flask dont le but est de fournir une interface web à un programme en ligne de commande.
Le programme en question dispose d'un mode "interactif" (une sorte de shell) duquel on peut envoyer des commandes sur stdin et lire le résultat sur stdout.

Mon application lance donc un subprocess en tâche de fond, et lui envoie des commandes sur stdin et lit les réponses sur stdout au besoin.

Le code qui nous intéresse est le suivant :

def _spawn_burp(self):
    """Launch the burp client process"""
    cmd = [self.burpbin, '-c', self.burpconfcli, '-a', 'm']
    self.proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, universal_newlines=False)
    # wait a little bit in case the process dies on a network timeout
    time.sleep(0.5)
    if not self._proc_is_alive():
        raise Exception('Unable to spawn burp process')
    _, w, _ = select([], [self.proc.stdin], [], 5)
    if self.proc.stdin not in w:
        self._logger('info', 'no no no no')
        return
    else:
        self._logger('info', 'yes yes yes yes')
    self.proc.stdin.write(b'j:pretty-print-off\n')
    js = self._read_proc_stdout()
    if self._is_warning(js):
        self._logger('info', js['warning'])

def _proc_is_alive(self):
    """Check if the burp client process is still alive"""
    if self.proc:
        return self.proc.poll() == None
    return False

def _read_proc_stdout(self):
    """reads the burp process stdout and returns a document or None"""
    doc = u''
    js = None
    while True:
        try:
            if not self._proc_is_alive():
                raise Exception('process died while reading its output')
            r, _, _ = select([self.proc.stdout], [], [], 5)
            if self.proc.stdout not in r:
                raise TimeoutError('NOP')
            else:
                self._logger('info', 'YES')
            doc += self.proc.stdout.readline().decode('UTF-8').rstrip('\n')
            self._logger('info', doc)
            js = self._is_valid_json(doc)
            # if the string is a valid json and looks like a logline, we
            # simply ignore it
            self._logger('info', js)
            if js and self._is_ignored(js):
                self._logger('info', 'ICI')
                doc = ''
                continue
            elif js:
                break
        except (TimeoutError, IOError, Exception) as e:
            # the os throws an exception if there is no data or timeout
            self._logger('warning', str(e))
            break
    return js

Avec python 2.7 je récupère bien différentes lignes alors qu'avec python 3.4, readline retourne la première ligne puis plus rien après.

Debug ici

demandé 11-Oct-2015 par ziirish (158 points)

1 Réponse

+3 votes
 
Meilleure réponse

Problème résolu, il suffisait d'ajouter l'option bufsize=0 à la commande subprocess.Popen.

Changed in version 3.3.1: bufsize now defaults to -1 to enable
buffering by default to match the behavior that most code expects. In
versions prior to Python 3.2.4 and 3.3.1 it incorrectly defaulted to 0
which was unbuffered and allowed short reads. This was unintentional
and did not match the behavior of Python 2 as most code expected.

source

répondu 11-Oct-2015 par ziirish (158 points)
sélectionné 11-Oct-2015 par ziirish

C'est bizarre, parce que la citation semble dire le contraire : le mode unbuffered ne correspond pas au comportement de python2.

J'avoue que la citation n'est pas très claire, mais la doc de subprocess pour python 2 indique bien que la valeur par défaut est 0.

...