interactive-pipe 0.8.6__tar.gz → 0.8.8__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 (72) hide show
  1. interactive_pipe-0.8.8/LICENSE +21 -0
  2. {interactive_pipe-0.8.6/src/interactive_pipe.egg-info → interactive_pipe-0.8.8}/PKG-INFO +155 -21
  3. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/pyproject.toml +2 -2
  4. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/readme.md +151 -18
  5. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/__init__.py +19 -2
  6. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/core/cache.py +6 -5
  7. interactive_pipe-0.8.8/src/interactive_pipe/core/context.py +471 -0
  8. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/core/engine.py +94 -5
  9. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/core/filter.py +85 -37
  10. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/core/graph.py +13 -3
  11. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/core/pipeline.py +47 -25
  12. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/data_objects/audio.py +16 -7
  13. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/data_objects/curves.py +88 -49
  14. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/data_objects/data.py +24 -12
  15. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/data_objects/image.py +14 -7
  16. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/data_objects/parameters.py +12 -8
  17. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/gradio_control.py +55 -14
  18. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/gradio_gui.py +33 -14
  19. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/gui.py +83 -9
  20. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/mpl_control.py +24 -7
  21. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/mpl_gui.py +19 -9
  22. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/mpl_window.py +7 -3
  23. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/nb_control.py +51 -9
  24. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/nb_gui.py +12 -8
  25. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/qt_control.py +79 -35
  26. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/qt_gui.py +72 -27
  27. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/headless/control.py +81 -28
  28. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/headless/keyboard.py +9 -6
  29. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/headless/pipeline.py +56 -16
  30. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/helper/choose_backend.py +4 -0
  31. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/helper/filter_decorator.py +6 -4
  32. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8/src/interactive_pipe.egg-info}/PKG-INFO +155 -21
  33. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe.egg-info/SOURCES.txt +10 -2
  34. interactive_pipe-0.8.8/test/test_context.py +630 -0
  35. interactive_pipe-0.8.8/test/test_context_compatibility.py +427 -0
  36. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_control_abbreviations.py +25 -10
  37. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_controller.py +5 -5
  38. interactive_pipe-0.8.8/test/test_core_exceptions.py +230 -0
  39. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_curves.py +2 -2
  40. interactive_pipe-0.8.8/test/test_data_objects_exceptions.py +297 -0
  41. interactive_pipe-0.8.8/test/test_edge_cases.py +248 -0
  42. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_filter.py +0 -1
  43. interactive_pipe-0.8.8/test/test_graphical_exceptions.py +322 -0
  44. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_headless.py +6 -6
  45. interactive_pipe-0.8.8/test/test_headless_exceptions.py +146 -0
  46. interactive_pipe-0.8.8/test/test_legacy_warnings.py +176 -0
  47. interactive_pipe-0.8.8/test/test_mutable_defaults.py +134 -0
  48. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_parameters.py +2 -2
  49. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_recorder.py +0 -1
  50. interactive_pipe-0.8.6/LICENSE +0 -24
  51. interactive_pipe-0.8.6/src/interactive_pipe/thirdparty/images_openai_api.py +0 -159
  52. interactive_pipe-0.8.6/src/interactive_pipe/thirdparty/music_spotify.py +0 -55
  53. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/setup.cfg +0 -0
  54. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/core/__init__.py +0 -0
  55. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/core/signature.py +0 -0
  56. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/data_objects/__init__.py +0 -0
  57. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/__init__.py +0 -0
  58. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/graphical/window.py +0 -0
  59. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/headless/__init__.py +0 -0
  60. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/helper/__init__.py +0 -0
  61. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/helper/_private.py +0 -0
  62. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/helper/control_abbreviation.py +0 -0
  63. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/helper/keyword_args_analyzer.py +0 -0
  64. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe/helper/pipeline_decorator.py +0 -0
  65. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
  66. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe.egg-info/requires.txt +0 -0
  67. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/src/interactive_pipe.egg-info/top_level.txt +0 -0
  68. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_cache.py +0 -0
  69. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_core.py +0 -0
  70. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_decorator.py +0 -0
  71. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_engine.py +0 -0
  72. {interactive_pipe-0.8.6 → interactive_pipe-0.8.8}/test/test_image.py +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Balthazar Neveu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,12 +1,12 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: interactive_pipe
