interactive-pipe 0.5.8__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/PKG-INFO +35 -3
  2. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/pyproject.toml +38 -2
  3. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/readme.md +12 -2
  4. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/__init__.py +2 -2
  5. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/core/cache.py +4 -4
  6. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/core/engine.py +6 -3
  7. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/core/filter.py +14 -10
  8. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/core/graph.py +16 -9
  9. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/core/pipeline.py +21 -14
  10. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/core/signature.py +2 -1
  11. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/data_objects/curves.py +120 -99
  12. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/data_objects/data.py +26 -16
  13. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/data_objects/image.py +18 -17
  14. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/data_objects/parameters.py +12 -10
  15. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/gui.py +33 -27
  16. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/mpl_control.py +20 -11
  17. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/mpl_gui.py +38 -23
  18. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/mpl_window.py +14 -11
  19. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/nb_control.py +19 -8
  20. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/nb_gui.py +15 -12
  21. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/qt_control.py +65 -46
  22. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/qt_gui.py +169 -111
  23. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/window.py +12 -8
  24. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/headless/control.py +23 -16
  25. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/headless/keyboard.py +15 -14
  26. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/headless/pipeline.py +71 -42
  27. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/helper/_private.py +1 -1
  28. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/helper/choose_backend.py +9 -8
  29. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/helper/control_abbreviation.py +50 -32
  30. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/helper/filter_decorator.py +29 -19
  31. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/helper/keyword_args_analyzer.py +22 -18
  32. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/helper/pipeline_decorator.py +7 -5
  33. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/thirdparty/images_openai_api.py +23 -17
  34. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/thirdparty/music_spotify.py +7 -5
  35. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe.egg-info/PKG-INFO +36 -4
  36. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe.egg-info/SOURCES.txt +1 -0
  37. interactive_pipe-0.6.0/src/interactive_pipe.egg-info/requires.txt +27 -0
  38. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_cache.py +7 -11
  39. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_control_abbreviations.py +9 -8
  40. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_controller.py +2 -1
  41. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_core.py +1 -10
  42. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_curves.py +24 -17
  43. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_decorator.py +6 -4
  44. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_engine.py +26 -15
  45. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_headless.py +41 -28
  46. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_image.py +3 -1
  47. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_parameters.py +4 -2
  48. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_recorder.py +12 -13
  49. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/LICENSE +0 -0
  50. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/setup.cfg +0 -0
  51. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/core/__init__.py +0 -0
  52. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/data_objects/__init__.py +0 -0
  53. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/graphical/__init__.py +0 -0
  54. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/headless/__init__.py +0 -0
  55. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe/helper/__init__.py +0 -0
  56. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
  57. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/src/interactive_pipe.egg-info/top_level.txt +0 -0
  58. {interactive_pipe-0.5.8 → interactive_pipe-0.6.0}/test/test_filter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: interactive_pipe
3
- Version: 0.5.8
3
+ Version: 0.6.0
4
4
  Summary: Library to create flexible interactive image processing pipelines and automatically add a graphical user interface without knowing anything about GUI coding!
5
5
  Author-email: Balthazar Neveu <balthazarneveu@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/balthazarneveu/interactive_pipe
