diff --git a/.hooks/pre-commit.example b/.hooks/pre-commit.example new file mode 100755 index 00000000..f4f188d7 --- /dev/null +++ b/.hooks/pre-commit.example @@ -0,0 +1,23 @@ +#!/bin/bash -e + +function run_precommit_checks +{ + echo "running mypy" + python -m mypy --config-file ./.mypy.ini --check-untyped-defs $1 + echo "running ruff" + python -m ruff check --unsafe-fixes --config .ruff.toml $1 + echo "running yapf" + python -m yapf -r -i --style .style.yapf $1 + + return 0 +} + + +source ${ENV_PYTHON}/bin/activate + +#sphinx-build -b html docs/ docs/_build -W +for file in $(git diff --name-only --cached | grep -e "modified\|added" | grep py) +do + run_precommit_checks $file +done + diff --git a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py index 1810bc73..b7033537 100644 --- a/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py +++ b/geos-mesh/src/geos/mesh/processing/FillPartialArrays.py @@ -9,6 +9,7 @@ from geos.mesh.utils.arrayModifiers import fillPartialAttributes from geos.mesh.utils.arrayHelpers import getAttributePieceInfo +from geos.utils.details import addLogSupport from vtkmodules.vtkCommonDataModel import vtkMultiBlockDataSet __doc__ = """ @@ -51,13 +52,13 @@ loggerTitle: str = "Fill Partial Attribute" +@addLogSupport( loggerTitle=loggerTitle ) class FillPartialArrays: def __init__( self: Self, multiBlockDataSet: vtkMultiBlockDataSet, dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ], - speHandler: bool = False, ) -> None: """Fill partial attributes with constant value per component. @@ -69,35 +70,10 @@ def __init__( Args: multiBlockDataSet (vtkMultiBlockDataSet): The mesh where to fill the attribute. dictAttributesValues (dict[str, Any]): The dictionary with the attribute to fill as keys and the list of filling values as items. - speHandler (bool, optional): True to use a specific handler, False to use the internal handler. - Defaults to False. """ self.multiBlockDataSet: vtkMultiBlockDataSet = multiBlockDataSet self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = dictAttributesValues - # Logger. - self.logger: Logger - if not speHandler: - self.logger = getLogger( loggerTitle, True ) - else: - self.logger = logging.getLogger( loggerTitle ) - self.logger.setLevel( logging.INFO ) - - def setLoggerHandler( self: Self, handler: logging.Handler ) -> None: - """Set a specific handler for the filter logger. - - In this filter 4 log levels are use, .info, .error, .warning and .critical, be sure to have at least the same 4 levels. - - Args: - handler (logging.Handler): The handler to add. - """ - if not self.logger.hasHandlers(): - self.logger.addHandler( handler ) - else: - self.logger.warning( - "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." - ) - def applyFilter( self: Self ) -> bool: """Create a constant attribute per region in the mesh. diff --git a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py index 218d6bfc..b6b89272 100644 --- a/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py +++ b/geos-pv/src/geos/pv/plugins/PVFillPartialArrays.py @@ -9,10 +9,10 @@ from paraview.util.vtkAlgorithm import ( # type: ignore[import-not-found] VTKPythonAlgorithmBase, smdomain, smhint, smproperty, smproxy, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/util/vtkAlgorithm.py from paraview.detail.loghandler import ( # type: ignore[import-not-found] VTKHandler, -) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py +) # source: https://github.com/Kitware/ParaView/blob/master/Wrapping/Python/paraview/detail/loghandler.py from vtkmodules.vtkCommonDataModel import ( vtkMultiBlockDataSet, ) @@ -66,8 +66,7 @@ def __init__( self: Self, ) -> None: self.clearDictAttributesValues: bool = True self.dictAttributesValues: dict[ str, Union[ list[ Any ], None ] ] = {} - - @smproperty.xml(""" + @smproperty.xml( """ None: attributeName | fillingValueComponent1 fillingValueComponent2 ...\n To fill the attribute with the default value, live a blanc. The default value is:\n 0 for uint type, -1 for int type and nan for float type. - + @@ -99,13 +98,13 @@ def _setDictAttributesValues( self: Self, attributeName: str, values: str ) -> N if self.clearDictAttributesValues: self.dictAttributesValues = {} self.clearDictAttributesValues = False - + if attributeName is not None: - if values is not None : + if values is not None: self.dictAttributesValues[ attributeName ] = list( values.split( "," ) ) else: - self.dictAttributesValues[ attributeName ] = None - + self.dictAttributesValues[ attributeName ] = None # type: ignore[unreachable] + self.Modified() def RequestDataObject( @@ -155,14 +154,15 @@ def RequestData( outputMesh.ShallowCopy( inputMesh ) - filter: FillPartialArrays = FillPartialArrays( outputMesh, - self.dictAttributesValues, - True, + filter: FillPartialArrays = FillPartialArrays( + outputMesh, + self.dictAttributesValues, + speHandler=True, ) - + if not filter.logger.hasHandlers(): filter.setLoggerHandler( VTKHandler() ) - + filter.applyFilter() self.clearDictAttributesValues = True diff --git a/geos-utils/src/geos/utils/details.py b/geos-utils/src/geos/utils/details.py new file mode 100644 index 00000000..88bf7da0 --- /dev/null +++ b/geos-utils/src/geos/utils/details.py @@ -0,0 +1,66 @@ +import logging +from geos.utils.Logger import Logger, getLogger +from functools import wraps +from typing import Type, TypeVar, Callable, Protocol, Any + +__doc__ = """ + Group of decorators and Protocols that will be used in filters to wrap behaviors without code replication + +""" + + +class HasLogger( Protocol ): + """Protocol for classes that have logging support.""" + + logger: Logger + + def setLoggerHandler( self, handler: logging.Handler ) -> None: + """Set a specific handler for the filter logger. + + In this filter 4 log levels are use, .info, .error, .warning and .critical, be sure to have at least the same 4 levels. + + Args: + handler (logging.Handler): The handler to add. + """ + pass + + +T = TypeVar( 'T', bound='HasLogger' ) + + +def addLogSupport( loggerTitle: str ) -> Callable[ [ Type[ T ] ], Type[ T ] ]: + """Decorator to add logger support in the class following existing architecture. + + Args: + loggerTitle (str): Title to display in the logger + """ + + def decorator( cls: Type[ T ] ) -> Type[ T ]: + original_init = cls.__init__ + + @wraps( original_init ) + def new_init( self: T, *args: Any, **kwargs: Any ) -> None: + spe_handler = kwargs.pop( 'speHandler', False ) + if spe_handler: + self.logger = logging.getLogger( loggerTitle ) + self.logger.setLevel( logging.INFO ) + else: + self.logger = getLogger( loggerTitle, True ) + + original_init( self, *args, **kwargs ) + + def setLoggerHandler( self: T, handler: logging.Handler ) -> None: + # No docstring needed - Protocol defines the contract + if not self.logger.handlers: + self.logger.addHandler( handler ) + else: + self.logger.warning( + "The logger already has an handler, to use yours set the argument 'speHandler' to True during the filter initialization." + ) + + cls.__init__ = new_init # type: ignore[assignment] + cls.setLoggerHandler = setLoggerHandler # type: ignore[assignment] + + return cls + + return decorator