19 nov. 2022

[Dynamo =+ Python] Gestion des erreurs (context manager)

 




Une alternative au bloc try except

if this article is not in your language, use the Google Translate widget ⬈ (bottom of page for Mobile version )



Un gestionnaire de contexte en Python est généralement utilisé comme expression dans une instruction, le mot clé with permet de gérer une ressource en garantissant l'invocation d'une méthode d'acquisition et d'une autre de libération de cette ressource.

Un gestionnaire de contexte contient une méthode appelée au début et une méthode appelée à la fin.

La méthode __enter__ est appelée avant le début de l'instruction with.

La méthode __exit__ est appelée après la dernière instruction de la méthode with.

De plus, les gestionnaires de contexte sont très utiles en cas d'exception de notre programme


  • Voici un premier exemple, avec :

    • une gestion d'erreur
    • une écriture d'un log
    • un affichage du message d'erreur via une boite de dialogue 
Ici la boite de dialogue peut être très utile pour informer l'utilisateur si le script est lancé depuis Dynamo Player.


Code Python.

import sys
import clr
import System
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import MessageBox, MessageBoxButtons, MessageBoxIcon

my_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments)
pf_path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86)
sys.path.append(pf_path + '\\IronPython 2.7\\Lib')
import logging

## Start create logger Object ##
logger = logging.getLogger("MyLogger")
# set to  DEBUG
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(funcName)s :: %(message)s')
# create handler 
file_handler = logging.FileHandler(my_path + '\\MyLogger.log', mode='a')
file_handler.setLevel(logging.ERROR)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.disabled = False

class ValidationError(Exception):
	def __init__(self, typeError = "Error", messageError = "An error occurred"):
		self._typeError = typeError
		self._messageError = messageError
		Exception.__init__(self, self._typeError)
		self.CMessageBox()
		
	def __str__(self):
		return "{} : {}".format(self._typeError , self._messageError)
		
	def CMessageBox(self):
		MessageBox.Show(self._messageError, self._typeError, MessageBoxButtons.OK, MessageBoxIcon.Error)

class ErrorContextManager:
	def __init__(self):
		pass
		
	def __enter__(self):
		print("Context Successfully Started\n")
		return self
		
	def __exit__(self, exc_type, exc_value, exc_tb):
		if exc_type:
			exc_tbnext = exc_tb
			lst_lines_error = []
			for i in range(10):
				if exc_tbnext is not None:
					lst_lines_error.append(str(exc_tbnext.tb_lineno))
					exc_tbnext = exc_tbnext.tb_next
				else:
					break
			print(lst_lines_error)
			error = "Error : {} at lines : line : {}".format( exc_value, " -> line : ".join(lst_lines_error))
			logger.exception("{}\n{}".format(exc_type.__name__, error))
			raise ValidationError(exc_type.__name__, error)
		else:
			print("Successfully Exited From Context\n")
		return self
		
with ErrorContextManager() as sc:
	test1 = 10 / 2
	test2 = 10 / 2
	test3 = 4 / 1
	test1 = 10 / 2
	test2 = 10 / 3
	test3 = 4 / 1
	test1 = 10 / 2
	test2 = 10 / 1
	test3 = 4 / 1
	test1 = 10 / 2
	test2 = 10 / 1
	test3 = 4 / 0
	test1 = 10 / 2
	test2 = 10 / 1
	test3 = 4 / 1
	test2 = 10 / 2
	test3 = 4 / 2
	test1 = 10 / 2
	test2 = 10 / 3
	test3 = 4 / 9
	test1 = 10 / 2
	test2 = 10 / "2"
	test3 = 4 / 2
  
.

  • Un autre exemple avec Civil 3D CPython3/PythonNet (2.5.x)


Avec le mot clé with, lors d'un appel d'une méthode .Net, IronPython appelle la méthode Dispose() (ouClose()) automatiquement lors de la sortie du bloc with


À ce jour, ce n'est pas le cas pour Python.NET (2.5.x) avec le moteur CPython3, ce qui peut poser quelques soucis de débogage lors d'erreurs.

 En attentant une meilleure prise en charge du mot clé with sur les objets .Net IDisposable, voici une amélioration de la gestion d'erreurs avec un context manager.




 Code Python

import sys
import clr
# Add Assemblies for AutoCAD and Civil3D
clr.AddReference('AcMgd')
clr.AddReference('AcCoreMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

# Import references from Civil3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *

class ErrorContextManager:
    def __init__(self):
        self.typeError = None
        self.tracebackError = None
        
    def __enter__(self):
        return self
        
    def __exit__(self, exc_type, exc_value, exc_tb):
        if exc_type:
            error = "{} at line {}\n".format( exc_value, exc_tb.tb_lineno)
            self.typeError = exc_type.__name__
            self.tracebackError = error
        return self
        
    def CheckError(self):
        assert self.typeError is None, "{} : {}".format(self.typeError, self.tracebackError)
    
adoc = Application.DocumentManager.MdiActiveDocument
editor = adoc.Editor
tv = []

with adoc.LockDocument():
    with adoc.Database as db:
        with db.TransactionManager.StartTransaction() as t:
            with ErrorContextManager() as sc:
                bt = t.GetObject(db.BlockTableId, OpenMode.ForWrite)
                btr = t.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite)
                for oid in btr:
                    bl = t.GetObject(oid, OpenMode.ForRead)
                    if isinstance(bl, DBText):
                        tv.append(bl.TextString)
                    
                    
            t.Commit()
sc.CheckError()
OUT = tv
pour aller plus loin, vous pouvez utiliser la libraire standard contextlib

0 commentaires:

Enregistrer un commentaire