@@ -11,8 +11,34 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.7
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Requires-Dist: matplotlib>=3.5.3
15
+ Requires-Dist: numpy>=1.21.6
16
+ Requires-Dist: Pillow>=9.0.1
17
+ Requires-Dist: PyYAML>=5.4.1
18
+ Provides-Extra: qt6
19
+ Requires-Dist: PyQt6>=6.5.2; extra == "qt6"
20
+ Requires-Dist: PyQt6_sip>=13.5.2; extra == "qt6"
21
+ Provides-Extra: qt5
22
+ Requires-Dist: PyQt5>=5.15.9; extra == "qt5"
23
+ Requires-Dist: PyQt5_sip>=12.12.1; extra == "qt5"
24
+ Provides-Extra: notebook
25
+ Requires-Dist: ipywidgets>=8.0.7; extra == "notebook"
26
+ Provides-Extra: pytest
27
+ Requires-Dist: opencv_python_headless>=4.8.1.78; extra == "pytest"
28
+ Requires-Dist: pytest>=6.2.5; extra == "pytest"
29
+ Provides-Extra: full
30
+ Requires-Dist: PyQt6>=6.5.2; extra == "full"
31
+ Requires-Dist: PyQt6_sip>=13.5.2; extra == "full"
32
+ Requires-Dist: opencv_python_headless>=4.8.1.78; extra == "full"
33
+ Requires-Dist: pytest>=6.2.5; extra == "full"
34
+ Requires-Dist: ipywidgets>=8.0.7; extra == "full"
35
+ Requires-Dist: pandas; extra == "full"
14
36
 
15
37
  # `interactive_pipe`
38
+
39
+ Quick setup
40
+ `pip install interactive-pipe[full]`
41
+
16
42
  ### Concept
17
43
  - Develop an algorithm while debugging visually with plots, while checking robustness & continuity to parameters change.
18
44
  - Magically create a graphical interface to easily demonstrate a concept or simply tune your algorithm.
@@ -33,7 +59,7 @@ License-File: LICENSE
33
59
  ----------------------
34
60
 
35
61
  ### Setup
36
- `pip3 install interactive-pipe`
62
+ `pip install interactive-pipe[full]`
37
63
 
38
64
  ----------------------
39
65
 
@@ -56,7 +82,7 @@ License-File: LICENSE
56
82
  ----------------------
57
83
 
58
84
  ## :scroll: Features
59
- **Version 0.5.8**
85
+ **Version 0.6.0**
60
86
  - Modular multi-image processing filters
61
87
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
62
88
  - Support in jupyter notebooks
@@ -117,6 +143,12 @@ Shortcuts while using the GUI (QT & matplotlib backends)
117
143
  # Tutorials
118
144
 
119
145
  ### Learn by examples
146
+ #### [Basic image processing](/demo/multi_image.py)
147
+ | GUI | Pipeline |
148
+ |:--:|:--:|
149
+ |![](/doc/images/image_selector.png) | ![](/doc/images/image_selector_graph.png) |
150
+
151
+ ![](/doc/images/demo_multi_image.gif)
120
152
 
121
153
  #### [Speech exploration notebook](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
122
154
  ![Speech processing exploration in a notebook](/doc/images/jupyter_integration_speech.png)
@@ -5,10 +5,17 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "interactive_pipe"
8
- version = "0.5.8"
8
+ version = "0.6.0"
9
9
  authors = [
10
10
  { name="Balthazar Neveu", email="balthazarneveu@gmail.com" },
11
11
  ]
12
+ dependencies = [
13
+ "matplotlib>=3.5.3",
14
+ "numpy>=1.21.6",
15
+ "Pillow>=9.0.1",
16
+ "PyYAML>=5.4.1",
17
+ ]
18
+
12
19
  description = "Library to create flexible interactive image processing pipelines and automatically add a graphical user interface without knowing anything about GUI coding!"
13
20
  readme = "readme.md"
14
21
  requires-python = ">=3.7"