3
- Version: 0.8.6
3
+ Version: 0.8.8
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
7
7
  Project-URL: Bug Tracker, https://github.com/balthazarneveu/interactive_pipe/issues
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: The Unlicense (Unlicense)
9
+ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.7
12
12
  Description-Content-Type: text/markdown
@@ -41,6 +41,7 @@ Requires-Dist: pytest>=6.2.5; extra == "full"
41
41
  Requires-Dist: ipywidgets>=7.7.1; extra == "full"
42
42
  Requires-Dist: pandas; extra == "full"
43
43
  Requires-Dist: gradio; extra == "full"
44
+ Dynamic: license-file
44
45
 
45
46
 
46
47
  <center>
@@ -49,6 +50,7 @@ Requires-Dist: gradio; extra == "full"
49
50
  |:--:|
50
51
  |Quick setup `pip install interactive-pipe` |
51
52
  | [Project website](https://balthazarneveu.github.io/interactive_pipe/) |
53
+ |[![Interactive pipe python package](https://github.com/balthazarneveu/interactive_pipe/actions/workflows/pytest.yaml/badge.svg)](https://github.com/balthazarneveu/interactive_pipe/actions/workflows/pytest.yaml) |
52
54
 
53
55
  </center>
54
56
 
@@ -68,8 +70,8 @@ Requires-Dist: gradio; extra == "full"
68
70
  | Science notebook | Toddler DIY Jukebox on a raspberry Pi |
69
71
  |:-----: |:-----:|
70
72
  | ![science notebook](/doc/images/scientific_notebook.jpg) | ![jukebox](/doc/images/jukebox.jpg)
71
- | Sliders are added automatically in your jupyter notebook. This works on Google Collab and the code takes about 40 lines of code. No Widgets, event handlers or matplotlib knowledge required.| Plays some music when you touch the icon. Images can be generated using the OpenAI [Dall-E API helpers](https://github.com/balthazarneveu/interactive_pipe/blob/master/src/interactive_pipe/thirdparty/images_openai_api.py) . Caption added through the title mechanism. Music samples generated by prompting [MusicGen](https://huggingface.co/spaces/facebook/MusicGen) |
72
- | [Demo notebook on collab](https://colab.research.google.com/drive/1AwHyjZH8MnzZqwsvbmxBoB15btuMIwtk?usp=sharing) | [jukebox.py demo code](/demo/jukebox.py)|
73
+ | Sliders are added automatically in your jupyter notebook. This works on Google Collab and the code takes about 40 lines of code. No Widgets, event handlers or matplotlib knowledge required.| Plays some music when you touch the icon. Caption added through the title mechanism. Music samples generated by prompting [MusicGen](https://huggingface.co/spaces/facebook/MusicGen) |
74
+ | [Demo notebook on collab](https://colab.research.google.com/drive/1AwHyjZH8MnzZqwsvbmxBoB15btuMIwtk?usp=sharing) | [jukebox_demo.py demo code](/demo/jukebox_demo.py)|
73
75
 
74
76
 
75
77
  ----------------------
@@ -106,7 +108,7 @@ pip install -e ".[full]"
106
108
 
107
109
  ## :scroll: Features
108
110
 
109
- **Version 0.8.6**
111
+ **Version 0.8.8**
110
112
  - Modular multi-image processing filters
111
113
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
112
114
  - Support in jupyter notebooks
@@ -114,10 +116,13 @@ pip install -e ".[full]"
114
116
  - Cache intermediate results in RAM for much faster processing
115
117
  - `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
116
118
  - Support Curve plots (2D signals)
117
- - :new: gradio backend (+allows sharing with others).
118
- - :new: Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
119
- - :new: Circular sliders for Qt Backend
120
- - :new: Text prompt (`free_text=("Hello world!", None),`)
119
+ - Gradio backend (+allows sharing with others).
120
+ - Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
121
+ - Circular sliders for Qt Backend
122
+ - Text prompt (`free_text=("Hello world!", None),`)
123
+ - TimeControl (possibility to play/pause time using an incrementing timer)
124
+ - :new: **Context API**: Direct access to shared context across filters via `get_context()`, `context`, `layout`, `audio`
125
+ - :new: MIT License
121
126
 
122
127
 
123
128
 
@@ -325,21 +330,119 @@ if __name__ == '__main__':
325
330
 
326
331
  ----------
327
332
 
333
+ ### Release Notes
334
+
335
+ #### Version 0.8.8 (January 2026)
336
+
337
+ **New Features:**
338
+ - **Clean Context API**: Access shared context directly without `global_params` pollution
339
+ - `get_context()` - Get the shared context dictionary
340
+ - `context` - Direct dict-like access to context
341
+ - `layout` - Access layout configuration directly
342
+ - `audio` - Access audio functionality directly
343
+
344
+ **Code Quality Improvements:**
345
+ - Replaced all assertions with proper exceptions (`ValueError`, `TypeError`, `RuntimeError`)
346
+ - Fixed all mutable default arguments across the codebase (prevents shared state bugs)
347
+ - Improved type hints with proper `Optional` and `Any` types
348
+ - Better error messages for debugging
349
+
350
+ **UX Improvements:**
351
+ - Dropdown menus are now hidden when only a single choice is available
352
+ - Helpful message displayed when Graphviz is not available (when pressing `G`)
353
+ - Fixed warning in linestyle for curves
354
+
355
+ **Bug Fixes:**
356
+ - Fixed audio initialization order in Qt backend
357
+ - Fixed pytest failures for optional dependencies in CI
358
+ - Fixed various edge cases in error handling
359
+
360
+ **Migration of old context or global_params:**
361
+ - Use `global_params=SharedContext.injected()` to let code interpreters know that there's no need to pass this parameter.
362
+
363
+ ```python
364
+ # Before
365
+ def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params:dict =None):
366
+ global_params["brightness"] = brightness
367
+ return img * brightness
368
+
369
+ # Progressive migration
370
+ def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params:dict ==SharedContext.injected()):
371
+ global_params["brightness"] = brightness
372
+ return img * brightness
373
+
374
+ # Recommended version
375
+ from interactive_pipe import context
376
+ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
377
+ context.shared_brightness = brightness # shared with all filters
378
+ return img * brightness
379
+ ```
380
+
381
+
382
+
383
+
384
+ **License:**
385
+ - Updated to MIT License
386
+
387
+ ---
388
+
328
389
  ### History
329
- - Interactive pipe was initially developped by [Balthazar Neveu](https://github.com/balthazarneveu) as part of the [irdrone project](https://github.com/wisescootering/infrareddrone/tree/master/interactive) based on matplotlib.
390
+ - Interactive pipe was initially developed by [Balthazar Neveu](https://github.com/balthazarneveu) as part of the [irdrone project](https://github.com/wisescootering/infrareddrone/tree/master/interactive) based on matplotlib.
330
391
  - Later, more contributions were also made by [Giuseppe Moschetti](https://github.com/g-moschetti) and Sylvain Leroy.
331
392
  - August 2023: rewriting the whole core and supporting several graphical backends!
332
393
  - September 2024: Gradio backend
394
+ - January 2026: Clean Context API and code quality improvements (v0.8.8)
333
395
 
334
396
 
335
397
  ### FAQ
336
- - :question: Is there a difference between `global_params` and `context` ?
337
- > No, `global_params`, `global_parameters`, `global_state`, `global_context`, `context`, `state` all mean the same thing and are all supported for legacy reasons. `context` is the preferred wording.
398
+ - :question: What is the recommended way to access shared context?
399
+ > **New in v0.8.8**: Use the clean context API for direct access:
400
+ > ```python
401
+ > from interactive_pipe import context, layout, audio, get_context
402
+ >
403
+ > @interactive()
404
+ > def my_filter(img):
405
+ > context["shared_key"] = "shared_value" # Direct dict-like access
406
+ > context.brightness = 0.5
407
+ > layout.set_title("output_image", "My Image") # Layout helpers
408
+ >
409
+ > return img
410
+ > ```
411
+
412
+ - :question: How do I change the layout? *Can I change the grid layout of images live? (like you compare 2 images side by side and you want to start comparing 4 images in a 2x2 fashion for debugging purpose)*. It is possible with Qt backend.
413
+
414
+ > Use the `layout` helper to control image arrangement and styling:
415
+ > ```python
416
+ > from interactive_pipe import layout
417
+ >
418
+ > def change_layout(layout: str="side_by_side"):
419
+ > # Arrange outputs in a 2x2 grid
420
+ > if layout == "side_by_side":
421
+ > layout.grid([["input", "result"]])
422
+ > if layout == "grid2x2":
423
+ > layout.grid([["input", "processed"], ["histogram_graph", "result"]])
424
+ > # Style individual outputs
425
+ > layout.style("result", title="Final Result")
426
+ > # Note that the string "input", "processed", "histogram_graph", "result"
427
+ > # are the variables used in the pipeline (see below)!
428
+ >
429
+ > def pipeline(input):
430
+ > processed = denoise(input)
431
+ > result = change_brightness(processed)
432
+ > histogram_graph = compute_histo(processed)
433
+ > change_layout()
434
+ > return result
435
+ > ```
436
+
338
437
  - :question: Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
339
438
  > No, don't worry, these will be mapped back to regular sliders!
340
439
  - :question: How do I play audio live?
341
- > :sound: Inside a processing block, write the audio file to disk
342
- > and use `context["__set_audio"](audio_file)`
440
+ > :sound: Inside a processing block, write the audio file to disk and use the audio helper:
441
+ > ```python
442
+ > from interactive_pipe import audio
443
+ > audio.set_audio(audio_file) # New clean API (v0.8.8)
444
+ > # or legacy: context["__set_audio"](audio_file)
445
+ > ```
343
446
  - :question: Do I have to decorate my processing block using the `@interactive`
344
447
  > If you use the `@` decoration style, your function won't be useable in a regular manner (wich may be problematic in a serious development environment)
345
448
  ```python
@@ -372,7 +475,14 @@ def add_interactivity():
372
475
  # Don't do that!
373
476
  def bad_processing_block(inp):
374
477
  inp+=1
375
- ```
478
+ ```
479
+
480
+ - :question: Is there a difference between `global_params` and `context` ?
481
+ > No, `global_params`, `global_parameters`, `global_state`, `global_context`, `context`, `state` all mean the same thing and are all supported for legacy reasons. `context` is the preferred wording. However, we now recommend using the clean context API (see above).
482
+ > :warning: The old `global_params={}` / `context={}` keyword argument style still works for backwards compatibility but is deprecated.
483
+
484
+
485
+
376
486
  # Roadmap and todos
377
487
  :bug: Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
378
488
 
@@ -400,12 +510,36 @@ def bad_processing_block(inp):
400
510
 
401
511
  ![Ultra simplistic ISP](/doc/images/isp_pipeline.png)
402
512
 
513
+ ----------------------
514
+
515
+ ### Development
516
+
517
+ #### Code quality checks
518
+
519
+ Before committing, ensure your code passes the linters and tests. The CI runs these checks automatically:
520
+
521
+ **What CI does:**
522
+ - **Black formatting check** (`.github/workflows/formatting.yaml`): Runs `black --check` to verify code formatting
523
+ - **Flake8 linting** (`.github/workflows/flake8.yaml`): Runs `flake8` to check code quality
524
+ - **Pytest tests** (`.github/workflows/pytest.yaml`): Runs `pytest` on Python 3.9, 3.10, and 3.11
403
525
 
526
+ **Local commands (match CI):**
404
527
 
528
+ ```bash
529
+ # Install linting tools and test dependencies
530
+ pip install black flake8
531
+ pip install -e ".[pytest]"
405
532
 
533
+ # Check code formatting (Black) - matches CI
534
+ black --check .
535
+
536
+ # Auto-format code (Black) - run this if check fails
537
+ black .
538
+
539
+ # Run linting checks (flake8) - matches CI
540
+ flake8 .
541
+
542
+ # Run tests (pytest) - matches CI
543
+ pytest
544
+ ```
406
545
 
407
- #### :test_tube: Experimental features
408
- - Custom events on specific key press
409
- - Display the execution graph of the pipeline `G` key
410
- - [thirdparty/music](/src/interactive_pipe/thirdparty/music.py) Play audio (Qt backend only). Play songs on spotify (linux only) when the spotify app is running.
411
- - [thirdparty/images_openai_api](/src/interactive_pipe/thirdparty/images_openai_api.py) Generate images from prompt using OpenAI API image generation DALL-E Model (:dollar: paid service ~ 2cents/image)
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "interactive_pipe"
8
- version = "0.8.6"
8
+ version = "0.8.8"
9
9
  authors = [
10
10
  { name="Balthazar Neveu", email="balthazarneveu@gmail.com" },
11
11
  ]
@@ -28,7 +28,7 @@ readme = "readme.md"
28
28
  requires-python = ">=3.7"
29
29
  classifiers = [
30
30
  "Programming Language :: Python :: 3",
31
- "License :: OSI Approved :: The Unlicense (Unlicense)",
31
+ "License :: OSI Approved :: MIT License",
32
32
  "Operating System :: OS Independent",
33
33
  ]
34
34
 
@@ -5,6 +5,7 @@
5
5
  |:--:|
6
6
  |Quick setup `pip install interactive-pipe` |
7
7
  | [Project website](https://balthazarneveu.github.io/interactive_pipe/) |
8
+ |[![Interactive pipe python package](https://github.com/balthazarneveu/interactive_pipe/actions/workflows/pytest.yaml/badge.svg)](https://github.com/balthazarneveu/interactive_pipe/actions/workflows/pytest.yaml) |
8
9
 
9
10
  </center>
10
11
 
@@ -24,8 +25,8 @@
24
25
  | Science notebook | Toddler DIY Jukebox on a raspberry Pi |
25
26
  |:-----: |:-----:|
26
27
  | ![science notebook](/doc/images/scientific_notebook.jpg) | ![jukebox](/doc/images/jukebox.jpg)
27
- | Sliders are added automatically in your jupyter notebook. This works on Google Collab and the code takes about 40 lines of code. No Widgets, event handlers or matplotlib knowledge required.| Plays some music when you touch the icon. Images can be generated using the OpenAI [Dall-E API helpers](https://github.com/balthazarneveu/interactive_pipe/blob/master/src/interactive_pipe/thirdparty/images_openai_api.py) . Caption added through the title mechanism. Music samples generated by prompting [MusicGen](https://huggingface.co/spaces/facebook/MusicGen) |
28
- | [Demo notebook on collab](https://colab.research.google.com/drive/1AwHyjZH8MnzZqwsvbmxBoB15btuMIwtk?usp=sharing) | [jukebox.py demo code](/demo/jukebox.py)|
28
+ | Sliders are added automatically in your jupyter notebook. This works on Google Collab and the code takes about 40 lines of code. No Widgets, event handlers or matplotlib knowledge required.| Plays some music when you touch the icon. Caption added through the title mechanism. Music samples generated by prompting [MusicGen](https://huggingface.co/spaces/facebook/MusicGen) |
29
+ | [Demo notebook on collab](https://colab.research.google.com/drive/1AwHyjZH8MnzZqwsvbmxBoB15btuMIwtk?usp=sharing) | [jukebox_demo.py demo code](/demo/jukebox_demo.py)|
29
30
 
30
31
 
31
32
  ----------------------
@@ -62,7 +63,7 @@ pip install -e ".[full]"
62
63
 
63
64
  ## :scroll: Features
64
65
 
65
- **Version 0.8.6**
66
+ **Version 0.8.8**
66
67
  - Modular multi-image processing filters
67
68
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
68
69
  - Support in jupyter notebooks
@@ -70,10 +71,13 @@ pip install -e ".[full]"
70
71
  - Cache intermediate results in RAM for much faster processing
71
72
  - `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
72
73
  - Support Curve plots (2D signals)
73
- - :new: gradio backend (+allows sharing with others).
74
- - :new: Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
75
- - :new: Circular sliders for Qt Backend
76
- - :new: Text prompt (`free_text=("Hello world!", None),`)
74
+ - Gradio backend (+allows sharing with others).
75
+ - Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
76
+ - Circular sliders for Qt Backend
77
+ - Text prompt (`free_text=("Hello world!", None),`)
78
+ - TimeControl (possibility to play/pause time using an incrementing timer)
79
+ - :new: **Context API**: Direct access to shared context across filters via `get_context()`, `context`, `layout`, `audio`
80
+ - :new: MIT License
77
81
 
78
82
 
79
83
 
@@ -281,21 +285,119 @@ if __name__ == '__main__':
281
285
 
282
286
  ----------
283
287
 
288
+ ### Release Notes
289
+
290
+ #### Version 0.8.8 (January 2026)
291
+
292
+ **New Features:**
293
+ - **Clean Context API**: Access shared context directly without `global_params` pollution
294
+ - `get_context()` - Get the shared context dictionary
295
+ - `context` - Direct dict-like access to context
296
+ - `layout` - Access layout configuration directly
297
+ - `audio` - Access audio functionality directly
298
+
299
+ **Code Quality Improvements:**
300
+ - Replaced all assertions with proper exceptions (`ValueError`, `TypeError`, `RuntimeError`)
301
+ - Fixed all mutable default arguments across the codebase (prevents shared state bugs)
302
+ - Improved type hints with proper `Optional` and `Any` types
303
+ - Better error messages for debugging
304
+
305
+ **UX Improvements:**
306
+ - Dropdown menus are now hidden when only a single choice is available
307
+ - Helpful message displayed when Graphviz is not available (when pressing `G`)
308
+ - Fixed warning in linestyle for curves
309
+
310
+ **Bug Fixes:**
311
+ - Fixed audio initialization order in Qt backend
312
+ - Fixed pytest failures for optional dependencies in CI
313
+ - Fixed various edge cases in error handling
314
+
315
+ **Migration of old context or global_params:**
316
+ - Use `global_params=SharedContext.injected()` to let code interpreters know that there's no need to pass this parameter.
317
+
318
+ ```python
319
+ # Before
320
+ def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params:dict =None):
321
+ global_params["brightness"] = brightness
322
+ return img * brightness
323
+
324
+ # Progressive migration
325
+ def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params:dict ==SharedContext.injected()):
326
+ global_params["brightness"] = brightness
327
+ return img * brightness
328
+
329
+ # Recommended version
330
+ from interactive_pipe import context
331
+ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
332
+ context.shared_brightness = brightness # shared with all filters
333
+ return img * brightness
334
+ ```
335
+
336
+
337
+
338
+
339
+ **License:**
340
+ - Updated to MIT License
341
+
342
+ ---
343
+
284
344
  ### History
285
- - Interactive pipe was initially developped by [Balthazar Neveu](https://github.com/balthazarneveu) as part of the [irdrone project](https://github.com/wisescootering/infrareddrone/tree/master/interactive) based on matplotlib.
345
+ - Interactive pipe was initially developed by [Balthazar Neveu](https://github.com/balthazarneveu) as part of the [irdrone project](https://github.com/wisescootering/infrareddrone/tree/master/interactive) based on matplotlib.
286
346
  - Later, more contributions were also made by [Giuseppe Moschetti](https://github.com/g-moschetti) and Sylvain Leroy.
287
347
  - August 2023: rewriting the whole core and supporting several graphical backends!
288
348
  - September 2024: Gradio backend
349
+ - January 2026: Clean Context API and code quality improvements (v0.8.8)
289
350
 
290
351
 
291
352
  ### FAQ
292
- - :question: Is there a difference between `global_params` and `context` ?
293
- > No, `global_params`, `global_parameters`, `global_state`, `global_context`, `context`, `state` all mean the same thing and are all supported for legacy reasons. `context` is the preferred wording.
353
+ - :question: What is the recommended way to access shared context?
354
+ > **New in v0.8.8**: Use the clean context API for direct access:
355
+ > ```python
356
+ > from interactive_pipe import context, layout, audio, get_context
357
+ >
358
+ > @interactive()
359
+ > def my_filter(img):
360
+ > context["shared_key"] = "shared_value" # Direct dict-like access
361
+ > context.brightness = 0.5
362
+ > layout.set_title("output_image", "My Image") # Layout helpers
363
+ >
364
+ > return img
365
+ > ```
366
+
367
+ - :question: How do I change the layout? *Can I change the grid layout of images live? (like you compare 2 images side by side and you want to start comparing 4 images in a 2x2 fashion for debugging purpose)*. It is possible with Qt backend.
368
+
369
+ > Use the `layout` helper to control image arrangement and styling:
370
+ > ```python
371
+ > from interactive_pipe import layout
372
+ >
373
+ > def change_layout(layout: str="side_by_side"):
374
+ > # Arrange outputs in a 2x2 grid
375
+ > if layout == "side_by_side":
376
+ > layout.grid([["input", "result"]])
377
+ > if layout == "grid2x2":
378
+ > layout.grid([["input", "processed"], ["histogram_graph", "result"]])
379
+ > # Style individual outputs
380
+ > layout.style("result", title="Final Result")
381
+ > # Note that the string "input", "processed", "histogram_graph", "result"
382
+ > # are the variables used in the pipeline (see below)!
383
+ >
384
+ > def pipeline(input):
385
+ > processed = denoise(input)
386
+ > result = change_brightness(processed)
387
+ > histogram_graph = compute_histo(processed)
388
+ > change_layout()
389
+ > return result
390
+ > ```
391
+
294
392
  - :question: Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
295
393
  > No, don't worry, these will be mapped back to regular sliders!
296
394
  - :question: How do I play audio live?
297
- > :sound: Inside a processing block, write the audio file to disk
298
- > and use `context["__set_audio"](audio_file)`
395
+ > :sound: Inside a processing block, write the audio file to disk and use the audio helper:
396
+ > ```python
397
+ > from interactive_pipe import audio
398
+ > audio.set_audio(audio_file) # New clean API (v0.8.8)
399
+ > # or legacy: context["__set_audio"](audio_file)
400
+ > ```
299
401
  - :question: Do I have to decorate my processing block using the `@interactive`
300
402
  > If you use the `@` decoration style, your function won't be useable in a regular manner (wich may be problematic in a serious development environment)
301
403
  ```python
@@ -328,7 +430,14 @@ def add_interactivity():
328
430
  # Don't do that!
329
431
  def bad_processing_block(inp):
330
432
  inp+=1
331
- ```
433
+ ```
434
+
435
+ - :question: Is there a difference between `global_params` and `context` ?
436
+ > No, `global_params`, `global_parameters`, `global_state`, `global_context`, `context`, `state` all mean the same thing and are all supported for legacy reasons. `context` is the preferred wording. However, we now recommend using the clean context API (see above).
437
+ > :warning: The old `global_params={}` / `context={}` keyword argument style still works for backwards compatibility but is deprecated.
438
+
439
+
440
+
332
441
  # Roadmap and todos
333
442
  :bug: Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
334
443
 
@@ -356,12 +465,36 @@ def bad_processing_block(inp):
356
465
 
357
466
  ![Ultra simplistic ISP](/doc/images/isp_pipeline.png)
358
467
 
468
+ ----------------------
469
+
470
+ ### Development
471
+
472
+ #### Code quality checks
473
+
474
+ Before committing, ensure your code passes the linters and tests. The CI runs these checks automatically:
475
+
476
+ **What CI does:**
477
+ - **Black formatting check** (`.github/workflows/formatting.yaml`): Runs `black --check` to verify code formatting
478
+ - **Flake8 linting** (`.github/workflows/flake8.yaml`): Runs `flake8` to check code quality
479
+ - **Pytest tests** (`.github/workflows/pytest.yaml`): Runs `pytest` on Python 3.9, 3.10, and 3.11
359
480
 
481
+ **Local commands (match CI):**
360
482
 
483
+ ```bash
484
+ # Install linting tools and test dependencies
485
+ pip install black flake8
486
+ pip install -e ".[pytest]"
361
487
 
488
+ # Check code formatting (Black) - matches CI
489
+ black --check .
490
+
491
+ # Auto-format code (Black) - run this if check fails
492
+ black .
493
+
494
+ # Run linting checks (flake8) - matches CI
495
+ flake8 .
496
+
497
+ # Run tests (pytest) - matches CI
498
+ pytest
499
+ ```
362
500
 
363
- #### :test_tube: Experimental features
364
- - Custom events on specific key press
365
- - Display the execution graph of the pipeline `G` key
366
- - [thirdparty/music](/src/interactive_pipe/thirdparty/music.py) Play audio (Qt backend only). Play songs on spotify (linux only) when the spotify app is running.
367
- - [thirdparty/images_openai_api](/src/interactive_pipe/thirdparty/images_openai_api.py) Generate images from prompt using OpenAI API image generation DALL-E Model (:dollar: paid service ~ 2cents/image)
@@ -1,11 +1,28 @@
1
- __version__ = "0.8.6"
1
+ __version__ = "0.8.8"
2
2
  from interactive_pipe.helper.pipeline_decorator import pipeline, interactive_pipeline
3
- from interactive_pipe.headless.control import Control, CircularControl, TextPrompt
3
+ from interactive_pipe.headless.control import (
4
+ Control,
5
+ CircularControl,
6
+ TextPrompt,
7
+ TimeControl,
8
+ )
4
9
  from interactive_pipe.data_objects.curves import Curve, SingleCurve
5
10
  from interactive_pipe.data_objects.image import Image
6
11
  from interactive_pipe.headless.keyboard import KeyboardControl
7
12
  from interactive_pipe.helper.filter_decorator import interactive, interact
8
13
 
14
+ # Clean context API - no more global_params pollution!
15
+ from interactive_pipe.core.context import (
16
+ get_context,
17
+ context, # Direct dict-like access
18
+ layout,
19
+ audio,
20
+ SharedContext, # For legacy code migration with explicit injection
21
+ )
22
+
23
+ # Clean exception handling
24
+ from interactive_pipe.core.engine import FilterError
25
+
9
26
  # Allowing more straightforward naming convention
10
27
  block = interactive
11
28
  pipeline = interactive_pipeline
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from copy import deepcopy
3
- from typing import Any
3
+ from typing import Any, Optional
4
4
 
5
5
 
6
6
  class CachedResults:
@@ -26,7 +26,7 @@ class CachedResults:
26
26
  Underlying class used in the interactive pipe cache mechanism.
27
27
  """
28
28
 
29
- def __init__(self, name: str = None, safe_buffer_deepcopy: bool = True):
29
+ def __init__(self, name: Optional[str] = None, safe_buffer_deepcopy: bool = True):
30
30
  self.name = name
31
31
  self.result = None
32
32
  self.state_change = StateChange(name=name)
@@ -68,7 +68,7 @@ class CachedResults:
68
68
  )
69
69
 
70
70
  def __repr__(self) -> str:
71
- return self.name
71
+ return self.name if self.name is not None else "CachedResults"
72
72
 
73
73
 
74
74
  class StateChange:
@@ -78,7 +78,7 @@ class StateChange:
78
78
  Underlying class used in the interactive pipe cache mechanism.
79
79
  """
80
80
 
81
- def __init__(self, name: str = None):
81
+ def __init__(self, name: Optional[str] = None):
82
82
  self.name = name
83
83
  self._stored_params = None
84
84
  self._update_needed = False
@@ -106,6 +106,7 @@ class StateChange:
106
106
  self._update_needed = value
107
107
 
108
108
  def __repr__(self) -> str:
109
- return f"{self.name}: " + (
109
+ name_str = self.name if self.name is not None else "StateChange"
110
+ return f"{name_str}: " + (
110
111
  "needs update" if self.update_needed else "no update needed"
111
112
  )