From 9733ddbae069f30c6be5eb3c93999d0e18d9ba16 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Fri, 13 Jan 2023 14:27:45 +0100
Subject: [PATCH 01/16] ENH: add property elapsed time

---
 pyotb/apps.py | 8 ++++++--
 pyotb/core.py | 6 +++++-
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/pyotb/apps.py b/pyotb/apps.py
index a2af7ed..b63da8a 100644
--- a/pyotb/apps.py
+++ b/pyotb/apps.py
@@ -29,7 +29,7 @@ def get_available_applications(as_subprocess: bool = False) -> list[str]:
         env = os.environ.copy()
         if "PYTHONPATH" not in env:
             env["PYTHONPATH"] = ""
-        env["PYTHONPATH"] = ":" + str(Path(otb.__file__).parent)
+        env["PYTHONPATH"] += ":" + str(Path(otb.__file__).parent)
         env["OTB_LOGGER_LEVEL"] = "CRITICAL"  # in order to suppress warnings while listing applications
         pycmd = "import otbApplication; print(otbApplication.Registry.GetAvailableApplications())"
         cmd_args = [sys.executable, "-c", pycmd]
@@ -49,7 +49,7 @@ def get_available_applications(as_subprocess: bool = False) -> list[str]:
         except (ValueError, SyntaxError, AssertionError):
             logger.debug("Failed to decode output or convert to tuple:\nstdout=%s\nstderr=%s", stdout, stderr)
         if not app_list:
-            logger.info("Failed to list applications in an independent process. Falling back to local python import")
+            logger.debug("Failed to list applications in an independent process. Falling back to local python import")
     # Find applications using the normal way
     if not app_list:
         app_list = otb.Registry.GetAvailableApplications()
@@ -68,6 +68,10 @@ class App(OTBObject):
         super().__init__(*args, **kwargs)
         self.description = self.app.GetDocLongDescription()
 
+    def elapsed_time(self):
+        """Get elapsed time between app init and end of exec or file writing."""
+        return self.time_end - self.time_start
+
     @property
     def used_outputs(self) -> list[str]:
         """List of used application outputs."""
diff --git a/pyotb/core.py b/pyotb/core.py
index 88496b6..c3649b5 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -4,6 +4,7 @@ from __future__ import annotations
 from typing import Any
 from pathlib import Path
 from ast import literal_eval
+from time import perf_counter
 
 import numpy as np
 import otbApplication as otb
@@ -44,7 +45,7 @@ class OTBObject:
         create = otb.Registry.CreateApplicationWithoutLogger if quiet else otb.Registry.CreateApplication
         self.app = create(name)
         self.parameters_keys = tuple(self.app.GetParametersKeys())
