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.

Où bien placer une exception ?

0 votes

Extrait du code d'origine (basé sur execo, une lib d'administration système) :

import execo

def executeZFS(zfscmd, host=None, shell=False, nolog=False):
"""
    execute zfs command, return stdout or None
    if host, use SshProcess (remote)
    if shell, use shell=True
    if nolog, use nolog_exit_code=True (don't CRITICAL)
    process zfs error 'dataset does not exist'
"""
if host not in [None, 'localhost']:
    if nolog:
        with execo.process.SshProcess(zfscmd, host, nolog_exit_code=True).run() as process:
            pass
    else:
        with execo.process.SshProcess(zfscmd, host).run() as process:
            pass

elif shell:
    if nolog:
        with execo.process.Process(zfscmd, shell=True, nolog_exit_code=True).run() as process:
            pass
    else:
        with execo.process.Process(zfscmd, shell=True).run() as process:
            pass

elif nolog:
    with execo.process.Process(zfscmd, nolog_exit_code=True).run() as process:
        pass

else:
    with execo.process.Process(zfscmd).run() as process:
        pass

if process.exit_code != 0:  # or except ?
    # NOTE: every zfs error code is 256.
    # best to search "dataset does not exist" in stdout
    if process.stdout.count('dataset does not exist') == 1 :
        execo.log.logger.debug('This is expected: dataset does not exist')
        return None
    else:  # something is wrong, bubble it
        execo.log.logger.critical('process:\n' + str(process))
        execo.log.logger.warning('process stdout:\n' + process.stdout)
        execo.log.logger.warning('process stderr:\n' + process.stderr)

else:  # everything is OK, return stdout
    execo.log.logger.debug(type(process.stdout))
    return process.stdout

que je peux appeller comme ceci, pour le cas de figure 'récurrent' :

commande = "zfs list -H -o name -t snapshot -r data"
result = executeZFS(commande, host='root@serveur', nolog=True)

Ma question: je ne sais pas vraiment où placer le try/except. Dans cette fonction ? À quel étage ? Dans la fonction appelante de celle-ci ? Ais-je plutôt intérêt à me créer une Exception perso ?

(execo contient un wrapper autour de logging)

demandé 22-Jan par GruiicK (178 points)
edité 23-Jan par GruiicK

2 Réponses

+1 vote
 
Meilleure réponse

En me basant sur ta réponse, j'ai refactorisé la fonction pour que l'ajout du try...except soit plus facile et lisible.

import sys
import execo

def executeZFS(zfscmd, host=None, shell=False, nolog=False):
    """
        execute zfs command, return stdout as a string or None
        if host, use SshProcess (remote)
        if shell, use shell=True (accept '|' in zfscmd)
        if nolog, use nolog_exit_code=True (don't go CRITICAL rightaway)
            process zfs expected error 'dataset does not exist'
    """

    use_ssh = host not in [None, 'localhost']
    args = [zfscmd, host] if use_ssh else [zfscmd]
    kwargs = {'nolog_exit_code': nolog, 'shell': shell}
    func = execo.process.SshProcess if use_ssh else execo.process.Process

    process = func(*args, **kwargs).run()

    if process.exit_code == 0:  # everything is OK, return stdout
        execo.log.logger.debug(type(process.stdout))
        return process.stdout

    # only if nolog_exit_code=True, else backfire already occured
    # NOTE: every zfs error code is 256.
    # best to search "dataset does not exist" in stdout
    if process.stdout.count('dataset does not exist') == 1 :  # or > 0 ?
        execo.log.logger.debug('This is expected: dataset does not exist')
        return None

    # something went wrong, explode nicely
    execo.log.logger.critical('process:\n%s', process)
    execo.log.logger.warning('process stdout:\n%s', process.stdout)
    execo.log.logger.warning('process stderr:\n%s', process.stderr)
    sys.exit(1)

Ensuite, tu peux placer la gestion des exceptions autour de l'appel à process = func(*args, **kwargs).run() :