@@ -20,4 +27,33 @@ classifiers = [
20
27
 
21
28
  [project.urls]
22
29
  "Homepage" = "https://github.com/balthazarneveu/interactive_pipe"
23
- "Bug Tracker" = "https://github.com/balthazarneveu/interactive_pipe/issues"
30
+ "Bug Tracker" = "https://github.com/balthazarneveu/interactive_pipe/issues"
31
+
32
+ [project.optional-dependencies]
33
+ qt6=[
34
+ "PyQt6>=6.5.2",
35
+ "PyQt6_sip>=13.5.2",
36
+ ]
37
+
38
+ qt5=[
39
+ "PyQt5>=5.15.9",
40
+ "PyQt5_sip>=12.12.1",
41
+ ]
42
+
43
+ notebook=[
44
+ "ipywidgets>=8.0.7",
45
+ ]
46
+
47
+ pytest=[
48
+ "opencv_python_headless>=4.8.1.78",
49
+ "pytest>=6.2.5"
50
+ ]
51
+
52
+ full=[
53
+ "PyQt6>=6.5.2",
54
+ "PyQt6_sip>=13.5.2",
55
+ "opencv_python_headless>=4.8.1.78",
56
+ "pytest>=6.2.5",
57
+ "ipywidgets>=8.0.7",
58
+ "pandas",
59
+ ]
@@ -1,4 +1,8 @@
1
1
  # `interactive_pipe`
2
+
3
+ Quick setup
4
+ `pip install interactive-pipe[full]`
5
+
2
6
  ### Concept
3
7
  - Develop an algorithm while debugging visually with plots, while checking robustness & continuity to parameters change.
4
8
  - Magically create a graphical interface to easily demonstrate a concept or simply tune your algorithm.
@@ -19,7 +23,7 @@
19
23
  ----------------------
20
24
 
21
25
  ### Setup
22
- `pip3 install interactive-pipe`
26
+ `pip install interactive-pipe[full]`
23
27
 
24
28
  ----------------------
25
29
 
@@ -42,7 +46,7 @@
42
46
  ----------------------
43
47
 
44
48
  ## :scroll: Features
45
- **Version 0.5.8**
49
+ **Version 0.6.0**
46
50
  - Modular multi-image processing filters
47
51
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
48
52
  - Support in jupyter notebooks
@@ -103,6 +107,12 @@ Shortcuts while using the GUI (QT & matplotlib backends)
103
107
  # Tutorials
104
108
 
105
109
  ### Learn by examples
110
+ #### [Basic image processing](/demo/multi_image.py)
111
+ | GUI | Pipeline |
112
+ |:--:|:--:|
113
+ |![](/doc/images/image_selector.png) | ![](/doc/images/image_selector_graph.png) |
114
+
115
+ ![](/doc/images/demo_multi_image.gif)
106
116
 
107
117
  #### [Speech exploration notebook](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
108
118
  ![Speech processing exploration in a notebook](/doc/images/jupyter_integration_speech.png)
@@ -1,4 +1,4 @@
1
1
  from interactive_pipe.helper.pipeline_decorator import pipeline, interactive_pipeline
2
2
  from interactive_pipe.headless.control import Control
3
- from interactive_pipe.headless.keyboard import KeyboardControl
4
- from interactive_pipe.helper.filter_decorator import interactive, interact
3
+ from interactive_pipe.headless.keyboard import KeyboardControl
4
+ from interactive_pipe.helper.filter_decorator import interactive, interact
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from copy import deepcopy
3
- from typing import Any, Dict
3
+ from typing import Any
4
4
 
5
5
 
6
6
  class CachedResults:
@@ -8,7 +8,7 @@ class CachedResults:
8
8
  Helper class to store the results of a Filter.
9
9
 
10
10
  The overall purpose of these classes is to prevent unnecessary computations by reusing the
11
- results of previous filter operations when the input parameters have not changed.
11
+ results of previous filter operations when the input parameters have not changed.
12
12
  This is accomplished by storing the results of the filter operations and the parameters
13
13
  used in those operations, and checking for changes in the parameters
14
14
  before re-computing the results.
@@ -20,7 +20,7 @@ class CachedResults:
20
20
 
21
21
  Please note that if you use safe_buffer_deepcopy=False,
22
22
  only pointers are copied when updating the cache, no deepcopy is performed here.
23
- You should only use safe_buffer_deepcopy=False
23
+ You should only use safe_buffer_deepcopy=False
24
24
  if you're 100% sure you don't do inplace modifications.
25
25
 
26
26
  Underlying class used in the interactive pipe cache mechanism.
@@ -76,7 +76,7 @@ class CachedResults:
76
76
  class StateChange:
77
77
  """
78
78
  Helper class to check whether or not input parameters have been updated.
79
-
79
+
80
80
  Underlying class used in the interactive pipe cache mechanism.
81
81
  """
82
82
 
@@ -7,6 +7,7 @@ from typing import List
7
7
  from interactive_pipe.core.cache import CachedResults
8
8
  from interactive_pipe.core.filter import FilterCore
9
9
 
10
+
10
11
  class PipelineEngine:
11
12
  def __init__(self, cache=False, safe_input_buffer_deepcopy=True) -> None:
12
13
  self.cache = cache
@@ -21,7 +22,8 @@ class PipelineEngine:
21
22
  for input_index, inp in enumerate(imglst):
22
23
  if self.safe_input_buffer_deepcopy:
23
24
  result[input_index] = deepcopy(inp)
24
- logging.debug(f"<<< Deepcopy input images {input_index}")
25
+ logging.debug(
26
+ f"<<< Deepcopy input images {input_index}")
25
27
  else:
26
28
  result[input_index] = inp
27
29
  elif isinstance(imglst, dict):
@@ -56,7 +58,8 @@ class PipelineEngine:
56
58
  if prc.inputs:
57
59
  routing_in = [
58
60
  result[idi] if idi is not None else None for idi in prc.inputs]
59
- logging.debug("in types->", [type(inp) for inp in routing_in])
61
+ logging.debug("in types->", [type(inp)
62
+ for inp in routing_in])
60
63
  out = prc.run(*routing_in)
61
64
  if out is not None:
62
65
  logging.debug("out types->", [type(ou) for ou in out])
@@ -83,4 +86,4 @@ class PipelineEngine:
83
86
  # Limit result using self.numfigs but with indices pointed by last filter
84
87
  logging.info("\n".join(performances))
85
88
  logging.info(f"Full buffer: {len(result)}")
86
- return result
89
+ return result
@@ -5,13 +5,14 @@ from typing import Callable, List, Optional, Union, Tuple, Any
5
5
  from interactive_pipe.core.cache import CachedResults
6
6
  from interactive_pipe.core.signature import analyze_apply_fn_signature
7
7
 
8
+
8
9
  class PureFilter:
9
10
  def __init__(self, apply_fn: Optional[Callable] = None, name: Optional[str] = None, default_params: dict = {}):
10
11
  self.name = name if name else (
11
12
  self.__class__.__name__ if not apply_fn else apply_fn.__name__)
12
13
  if apply_fn is not None:
13
14
  self.apply = apply_fn
14
- self.__initialize_default_values() # initialize default values from .apply method
15
+ self.__initialize_default_values() # initialize default values from .apply method
15
16
  self.values = deepcopy(default_params)
16
17
 
17
18
  @property
@@ -34,14 +35,14 @@ class PureFilter:
34
35
  self.signature = (self.__args_names, self.__kwargs_names)
35
36
  else: # skip computing signature
36
37
  pass
37
-
38
+
38
39
  def __initialize_default_values(self):
39
40
  assert not hasattr(self, "_values")
40
41
  self.check_apply_signature()
41
42
  self._values = self.__kwargs_names
42
43
  if "global_params" in self.__kwargs_names.keys():
43
44
  self._values.pop("global_params")
44
-
45
+
45
46
  @property
46
47
  def values(self):
47
48
  return self._values
@@ -70,6 +71,7 @@ class PureFilter:
70
71
 
71
72
  class FilterCore(PureFilter):
72
73
  """PureFilter with cache storage + routing nodes defined (inputs & outputs fields)"""
74
+
73
75
  def __init__(self,
74
76
  apply_fn: Callable = None,
75
77
  name: Optional[str] = None,
@@ -108,19 +110,21 @@ class FilterCore(PureFilter):
108
110
  assert len(out) >= len(
109
111
  self.outputs), "number of outputs shall be at least greater or equal to what's expected by the filter"
110
112
  return out
111
-
113
+
112
114
  else:
113
- logging.debug(f"need to return a tuple when you have a single element out {type(out)}")
114
- assert len(self.outputs)==1, "returning a single element!"
115
+ logging.debug(
116
+ f"need to return a tuple when you have a single element out {type(out)}")
117
+ assert len(self.outputs) == 1, "returning a single element!"
115
118
  return (out,)
116
-
119
+
117
120
  def __repr__(self) -> str:
118
121
  descr = f"{self.name}: "
119
122
  descr += "(" + (", ".join([f"{it}" for it in self.inputs])) + ")"
120
- descr += " -> " + "(" + ", ".join([f"{it}" for it in self.outputs]) + ")"
123
+ descr += " -> " + \
124
+ "(" + ", ".join([f"{it}" for it in self.outputs]) + ")"
121
125
  # descr += "\n"
122
126
  return descr
123
-
127
+
124
128
  def __call__(self, *args: Any, **kwargs: Any) -> Any:
125
129
  control_dict = {}
126
130
  if hasattr(self, "controls"):
@@ -128,4 +132,4 @@ class FilterCore(PureFilter):
128
132
  control_dict[ctrl_name] = ctrl.value
129
133
  merged_kwargs = {**kwargs, **control_dict}
130
134
  out = self.apply(*args, **merged_kwargs)
131
- return out
135
+ return out
@@ -5,6 +5,7 @@ from typing import List, Callable, Tuple, Optional, Union
5
5
  from interactive_pipe.core.filter import FilterCore
6
6
  from interactive_pipe.core.signature import analyze_apply_fn_signature
7
7
 
8
+
8
9
  def get_name(node: ast.NodeVisitor) -> Union[str, List[str], None]:
9
10
  if isinstance(node, ast.Name):
10
11
  return node.id
@@ -14,8 +15,9 @@ def get_name(node: ast.NodeVisitor) -> Union[str, List[str], None]:
14
15
  return [get_name(elt) for elt in node.elts]
15
16
  else:
16
17
  return None
17
-
18
- def flatten_target_names(targets: List[Union[list, str, None]], mapping_function: Optional[Callable]=None) -> List[str]:
18
+
19
+
20
+ def flatten_target_names(targets: List[Union[list, str, None]], mapping_function: Optional[Callable] = None) -> List[str]:
19
21
  output_names = []
20
22
  for target in targets:
21
23
  target_name = mapping_function(target) if mapping_function else target
@@ -29,7 +31,7 @@ def flatten_target_names(targets: List[Union[list, str, None]], mapping_function
29
31
  return output_names
30
32
 
31
33
 
32
- def get_call_graph(func:Callable, global_context=None) -> dict:
34
+ def get_call_graph(func: Callable, global_context=None) -> dict:
33
35
  code = inspect.getsource(func)
34
36
  tree = ast.parse(code)
35
37
  results = []
@@ -53,10 +55,13 @@ def get_call_graph(func:Callable, global_context=None) -> dict:
53
55
  value = node.value
54
56
  if isinstance(value, ast.Call):
55
57
  function_name = get_name(value.func)
56
- logging.debug(f"classic function with assignment {function_name}")
57
- assert function_name in global_context.keys(), f"Probably mispelled {function_name}"
58
+ logging.debug(
59
+ f"classic function with assignment {function_name}")
60
+ assert function_name in global_context.keys(
61
+ ), f"Probably mispelled {function_name}"
58
62
  input_names = [get_name(arg) for arg in value.args]
59
- output_names = flatten_target_names(targets, mapping_function=get_name)
63
+ output_names = flatten_target_names(
64
+ targets, mapping_function=get_name)
60
65
  function_object = global_context[function_name]
61
66
  if isinstance(function_object, FilterCore):
62
67
  # Case of a filter instance
@@ -73,16 +78,18 @@ def get_call_graph(func:Callable, global_context=None) -> dict:
73
78
  sig = analyze_apply_fn_signature(function_object)
74
79
  function_apply = global_context[function_name]
75
80
  else:
76
- raise TypeError(f"Not supported {function_name} - should be function or FilterCore")
81
+ raise TypeError(
82
+ f"Not supported {function_name} - should be function or FilterCore")
77
83
  results.append({
78
84
  "function_name": function_name,
79
85
  "function_object": function_apply,
80
- "signature" : {"args": sig[0], "kwargs": sig[1]},
86
+ "signature": {"args": sig[0], "kwargs": sig[1]},
81
87
  "args": input_names,
82
88
  "returns": output_names,
83
89
  })
84
90
  main_function = tree.body[0]
85
- outputs = [get_name(ret.value) for ret in main_function.body if isinstance(ret, ast.Return)]
91
+ outputs = [get_name(ret.value)
92
+ for ret in main_function.body if isinstance(ret, ast.Return)]
86
93
  assert len(outputs) <= 1, "cannot return several times!"
87
94
  outputs = flatten_target_names(outputs, mapping_function=None)
88
95
  inputs, kwargs = analyze_apply_fn_signature(func)
@@ -3,6 +3,7 @@ from interactive_pipe.core.filter import FilterCore
3
3
  from interactive_pipe.core.engine import PipelineEngine
4
4
  import logging
5
5
 
6
+
6
7
  class PipelineCore:
7
8
  """A pipeline is defined as the combination of:
8
9
  - a list of filters
@@ -10,19 +11,21 @@ class PipelineCore:
10
11
  - optionally, some inputs to process
11
12
  """
12
13
 
13
- def __init__(self, filters: List[FilterCore], name="pipeline", cache=False, inputs: Optional[list] = None, parameters: dict = {}, global_params={}, outputs: Optional[list]=None, safe_input_buffer_deepcopy: bool=True):
14
+ def __init__(self, filters: List[FilterCore], name="pipeline", cache=False, inputs: Optional[list] = None, parameters: dict = {}, global_params={}, outputs: Optional[list] = None, safe_input_buffer_deepcopy: bool = True):
14
15
  if not all(isinstance(f, FilterCore) for f in filters):
15
16
  raise ValueError(
16
17
  f"All elements in 'filters' must be instances of 'Filter'. {[type(f) for f in filters]}")
17
18
  self.filters = filters
18
- self.engine = PipelineEngine(cache, safe_input_buffer_deepcopy=safe_input_buffer_deepcopy)
19
+ self.engine = PipelineEngine(
20
+ cache, safe_input_buffer_deepcopy=safe_input_buffer_deepcopy)
19
21
  self.global_params = global_params
20
22
  for filter in self.filters:
21
23
  # link each filter to global params
22
24
  filter.global_params = self.global_params
23
25
  self.reset_cache()
24
26
  if inputs is None:
25
- logging.warning("Setting a pipeline without input - use inputs=[] to get rid of this warning")
27
+ logging.warning(
28
+ "Setting a pipeline without input - use inputs=[] to get rid of this warning")
26
29
  self.inputs_routing = []
27
30
  else:
28
31
  self.inputs_routing = inputs
@@ -30,9 +33,10 @@ class PipelineCore:
30
33
  self.__initialized_inputs = False
31
34
  if outputs is None:
32
35
  outputs = self.filters[-1].outputs
33
- logging.warning(f"Using last filter outputs {self.filters[-1]} {outputs}")
34
-
35
- self.outputs = outputs # output indexes (routing)
36
+ logging.warning(
37
+ f"Using last filter outputs {self.filters[-1]} {outputs}")
38
+
39
+ self.outputs = outputs # output indexes (routing)
36
40
  # You need to set the values to their default_value for each filter
37
41
  self.parameters = parameters
38
42
  self.results = None
@@ -46,14 +50,14 @@ class PipelineCore:
46
50
  """Useful for standalone python acess without gui or disk write
47
51
  """
48
52
  return self.engine.run(self.filters, imglst=self.inputs)
49
-
53
+
50
54
  @property
51
55
  def parameters(self):
52
56
  parameters = {}
53
57
  for filt in self.filters:
54
58
  parameters[filt.name] = filt.values
55
59
  return parameters
56
-
60
+
57
61
  @parameters.setter
58
62
  def parameters(self, new_parameters: Dict[str, any]):
59
63
  """Force tuning parameters
@@ -76,14 +80,14 @@ class PipelineCore:
76
80
  available_filters_names = [filt.name for filt in self.filters]
77
81
  for filter_name in new_parameters.keys():
78
82
  assert filter_name in available_filters_names, f"filter {filter_name} does not exist {available_filters_names}"
79
- self.filters[available_filters_names.index(filter_name)].values = new_parameters[filter_name]
80
-
83
+ self.filters[available_filters_names.index(
84
+ filter_name)].values = new_parameters[filter_name]
81
85
 
82
86
  @property
83
87
  def inputs(self):
84
88
  assert self.__initialized_inputs, "Cannot access unitialized inputs!"
85
89
  return self.__inputs
86
-
90
+
87
91
  @inputs.setter
88
92
  def inputs(self, inputs: list):
89
93
  if inputs is not None:
@@ -94,7 +98,8 @@ class PipelineCore:
94
98
  self.__inputs = inputs
95
99
  elif isinstance(inputs, list) or isinstance(inputs, tuple):
96
100
  # inputs is a list or a tuple
97
- assert len(inputs) == len(self.inputs_routing), f"wrong amount of inputs\nprovided {len(inputs)} inputs vs expected:{len(self.inputs_routing)}"
101
+ assert len(inputs) == len(
102
+ self.inputs_routing), f"wrong amount of inputs\nprovided {len(inputs)} inputs vs expected:{len(self.inputs_routing)}"
98
103
  self.__inputs = {}
99
104
  for idx, input_name in enumerate(self.inputs_routing):
100
105
  self.__inputs[input_name] = inputs[idx]
@@ -104,9 +109,11 @@ class PipelineCore:
104
109
  self.__inputs = {self.inputs_routing[0]: inputs}
105
110
  assert isinstance(self.__inputs, dict)
106
111
  if len(self.__inputs.keys()) == 0:
107
- self.__inputs = None # similar to having no input, but explicitly saying that we have initialized it.
112
+ # similar to having no input, but explicitly saying that we have initialized it.
113
+ self.__inputs = None
108
114
  else:
109
- assert self.inputs_routing is None or ((isinstance(self.inputs_routing, tuple) or isinstance(self.inputs_routing, list)) and len(self.inputs_routing)==0)
115
+ assert self.inputs_routing is None or ((isinstance(self.inputs_routing, tuple) or isinstance(
116
+ self.inputs_routing, list)) and len(self.inputs_routing) == 0)
110
117
  self.__inputs = None
111
118
  self.__initialized_inputs = True
112
119
  self.reset_cache()
@@ -1,6 +1,7 @@
1
1
  import inspect
2
2
  from typing import Callable, Tuple
3
3
 
4
+
4
5
  def analyze_apply_fn_signature(apply_fn: Callable) -> Tuple[dict, dict]:
5
6
  signature = inspect.signature(apply_fn)
6
7
  keyword_args = {
@@ -14,4 +15,4 @@ def analyze_apply_fn_signature(apply_fn: Callable) -> Tuple[dict, dict]:
14
15
  for k, v in signature.parameters.items()
15
16
  if v.default is inspect.Parameter.empty
16
17
  ]
17
- return positional_args, keyword_args
18
+ return positional_args, keyword_args