-        # Output parameters types
+        self.time_start, self.time_end = 0, 0
         self.all_param_types = {k: self.app.GetParameterType(k) for k in self.parameters_keys}
         self.out_param_types = {k: v for k, v in self.all_param_types.items()
                                 if v in (otb.ParameterType_OutputImage,
@@ -191,11 +192,13 @@ class OTBObject:
     def execute(self):
         """Execute and write to disk if any output parameter has been set during init."""
         logger.debug("%s: run execute() with parameters=%s", self.name, self.parameters)
+        self.time_start = perf_counter()
         try:
             self.app.Execute()
         except (RuntimeError, FileNotFoundError) as e:
             raise Exception(f"{self.name}: error during during app execution") from e
         self.frozen = False
+        self.time_end = perf_counter()
         logger.debug("%s: execution ended", self.name)
         self.save_objects()  # this is required for apps like ReadImageInfo or ComputeImagesStatistics
 
@@ -207,6 +210,7 @@ class OTBObject:
         except RuntimeError:
             logger.debug("%s: failed with WriteOutput, executing once again with ExecuteAndWriteOutput", self.name)
             self.app.ExecuteAndWriteOutput()
+        self.time_end = perf_counter()
 
     def write(self, *args, filename_extension: str = "", pixel_type: dict[str, str] | str = None,
               preserve_dtype: bool = False, **kwargs):
-- 
GitLab


From a56158153fac1865a3e6d0c40a0fd9e040952331 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Fri, 13 Jan 2023 14:27:55 +0100
Subject: [PATCH 02/16] FIX: import error help message

---
 pyotb/helpers.py | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/pyotb/helpers.py b/pyotb/helpers.py
index d38dfb3..ebccc48 100644
--- a/pyotb/helpers.py
+++ b/pyotb/helpers.py
@@ -294,16 +294,12 @@ def __suggest_fix_import(error_message: str, prefix: str):
                     logger.critical("You may need to install cmake in order to recompile python bindings")
             else:
                 logger.critical("Unable to automatically locate python dynamic library of %s", sys.executable)
-            return
     elif sys.platform == "win32":
         if error_message.startswith("DLL load failed"):
             if sys.version_info.minor != 7:
                 logger.critical("You need Python 3.5 (OTB releases 6.4 to 7.4) or Python 3.7 (since OTB 8)")
-                issue_link = "https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/-/issues/2010"
-                logger.critical("Another workaround is to recompile Python bindings with cmake, see %s", issue_link)
             else:
                 logger.critical("It seems that your env variables aren't properly set,"
                                 " first use 'call otbenv.bat' then try to import pyotb once again")
-            return
     docs_link = "https://www.orfeo-toolbox.org/CookBook/Installation.html"
     logger.critical("You can verify installation requirements for your OS at %s", docs_link)
-- 
GitLab


From 06c983d4be574cb6d0c6f0ffe65a94cb059d90d7 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Fri, 13 Jan 2023 14:28:37 +0100
Subject: [PATCH 03/16] ENH: add properties data and metadata

---
 pyotb/core.py | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/pyotb/core.py b/pyotb/core.py
index c3649b5..f2fe4a8 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -85,6 +85,22 @@ class OTBObject:
         """Get the name of first output image parameter."""
         return self.get_first_key(param_types=[otb.ParameterType_OutputImage])
 
+    @property
+    def data(self):
+        """Expose app's output data values in a dictionary."""
+        skip_keys = tuple(self.out_param_types) + tuple(self.parameters) + ("ram", "elev.default")
+        keys = (k for k in self.parameters_keys if k not in skip_keys)
+        def _check(v):
+            return not isinstance(v, otb.ApplicationProxy) and v not in ("", None, [], ())
+        return {str(k): self[k] for k in keys if _check(self[k])}
+
+    @property
+    def metadata(self):
+        if not self.key_output_image:
+            raise TypeError(f"{self.name}: this application has no raster output")
+        return dict(self.app.GetMetadataDictionary(self.key_output_image))
+
+
     @property
     def dtype(self) -> np.dtype:
         """Expose the pixel type of output image using numpy convention.
@@ -186,6 +202,11 @@ class OTBObject:
             # Convert output param path to Output object
             if key in self.out_param_types:
                 value = Output(self, key, value)
+            elif isinstance(key, str):
+                try:
+                    value = literal_eval(value)
+                except (ValueError, SyntaxError):
+                    pass
             # Save attribute
             setattr(self, key, value)
 
-- 
GitLab


From f066af5d22464f98f68930538aa5074de9f7eae6 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Fri, 13 Jan 2023 14:28:54 +0100
Subject: [PATCH 04/16] ENH: add functions get_infos and get_statistics

---
 pyotb/core.py | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/pyotb/core.py b/pyotb/core.py
index f2fe4a8..dc02141 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -322,6 +322,16 @@ class OTBObject:
         for key in keys:
             self.app.SetParameterOutputImagePixelType(key, dtype)
 
+    def get_infos(self):
+        if not self.key_output_image:
+            raise TypeError(f"{self.name}: this application has no raster output")
+        return OTBObject("ReadImageInfo", self, quiet=True).data
+
+    def get_statistics(self):
+        if not self.key_output_image:
+            raise TypeError(f"{self.name}: this application has no raster output")
+        return OTBObject("ComputeImagesStatistics", self, quiet=True).data
+
     def read_values_at_coords(self, row: int, col: int, bands: int = None) -> list[int | float] | int | float:
         """Get pixel value(s) at a given YX coordinates.
 
-- 
GitLab


From 408d20d7acf0c54b84566a62db24426cba410c98 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Fri, 13 Jan 2023 14:44:31 +0100
Subject: [PATCH 05/16] ENH: add tests for new features + reorganize

---
 tests/test_core.py  | 58 ++++++++++++++++++++++++++++++---------------
 tests/test_numpy.py |  4 ++++
 2 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/tests/test_core.py b/tests/test_core.py
index 2eadef7..436f99e 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -8,6 +8,12 @@ import pytest
 
 FILEPATH = os.environ["TEST_INPUT_IMAGE"]
 INPUT = pyotb.Input(FILEPATH)
+TEST_IMAGE_STATS = {
+    'out.mean': [79.5505, 109.225, 115.456, 249.349],
+    'out.min': [33, 64, 91, 47],
+    'out.max': [255, 255, 230, 255],
+    'out.std': [51.0754, 35.3152, 23.4514, 20.3827]
+}
 
 
 # Input settings
@@ -20,7 +26,7 @@ def test_wrong_key():
         pyotb.BandMath(INPUT, expression="im1b1")
 
 
-# OTBObject's properties
+# OTBObject properties
 def test_key_input():
     assert INPUT.key_input == INPUT.key_input_image == "in"
 
@@ -41,10 +47,39 @@ def test_transform():
     assert INPUT.transform == (6.0, 0.0, 760056.0, 0.0, -6.0, 6946092.0)
 
 
+def test_data():
+    assert pyotb.ComputeImagesStatistics(INPUT).data == TEST_IMAGE_STATS
+
+
+def test_metadata():
+    assert INPUT.metadata["Metadata_1"] == "TIFFTAG_SOFTWARE=CSinG - 13 SEPTEMBRE 2012"
+
+
 def test_nonraster_property():
     with pytest.raises(TypeError):
         pyotb.ReadImageInfo(INPUT).dtype
 
+def test_elapsed_time():
+    assert pyotb.ReadImageInfo(INPUT).elapsed_time < 1
+
+# Other functions
+def test_get_infos():
+    infos = INPUT.get_infos()
+    assert (infos["sizex"], infos["sizey"]) == (251, 304)
+
+
+def test_get_statistics():
+    assert INPUT.get_statistics() == TEST_IMAGE_STATS
+
+
+def test_xy_to_rowcol():
+    assert INPUT.xy_to_rowcol(760101, 6945977) == (19, 7)
+
+
+def test_write():
+    INPUT.write("/tmp/missing_dir/test_write.tif")
+    assert INPUT.out.exists()
+
 
 # Slicer
 def test_slicer_shape():
@@ -85,7 +120,7 @@ def test_binary_mask_where():
     assert res.exp == "(((((im1b1 == 1) || (im1b1 == 2)) || (im1b1 == 3)) || (im1b1 == 4)) ? 255 : 0)"
 
 
-# Apps
+# Essential apps
 def test_app_readimageinfo():
     info = pyotb.ReadImageInfo(INPUT, quiet=True)
     assert (info.sizex, info.sizey) == (251, 304)
@@ -94,12 +129,12 @@ def test_app_readimageinfo():
 
 def test_app_computeimagestats():
     stats = pyotb.ComputeImagesStatistics([INPUT], quiet=True)
-    assert stats["out.min"] == "[33, 64, 91, 47]"
+    assert stats["out.min"] == TEST_IMAGE_STATS["out.min"]
 
 
 def test_app_computeimagestats_sliced():
     slicer_stats = pyotb.ComputeImagesStatistics(il=[INPUT[:10, :10, 0]], quiet=True)
-    assert slicer_stats["out.min"] == "[180]"
+    assert slicer_stats["out.min"] == [180]
 
 
 def test_read_values_at_coords():
@@ -107,21 +142,6 @@ def test_read_values_at_coords():
     assert INPUT[10, 20, :] == [207, 192, 172, 255]
 
 
-# XY => RowCol
-def test_xy_to_rowcol():
-    assert INPUT.xy_to_rowcol(760101, 6945977) == (19, 7)
-
-
-def test_pixel_coords_numpy_equals_otb():
-    assert INPUT[19,7] == list(INPUT.to_numpy()[19,7])
-
-
-# Create dir before write
-def test_write():
-    INPUT.write("/tmp/missing_dir/test_write.tif")
-    assert INPUT.out.filepath.exists()
-
-
 # BandMath NDVI == RadiometricIndices NDVI ?
 def test_ndvi_comparison():
     ndvi_bandmath = (INPUT[:, :, -1] - INPUT[:, :, [0]]) / (INPUT[:, :, -1] + INPUT[:, :, 0])
diff --git a/tests/test_numpy.py b/tests/test_numpy.py
index 4240cb9..dd33425 100644
--- a/tests/test_numpy.py
+++ b/tests/test_numpy.py
@@ -36,6 +36,10 @@ def test_convert_to_array():
     assert INPUT.shape == array.shape
 
 
+def test_pixel_coords_otb_equals_numpy():
+    assert INPUT[19,7] == list(INPUT.to_numpy()[19,7])
+
+
 def test_add_noise_array():
     white_noise = np.random.normal(0, 50, size=INPUT.shape)
     noisy_image = INPUT + white_noise
-- 
GitLab


From 8390bd43aa8387ef28cdd8bda9fc0726d39b56c3 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Fri, 13 Jan 2023 14:49:23 +0100
Subject: [PATCH 06/16] FIX: missing property decorator

---
 pyotb/apps.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pyotb/apps.py b/pyotb/apps.py
index b63da8a..5092fee 100644
--- a/pyotb/apps.py
+++ b/pyotb/apps.py
@@ -68,6 +68,7 @@ class App(OTBObject):
         super().__init__(*args, **kwargs)
         self.description = self.app.GetDocLongDescription()
 
+    @property
     def elapsed_time(self):
         """Get elapsed time between app init and end of exec or file writing."""
         return self.time_end - self.time_start
-- 
GitLab


From 0b6d767a4e3ccb02de3294acc990df1cd9f25d8f Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Fri, 13 Jan 2023 15:02:40 +0100
Subject: [PATCH 07/16] STYLE: linting and docstrings

---
 pyotb/core.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/pyotb/core.py b/pyotb/core.py
index dc02141..d4f32c0 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -90,17 +90,18 @@ class OTBObject:
         """Expose app's output data values in a dictionary."""
         skip_keys = tuple(self.out_param_types) + tuple(self.parameters) + ("ram", "elev.default")
         keys = (k for k in self.parameters_keys if k not in skip_keys)
+
         def _check(v):
             return not isinstance(v, otb.ApplicationProxy) and v not in ("", None, [], ())
         return {str(k): self[k] for k in keys if _check(self[k])}
 
     @property
     def metadata(self):
+        """Return first output image metadata dictionary"""
         if not self.key_output_image:
             raise TypeError(f"{self.name}: this application has no raster output")
         return dict(self.app.GetMetadataDictionary(self.key_output_image))
 
-
     @property
     def dtype(self) -> np.dtype:
         """Expose the pixel type of output image using numpy convention.
@@ -323,11 +324,13 @@ class OTBObject:
             self.app.SetParameterOutputImagePixelType(key, dtype)
 
     def get_infos(self):
+        """Return a dict output of ReadImageInfo for the first image output"""
         if not self.key_output_image:
             raise TypeError(f"{self.name}: this application has no raster output")
         return OTBObject("ReadImageInfo", self, quiet=True).data
 
     def get_statistics(self):
+        """Return a dict output of ComputeImagesStatistics for the first image output"""
         if not self.key_output_image:
             raise TypeError(f"{self.name}: this application has no raster output")
         return OTBObject("ComputeImagesStatistics", self, quiet=True).data
-- 
GitLab


From 5cfa66f08e6949b412fdd1981d95d1790017f4bd Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Fri, 13 Jan 2023 15:11:23 +0100
Subject: [PATCH 08/16] STYLE: docstrings

---
 pyotb/core.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pyotb/core.py b/pyotb/core.py
index d4f32c0..6186ef2 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -97,7 +97,7 @@ class OTBObject:
 
     @property
     def metadata(self):
-        """Return first output image metadata dictionary"""
+        """Return first output image metadata dictionary."""
         if not self.key_output_image:
             raise TypeError(f"{self.name}: this application has no raster output")
         return dict(self.app.GetMetadataDictionary(self.key_output_image))
@@ -324,13 +324,13 @@ class OTBObject:
             self.app.SetParameterOutputImagePixelType(key, dtype)
 
     def get_infos(self):
-        """Return a dict output of ReadImageInfo for the first image output"""
+        """Return a dict output of ReadImageInfo for the first image output."""
         if not self.key_output_image:
             raise TypeError(f"{self.name}: this application has no raster output")
         return OTBObject("ReadImageInfo", self, quiet=True).data
 
     def get_statistics(self):
-        """Return a dict output of ComputeImagesStatistics for the first image output"""
+        """Return a dict output of ComputeImagesStatistics for the first image output."""
         if not self.key_output_image:
             raise TypeError(f"{self.name}: this application has no raster output")
         return OTBObject("ComputeImagesStatistics", self, quiet=True).data
-- 
GitLab


From 359a813260b87ae8b2998f4bb43384f74c89634f Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Mon, 16 Jan 2023 13:36:48 +0100
Subject: [PATCH 09/16] FIX: check value is str before literal_eval

---
 pyotb/core.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyotb/core.py b/pyotb/core.py
index 6186ef2..0a8a91c 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -203,7 +203,7 @@ class OTBObject:
             # Convert output param path to Output object
             if key in self.out_param_types:
                 value = Output(self, key, value)
-            elif isinstance(key, str):
+            elif isinstance(value, str):
                 try:
                     value = literal_eval(value)
                 except (ValueError, SyntaxError):
-- 
GitLab


From 844465de41d79fcba15e87ce4884038f9155628e Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Mon, 16 Jan 2023 13:56:31 +0100
Subject: [PATCH 10/16] ENH: avoid some keys in data property

---
 pyotb/core.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pyotb/core.py b/pyotb/core.py
index 0a8a91c..ddaa62b 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -88,7 +88,8 @@ class OTBObject:
     @property
     def data(self):
         """Expose app's output data values in a dictionary."""
-        skip_keys = tuple(self.out_param_types) + tuple(self.parameters) + ("ram", "elev.default")
+        skip_keys = ("ram", "elev.default", "mapproj.utm.zone", "mapproj.utm.northhem")
+        skip_keys = skip_keys + tuple(self.out_param_types) + tuple(self.parameters)
         keys = (k for k in self.parameters_keys if k not in skip_keys)
 
         def _check(v):
-- 
GitLab


From 41d372d4ebfc8f50369725a2f2b64024c931b0db Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Tue, 24 Jan 2023 11:15:04 +0100
Subject: [PATCH 11/16] CI: add missing pylint exception

---
 pyproject.toml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pyproject.toml b/pyproject.toml
index 631be88..2ef1c71 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -54,6 +54,7 @@ disable = [
     "too-many-instance-attributes",
     "too-many-arguments",
     "too-many-return-statements",
+    "too-many-public-methods",
     "too-many-lines",
     "too-many-branches",
 ]
-- 
GitLab


From bdde3adc1d26f6eea00a8c37649df52acb7c42e8 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Tue, 24 Jan 2023 14:37:14 +0100
Subject: [PATCH 12/16] FIX: Output object must inherit from OTBObject

---
 pyotb/core.py | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/pyotb/core.py b/pyotb/core.py
index ddaa62b..df585a8 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -556,11 +556,9 @@ class OTBObject:
             AttributeError: when `name` is not an attribute of self.app
 
         """
-        try:
-            res = getattr(self.app, name)
-            return res
-        except AttributeError as e:
-            raise AttributeError(f"{self.name}: could not find attribute `{name}`") from e
+        if name in dir(self.app):
+            return getattr(self.app, name)
+        raise AttributeError(f"{self.name}: could not find attribute `{name}`")
 
     def __getitem__(self, key):
         """Override the default __getitem__ behaviour.
@@ -1260,20 +1258,21 @@ class Input(OTBObject):
         return f"<pyotb.Input object from {self.path}>"
 
 
-class Output:
+class Output(OTBObject):
     """Object that behave like a pointer to a specific application output file."""
 
-    def __init__(self, source_app: OTBObject, param_key: str, filepath: str = None, mkdir: bool = True):
+    def __init__(self, pyotb_app: OTBObject, param_key: str, filepath: str = None, mkdir: bool = True):  # pylint: disable=super-init-not-called
         """Constructor for an Output object.
 
         Args:
-            source_app: The pyotb App to store reference from
+            pyotb_app: The pyotb App to store reference from
             param_key: Output parameter key of the target app
             filepath: path of the output file (if not in memory)
             mkdir: create missing parent directories
 
         """
-        self.source_app = source_app
+        self.pyotb_app, self.app = pyotb_app, pyotb_app.app
+        self.parameters = pyotb_app.parameters
         self.param_key = param_key
         self.filepath = None
         if filepath:
@@ -1282,7 +1281,12 @@ class Output:
             self.filepath = Path(filepath)
             if mkdir:
                 self.make_parent_dirs()
-        self.name = f"Output {param_key} from {self.source_app.name}"
+        self.name = f"Output {param_key} from {self.pyotb_app.name}"
+
+    @property
+    def key_output_image(self):
+        """Overwrite OTBObject prop, in order to use Operation special methods with the right Output param_key."""
+        return self.param_key
 
     def exists(self) -> bool:
         """Check file exist."""
-- 
GitLab


From d0922315221b8449d7b764f5572aeaa884ae2679 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Tue, 24 Jan 2023 14:45:55 +0100
Subject: [PATCH 13/16] STYLE: max line length

---
 pyotb/core.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pyotb/core.py b/pyotb/core.py
index df585a8..acb09eb 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -1261,7 +1261,8 @@ class Input(OTBObject):
 class Output(OTBObject):
     """Object that behave like a pointer to a specific application output file."""
 
-    def __init__(self, pyotb_app: OTBObject, param_key: str, filepath: str = None, mkdir: bool = True):  # pylint: disable=super-init-not-called
+    def __init__(self, pyotb_app: OTBObject, param_key: str,
+                 filepath: str = None, mkdir: bool = True):  # pylint: disable=super-init-not-called
         """Constructor for an Output object.
 
         Args:
-- 
GitLab


From 6c67f2fa091c97c11ae3dd577ac4f59f2ea3c154 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Tue, 24 Jan 2023 14:47:00 +0100
Subject: [PATCH 14/16] STYLE: pylint ignore on wrong line

---
 pyotb/core.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pyotb/core.py b/pyotb/core.py
index acb09eb..c7ceb04 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -1261,13 +1261,13 @@ class Input(OTBObject):
 class Output(OTBObject):
     """Object that behave like a pointer to a specific application output file."""
 
-    def __init__(self, pyotb_app: OTBObject, param_key: str,
-                 filepath: str = None, mkdir: bool = True):  # pylint: disable=super-init-not-called
+    def __init__(self, pyotb_app: OTBObject,  # pylint: disable=super-init-not-called
+                 param_key: str, filepath: str = None, mkdir: bool = True):
         """Constructor for an Output object.
 
         Args:
             pyotb_app: The pyotb App to store reference from
-            param_key: Output parameter key of the target app
+            param_key: Output parameter key of the target app 
             filepath: path of the output file (if not in memory)
             mkdir: create missing parent directories
 
-- 
GitLab


From 8ab686b2d2045d013d828f588b107f39d4da6550 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Tue, 24 Jan 2023 14:50:53 +0100
Subject: [PATCH 15/16] STYLE: whitespace

---
 pyotb/core.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyotb/core.py b/pyotb/core.py
index c7ceb04..69a0fd2 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -1267,7 +1267,7 @@ class Output(OTBObject):
 
         Args:
             pyotb_app: The pyotb App to store reference from
-            param_key: Output parameter key of the target app 
+            param_key: Output parameter key of the target app
             filepath: path of the output file (if not in memory)
             mkdir: create missing parent directories
 
-- 
GitLab


From c8d19a1a91ff7ca677952ceb6fd5d9912c43df45 Mon Sep 17 00:00:00 2001
From: Vincent Delbar <vincent.delbar@latelescop.fr>
Date: Tue, 24 Jan 2023 15:49:38 +0100
Subject: [PATCH 16/16] STYLE: remove useless pylint exceptions

---
 pyotb/apps.py      |  4 ++--
 pyotb/core.py      |  2 +-
 pyotb/functions.py |  6 +++---
 pyproject.toml     | 16 +++++-----------
 4 files changed, 11 insertions(+), 17 deletions(-)

diff --git a/pyotb/apps.py b/pyotb/apps.py
index 5092fee..ad71f54 100644
--- a/pyotb/apps.py
+++ b/pyotb/apps.py
@@ -3,9 +3,10 @@
 from __future__ import annotations
 import os
 import sys
+import subprocess
 from pathlib import Path
 
-import otbApplication as otb
+import otbApplication as otb  # pylint: disable=import-error
 from .core import OTBObject
 from .helpers import logger
 
@@ -34,7 +35,6 @@ def get_available_applications(as_subprocess: bool = False) -> list[str]:
         pycmd = "import otbApplication; print(otbApplication.Registry.GetAvailableApplications())"
         cmd_args = [sys.executable, "-c", pycmd]
         try:
-            import subprocess  # pylint: disable=import-outside-toplevel
             params = {"env": env, "stdout": subprocess.PIPE, "stderr": subprocess.PIPE}
             with subprocess.Popen(cmd_args, **params) as p:
                 logger.debug('Exec "%s \'%s\'"', ' '.join(cmd_args[:-1]), pycmd)
diff --git a/pyotb/core.py b/pyotb/core.py
index 69a0fd2..6f0a4dc 100644
--- a/pyotb/core.py
+++ b/pyotb/core.py
@@ -7,7 +7,7 @@ from ast import literal_eval
 from time import perf_counter
 
 import numpy as np
-import otbApplication as otb
+import otbApplication as otb  # pylint: disable=import-error
 
 from .helpers import logger
 
diff --git a/pyotb/functions.py b/pyotb/functions.py
index aa161e6..71c298b 100644
--- a/pyotb/functions.py
+++ b/pyotb/functions.py
@@ -4,8 +4,9 @@ from __future__ import annotations
 import inspect
 import os
 import sys
-import textwrap
 import uuid
+import textwrap
+import subprocess
 from collections import Counter
 
 from .core import OTBObject, Input, Operation, LogicalOperation, get_nbchannels, Output
@@ -217,7 +218,7 @@ def run_tf_function(func):
 
     """
     try:
-        from .apps import TensorflowModelServe
+        from .apps import TensorflowModelServe  # pylint: disable=import-outside-toplevel
     except ImportError:
         logger.error('Could not run Tensorflow function: failed to import TensorflowModelServe.'
                      'Check that you have OTBTF configured (https://github.com/remicres/otbtf#how-to-install)')
@@ -303,7 +304,6 @@ def run_tf_function(func):
         pycmd = get_tf_pycmd(out_savedmodel, channels, scalar_inputs)
         cmd_args = [sys.executable, "-c", pycmd]
         try:
-            import subprocess
             subprocess.run(cmd_args, env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
         except subprocess.SubprocessError:
             logger.debug("Failed to call subprocess")
diff --git a/pyproject.toml b/pyproject.toml
index 2ef1c71..9538fc1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -26,7 +26,7 @@ classifiers=[
 ]
 
 [project.optional-dependencies]
-dev = ["pytest",  "pylint", "codespell", "pydocstyle", "tomli"]
+dev = ["pytest", "pylint", "codespell", "pydocstyle", "tomli"]
 
 [project.urls]
 documentation = "https://pyotb.readthedocs.io"
@@ -43,20 +43,14 @@ version = {attr = "pyotb.__version__"}
 max-line-length = 120
 disable = [
     "fixme",
-    "import-error",
-    "import-outside-toplevel",
-    "wrong-import-position",
-    "wrong-import-order",
     "invalid-name",
-    "too-many-nested-blocks",
+    "too-many-lines",
     "too-many-locals",
+    "too-many-branches",
     "too-many-statements",
-    "too-many-instance-attributes",
-    "too-many-arguments",
-    "too-many-return-statements",
     "too-many-public-methods",
-    "too-many-lines",
-    "too-many-branches",
+    "too-many-instance-attributes",
+    "wrong-import-position",
 ]
 
 [tool.pydocstyle]
-- 
GitLab