def executeZFS(zfscmd, host=None, shell=False, nolog=False):
    """
        execute zfs command, return stdout as a string or None
        if host, use SshProcess (remote)
        if shell, use shell=True (accept '|' in zfscmd)
        if nolog, use nolog_exit_code=True (don't go CRITICAL rightaway)
            process zfs expected error 'dataset does not exist'
    """

    use_ssh = host not in [None, 'localhost']
    args = [zfscmd, host] if use_ssh else [zfscmd]
    kwargs = {'nolog_exit_code': nolog, 'shell': shell}
    func = execo.process.SshProcess if use_ssh else execo.process.Process

    try:
        process = func(*args, **kwargs).run()
    except execo.exception.ProcessesFailed:
        execo.log.logger.exception('Process error')
        sys.exit(1)

    if process.exit_code == 0:  # everything is OK, return stdout
        execo.log.logger.debug(type(process.stdout))
        return process.stdout

    # NOTE: every zfs error code is 256.
    # best to search "dataset does not exist" in stdout
    if process.stdout.count('dataset does not exist'):
        execo.log.logger.debug('This is expected: dataset does not exist')
        return None

    # something went wrong, explode nicely
    execo.log.logger.critical('process:\n%s', process)
    execo.log.logger.warning('process stdout:\n%s', process.stdout)
    execo.log.logger.warning('process stderr:\n%s', process.stderr)
    sys.exit(1)

Je n'ai pas testé le code, mais ceci dit :)

répondu 16-Fev par Tiger-222 (1,056 points)
edité 18-Fev par Tiger-222

Effectivement, ta refactorisation est beaucoup plus lisible et élégante. Merci.

Après tests, le try...except de la seconde partie de ta réponse empêche la
capture du code erreur zfs.

Autant pour moi, c'est use_ssh = host not in [None, 'localhost'] (ajout du not). Et là mes tests passent avec la gestion de l'exception. C'est bÔ.

Ah oui, j'ai mal retranscrit la condition :o Je corrige ^^

0 votes

Après un café avec les dev d'execo, y'a plus simple (extrait) :

import execo
import sys

def executeZFS(zfscmd, host=None, shell=False, nolog=False):
"""
    execute zfs command, return stdout as a string or None
    if host, use SshProcess (remote)
    if shell, use shell=True (accept '|' in zfscmd)
    if nolog, use nolog_exit_code=True (don't go CRITICAL rightaway)
        process zfs expected error 'dataset does not exist'
"""

if host not in [None, 'localhost']:
    if nolog:
        process = execo.process.SshProcess(zfscmd, host, nolog_exit_code=True).run()
    else:
        process = execo.process.SshProcess(zfscmd, host).run()

elif shell:
    process = execo.process.Process(zfscmd, shell=True).run()

elif nolog:
    process =  execo.process.Process(zfscmd, nolog_exit_code=True).run()

else:
    process =  execo.process.Process(zfscmd).run()

# only if nolog_exit_code=True, else backfire already occured
if process.exit_code != 0:
    # NOTE: every zfs error code is 256.
    # best to search "dataset does not exist" in stdout
    if process.stdout.count('dataset does not exist') == 1 :  # or > 0 ?
        execo.log.logger.debug('This is expected: dataset does not exist')
        return None
    else:  # something went wrong, explode nicely
        execo.log.logger.critical('process:\n' + str(process))
        execo.log.logger.warning('process stdout:\n' + process.stdout)
        execo.log.logger.warning('process stderr:\n' + process.stderr)
        sys.exit(1)

else:  # everything is OK, return stdout
    execo.log.logger.debug(type(process.stdout))
    return process.stdout

Ce code n'ayant pas vocation à devenir une lib, ou à être appellé par ailleurs, un exit bien senti est sans doute beaucoup plus simple.
Bien sûr, je suis preneur de tout avis ou critique :o)

répondu 25-Jan par GruiicK (178 points)
...