celldetective 1.4.1.post1__py3-none-any.whl → 1.5.0b0__py3-none-any.whl

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 (151) hide show
  1. celldetective/__init__.py +25 -0
  2. celldetective/__main__.py +62 -43
  3. celldetective/_version.py +1 -1
  4. celldetective/extra_properties.py +477 -399
  5. celldetective/filters.py +192 -97
  6. celldetective/gui/InitWindow.py +541 -411
  7. celldetective/gui/__init__.py +0 -15
  8. celldetective/gui/about.py +44 -39
  9. celldetective/gui/analyze_block.py +120 -84
  10. celldetective/gui/base/__init__.py +0 -0
  11. celldetective/gui/base/channel_norm_generator.py +335 -0
  12. celldetective/gui/base/components.py +249 -0
  13. celldetective/gui/base/feature_choice.py +92 -0
  14. celldetective/gui/base/figure_canvas.py +52 -0
  15. celldetective/gui/base/list_widget.py +133 -0
  16. celldetective/gui/{styles.py → base/styles.py} +92 -36
  17. celldetective/gui/base/utils.py +33 -0
  18. celldetective/gui/base_annotator.py +900 -767
  19. celldetective/gui/classifier_widget.py +642 -554
  20. celldetective/gui/configure_new_exp.py +777 -671
  21. celldetective/gui/control_panel.py +635 -524
  22. celldetective/gui/dynamic_progress.py +449 -0
  23. celldetective/gui/event_annotator.py +2023 -1662
  24. celldetective/gui/generic_signal_plot.py +1292 -944
  25. celldetective/gui/gui_utils.py +899 -1289
  26. celldetective/gui/interactions_block.py +658 -0
  27. celldetective/gui/interactive_timeseries_viewer.py +447 -0
  28. celldetective/gui/json_readers.py +48 -15
  29. celldetective/gui/layouts/__init__.py +5 -0
  30. celldetective/gui/layouts/background_model_free_layout.py +537 -0
  31. celldetective/gui/layouts/channel_offset_layout.py +134 -0
  32. celldetective/gui/layouts/local_correction_layout.py +91 -0
  33. celldetective/gui/layouts/model_fit_layout.py +372 -0
  34. celldetective/gui/layouts/operation_layout.py +68 -0
  35. celldetective/gui/layouts/protocol_designer_layout.py +96 -0
  36. celldetective/gui/pair_event_annotator.py +3130 -2435
  37. celldetective/gui/plot_measurements.py +586 -267
  38. celldetective/gui/plot_signals_ui.py +724 -506
  39. celldetective/gui/preprocessing_block.py +395 -0
  40. celldetective/gui/process_block.py +1678 -1831
  41. celldetective/gui/seg_model_loader.py +580 -473
  42. celldetective/gui/settings/__init__.py +0 -7
  43. celldetective/gui/settings/_cellpose_model_params.py +181 -0
  44. celldetective/gui/settings/_event_detection_model_params.py +95 -0
  45. celldetective/gui/settings/_segmentation_model_params.py +159 -0
  46. celldetective/gui/settings/_settings_base.py +77 -65
  47. celldetective/gui/settings/_settings_event_model_training.py +752 -526
  48. celldetective/gui/settings/_settings_measurements.py +1133 -964
  49. celldetective/gui/settings/_settings_neighborhood.py +574 -488
  50. celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
  51. celldetective/gui/settings/_settings_signal_annotator.py +329 -305
  52. celldetective/gui/settings/_settings_tracking.py +1304 -1094
  53. celldetective/gui/settings/_stardist_model_params.py +98 -0
  54. celldetective/gui/survival_ui.py +422 -312
  55. celldetective/gui/tableUI.py +1665 -1700
  56. celldetective/gui/table_ops/_maths.py +295 -0
  57. celldetective/gui/table_ops/_merge_groups.py +140 -0
  58. celldetective/gui/table_ops/_merge_one_hot.py +95 -0
  59. celldetective/gui/table_ops/_query_table.py +43 -0
  60. celldetective/gui/table_ops/_rename_col.py +44 -0
  61. celldetective/gui/thresholds_gui.py +382 -179
  62. celldetective/gui/viewers/__init__.py +0 -0
  63. celldetective/gui/viewers/base_viewer.py +700 -0
  64. celldetective/gui/viewers/channel_offset_viewer.py +331 -0
  65. celldetective/gui/viewers/contour_viewer.py +394 -0
  66. celldetective/gui/viewers/size_viewer.py +153 -0
  67. celldetective/gui/viewers/spot_detection_viewer.py +341 -0
  68. celldetective/gui/viewers/threshold_viewer.py +309 -0
  69. celldetective/gui/workers.py +304 -126
  70. celldetective/log_manager.py +92 -0
  71. celldetective/measure.py +1895 -1478
  72. celldetective/napari/__init__.py +0 -0
  73. celldetective/napari/utils.py +1025 -0
  74. celldetective/neighborhood.py +1914 -1448
  75. celldetective/preprocessing.py +1620 -1220
  76. celldetective/processes/__init__.py +0 -0
  77. celldetective/processes/background_correction.py +271 -0
  78. celldetective/processes/compute_neighborhood.py +894 -0
  79. celldetective/processes/detect_events.py +246 -0
  80. celldetective/processes/measure_cells.py +565 -0
  81. celldetective/processes/segment_cells.py +760 -0
  82. celldetective/processes/track_cells.py +435 -0
  83. celldetective/processes/train_segmentation_model.py +694 -0
  84. celldetective/processes/train_signal_model.py +265 -0
  85. celldetective/processes/unified_process.py +292 -0
  86. celldetective/regionprops/_regionprops.py +358 -317
  87. celldetective/relative_measurements.py +987 -710
  88. celldetective/scripts/measure_cells.py +313 -212
  89. celldetective/scripts/measure_relative.py +90 -46
  90. celldetective/scripts/segment_cells.py +165 -104
  91. celldetective/scripts/segment_cells_thresholds.py +96 -68
  92. celldetective/scripts/track_cells.py +198 -149
  93. celldetective/scripts/train_segmentation_model.py +324 -201
  94. celldetective/scripts/train_signal_model.py +87 -45
  95. celldetective/segmentation.py +844 -749
  96. celldetective/signals.py +3514 -2861
  97. celldetective/tracking.py +1332 -1011
  98. celldetective/utils/__init__.py +0 -0
  99. celldetective/utils/cellpose_utils/__init__.py +133 -0
  100. celldetective/utils/color_mappings.py +42 -0
  101. celldetective/utils/data_cleaning.py +630 -0
  102. celldetective/utils/data_loaders.py +450 -0
  103. celldetective/utils/dataset_helpers.py +207 -0
  104. celldetective/utils/downloaders.py +197 -0
  105. celldetective/utils/event_detection/__init__.py +8 -0
  106. celldetective/utils/experiment.py +1782 -0
  107. celldetective/utils/image_augmenters.py +308 -0
  108. celldetective/utils/image_cleaning.py +74 -0
  109. celldetective/utils/image_loaders.py +926 -0
  110. celldetective/utils/image_transforms.py +335 -0
  111. celldetective/utils/io.py +62 -0
  112. celldetective/utils/mask_cleaning.py +348 -0
  113. celldetective/utils/mask_transforms.py +5 -0
  114. celldetective/utils/masks.py +184 -0
  115. celldetective/utils/maths.py +351 -0
  116. celldetective/utils/model_getters.py +325 -0
  117. celldetective/utils/model_loaders.py +296 -0
  118. celldetective/utils/normalization.py +380 -0
  119. celldetective/utils/parsing.py +465 -0
  120. celldetective/utils/plots/__init__.py +0 -0
  121. celldetective/utils/plots/regression.py +53 -0
  122. celldetective/utils/resources.py +34 -0
  123. celldetective/utils/stardist_utils/__init__.py +104 -0
  124. celldetective/utils/stats.py +90 -0
  125. celldetective/utils/types.py +21 -0
  126. {celldetective-1.4.1.post1.dist-info → celldetective-1.5.0b0.dist-info}/METADATA +1 -1
  127. celldetective-1.5.0b0.dist-info/RECORD +187 -0
  128. {celldetective-1.4.1.post1.dist-info → celldetective-1.5.0b0.dist-info}/WHEEL +1 -1
  129. tests/gui/test_new_project.py +129 -117
  130. tests/gui/test_project.py +127 -79
  131. tests/test_filters.py +39 -15
  132. tests/test_notebooks.py +8 -0
  133. tests/test_tracking.py +425 -144
  134. tests/test_utils.py +123 -77
  135. celldetective/gui/base_components.py +0 -23
  136. celldetective/gui/layouts.py +0 -1602
  137. celldetective/gui/processes/compute_neighborhood.py +0 -594
  138. celldetective/gui/processes/measure_cells.py +0 -360
  139. celldetective/gui/processes/segment_cells.py +0 -499
  140. celldetective/gui/processes/track_cells.py +0 -303
  141. celldetective/gui/processes/train_segmentation_model.py +0 -270
  142. celldetective/gui/processes/train_signal_model.py +0 -108
  143. celldetective/gui/table_ops/merge_groups.py +0 -118
  144. celldetective/gui/viewers.py +0 -1354
  145. celldetective/io.py +0 -3663
  146. celldetective/utils.py +0 -3108
  147. celldetective-1.4.1.post1.dist-info/RECORD +0 -123
  148. /celldetective/{gui/processes → processes}/downloader.py +0 -0
  149. {celldetective-1.4.1.post1.dist-info → celldetective-1.5.0b0.dist-info}/entry_points.txt +0 -0
  150. {celldetective-1.4.1.post1.dist-info → celldetective-1.5.0b0.dist-info}/licenses/LICENSE +0 -0
  151. {celldetective-1.4.1.post1.dist-info → celldetective-1.5.0b0.dist-info}/top_level.txt +0 -0
tests/gui/test_project.py CHANGED
@@ -1,99 +1,147 @@
1
1
  import pytest
2
2
  from PyQt5 import QtCore
3
+
4
+ import celldetective.gui.preprocessing_block
3
5
  from celldetective.gui.InitWindow import AppInitWindow
4
- from celldetective.utils import get_software_location
6
+ from celldetective import get_software_location
5
7
  import os
6
8
 
7
9
  software_location = get_software_location()
8
10
 
11
+
9
12
  @pytest.fixture
10
13
  def app(qtbot):
11
- test_app = AppInitWindow(software_location=software_location)
12
- qtbot.addWidget(test_app)
13
- return test_app
14
+ test_app = AppInitWindow(software_location=software_location)
15
+ qtbot.addWidget(test_app)
16
+ return test_app
17
+
14
18
 
15
19
  def test_open_project(app, qtbot):
16
- app.experiment_path_selection.setText(software_location + os.sep + 'examples/demo')
17
- qtbot.mouseClick(app.validate_button, QtCore.Qt.LeftButton)
18
- qtbot.wait(10000)
20
+ app.experiment_path_selection.setText(software_location + os.sep + "examples/demo")
21
+ qtbot.mouseClick(app.validate_button, QtCore.Qt.LeftButton)
22
+ qtbot.wait(10000)
23
+
19
24
 
20
25
  def test_launch_demo(app, qtbot):
21
- app.experiment_path_selection.setText(software_location + os.sep + 'examples/demo')
22
- qtbot.mouseClick(app.validate_button, QtCore.Qt.LeftButton)
26
+ app.experiment_path_selection.setText(software_location + os.sep + "examples/demo")
27
+ qtbot.mouseClick(app.validate_button, QtCore.Qt.LeftButton)
28
+
23
29
 
24
30
  def test_preprocessing_panel(app, qtbot):
25
31
 
26
- app.experiment_path_selection.setText(software_location + os.sep + 'examples/demo')
27
- qtbot.mouseClick(app.validate_button, QtCore.Qt.LeftButton)
32
+ app.experiment_path_selection.setText(software_location + os.sep + "examples/demo")
33
+ qtbot.mouseClick(app.validate_button, QtCore.Qt.LeftButton)
34
+
35
+ qtbot.mouseClick(
36
+ app.control_panel.PreprocessingPanel.collapse_btn,
37
+ QtCore.Qt.LeftButton,
38
+ )
39
+ qtbot.mouseClick(
40
+ app.control_panel.PreprocessingPanel.fit_correction_layout.add_correction_btn,
41
+ QtCore.Qt.LeftButton,
42
+ )
43
+ qtbot.mouseClick(
44
+ app.control_panel.PreprocessingPanel.collapse_btn,
45
+ QtCore.Qt.LeftButton,
46
+ )
28
47
 
29
- qtbot.mouseClick(app.control_panel.PreprocessingPanel.collapse_btn, QtCore.Qt.LeftButton)
30
- qtbot.mouseClick(app.control_panel.PreprocessingPanel.fit_correction_layout.add_correction_btn, QtCore.Qt.LeftButton)
31
- qtbot.mouseClick(app.control_panel.PreprocessingPanel.collapse_btn, QtCore.Qt.LeftButton)
32
48
 
33
49
  def test_app(app, qtbot):
34
50
 
35
- # Set an experiment folder and open
36
- app.experiment_path_selection.setText(os.sep.join([software_location,'examples','demo']))
37
- qtbot.mouseClick(app.validate_button, QtCore.Qt.LeftButton)
38
-
39
- # Set a position
40
- #app.control_panel.position_list.setCurrentIndex(0)
41
- #app.control_panel.update_position_options()
42
-
43
- # View stacl
44
- qtbot.mouseClick(app.control_panel.view_stack_btn, QtCore.Qt.LeftButton)
45
- #qtbot.wait(1000)
46
- app.control_panel.viewer.close()
47
-
48
- # Expand process block
49
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].collapse_btn, QtCore.Qt.LeftButton)
50
-
51
- # Use Threshold Config Wizard
52
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].upload_model_btn, QtCore.Qt.LeftButton)
53
- qtbot.wait(1000)
54
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].SegModelLoader.threshold_config_button, QtCore.Qt.LeftButton)
55
- app.control_panel.ProcessPopulations[0].SegModelLoader.ThreshWizard.close()
56
- app.control_panel.ProcessPopulations[0].SegModelLoader.close()
57
-
58
- # Check segmentation with napari
59
- #qtbot.mouseClick(app.control_panel.ProcessEffectors.check_seg_btn, QtCore.Qt.LeftButton)
60
- # close napari?
61
-
62
- # Train model
63
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].train_btn, QtCore.Qt.LeftButton)
64
- qtbot.wait(1000)
65
- app.control_panel.ProcessPopulations[0].settings_segmentation_training.close()
66
-
67
- # Config tracking
68
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].track_config_btn, QtCore.Qt.LeftButton)
69
- qtbot.wait(1000)
70
- app.control_panel.ProcessPopulations[0].settings_tracking.close()
71
-
72
- # Config measurements
73
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].measurements_config_btn, QtCore.Qt.LeftButton)
74
- qtbot.wait(1000)
75
- app.control_panel.ProcessPopulations[0].settings_measurements.close()
76
-
77
- # Classifier widget
78
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].classify_btn, QtCore.Qt.LeftButton)
79
- qtbot.wait(1000)
80
- app.control_panel.ProcessPopulations[0].ClassifierWidget.close()
81
-
82
- # Config signal annotator
83
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].config_signal_annotator_btn, QtCore.Qt.LeftButton)
84
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].settings_signal_annotator.rgb_btn, QtCore.Qt.LeftButton)
85
- qtbot.wait(1000)
86
- app.control_panel.ProcessPopulations[0].settings_signal_annotator.close()
87
-
88
- # Signal annotator widget
89
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].check_signals_btn, QtCore.Qt.LeftButton)
90
- qtbot.wait(1000)
91
- app.control_panel.ProcessPopulations[0].event_annotator.close()
92
-
93
- # Table widget
94
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].view_tab_btn, QtCore.Qt.LeftButton)
95
- qtbot.wait(1000)
96
- app.control_panel.ProcessPopulations[0].tab_ui.close()
97
-
98
- #qtbot.mouseClick(app.control_panel.PreprocessingPanel.fit_correction_layout.add_correction_btn, QtCore.Qt.LeftButton)
99
- qtbot.mouseClick(app.control_panel.ProcessPopulations[0].collapse_btn, QtCore.Qt.LeftButton)
51
+ # Set an experiment folder and open
52
+ app.experiment_path_selection.setText(
53
+ os.sep.join([software_location, "examples", "demo"])
54
+ )
55
+ qtbot.mouseClick(app.validate_button, QtCore.Qt.LeftButton)
56
+
57
+ # Set a position
58
+ # app.control_panel.position_list.setCurrentIndex(0)
59
+ # app.control_panel.update_position_options()
60
+
61
+ # View stacl
62
+ qtbot.mouseClick(app.control_panel.view_stack_btn, QtCore.Qt.LeftButton)
63
+ # qtbot.wait(1000)
64
+ app.control_panel.viewer.close()
65
+
66
+ # Expand process block
67
+ qtbot.mouseClick(
68
+ app.control_panel.ProcessPopulations[0].collapse_btn, QtCore.Qt.LeftButton
69
+ )
70
+
71
+ # Use Threshold Config Wizard
72
+ qtbot.mouseClick(
73
+ app.control_panel.ProcessPopulations[0].upload_model_btn, QtCore.Qt.LeftButton
74
+ )
75
+ qtbot.wait(1000)
76
+ qtbot.mouseClick(
77
+ app.control_panel.ProcessPopulations[
78
+ 0
79
+ ].seg_model_loader.threshold_config_button,
80
+ QtCore.Qt.LeftButton,
81
+ )
82
+ app.control_panel.ProcessPopulations[0].seg_model_loader.thresh_wizard.close()
83
+ app.control_panel.ProcessPopulations[0].seg_model_loader.close()
84
+
85
+ # Check segmentation with napari
86
+ # qtbot.mouseClick(app.control_panel.ProcessEffectors.check_seg_btn, QtCore.Qt.LeftButton)
87
+ # close napari?
88
+
89
+ # Train model
90
+ qtbot.mouseClick(
91
+ app.control_panel.ProcessPopulations[0].train_btn, QtCore.Qt.LeftButton
92
+ )
93
+ qtbot.wait(1000)
94
+ app.control_panel.ProcessPopulations[0].settings_segmentation_training.close()
95
+
96
+ # Config tracking
97
+ qtbot.mouseClick(
98
+ app.control_panel.ProcessPopulations[0].track_config_btn, QtCore.Qt.LeftButton
99
+ )
100
+ qtbot.wait(1000)
101
+ app.control_panel.ProcessPopulations[0].settings_tracking.close()
102
+
103
+ # Config measurements
104
+ qtbot.mouseClick(
105
+ app.control_panel.ProcessPopulations[0].measurements_config_btn,
106
+ QtCore.Qt.LeftButton,
107
+ )
108
+ qtbot.wait(1000)
109
+ app.control_panel.ProcessPopulations[0].settings_measurements.close()
110
+
111
+ # Classifier widget
112
+ qtbot.mouseClick(
113
+ app.control_panel.ProcessPopulations[0].classify_btn, QtCore.Qt.LeftButton
114
+ )
115
+ qtbot.wait(1000)
116
+ app.control_panel.ProcessPopulations[0].classifier_widget.close()
117
+
118
+ # Config signal annotator
119
+ qtbot.mouseClick(
120
+ app.control_panel.ProcessPopulations[0].config_signal_annotator_btn,
121
+ QtCore.Qt.LeftButton,
122
+ )
123
+ qtbot.mouseClick(
124
+ app.control_panel.ProcessPopulations[0].settings_signal_annotator.rgb_btn,
125
+ QtCore.Qt.LeftButton,
126
+ )
127
+ qtbot.wait(1000)
128
+ app.control_panel.ProcessPopulations[0].settings_signal_annotator.close()
129
+
130
+ # Signal annotator widget
131
+ qtbot.mouseClick(
132
+ app.control_panel.ProcessPopulations[0].check_signals_btn, QtCore.Qt.LeftButton
133
+ )
134
+ qtbot.wait(1000)
135
+ app.control_panel.ProcessPopulations[0].event_annotator.close()
136
+
137
+ # Table widget
138
+ qtbot.mouseClick(
139
+ app.control_panel.ProcessPopulations[0].view_tab_btn, QtCore.Qt.LeftButton
140
+ )
141
+ qtbot.wait(1000)
142
+ app.control_panel.ProcessPopulations[0].tab_ui.close()
143
+
144
+ # qtbot.mouseClick(app.control_panel.PreprocessingPanel.fit_correction_layout.add_correction_btn, QtCore.Qt.LeftButton)
145
+ qtbot.mouseClick(
146
+ app.control_panel.ProcessPopulations[0].collapse_btn, QtCore.Qt.LeftButton
147
+ )
tests/test_filters.py CHANGED
@@ -1,24 +1,48 @@
1
1
  import unittest
2
2
  import numpy as np
3
- from celldetective.filters import gauss_filter, abs_filter
3
+ from celldetective.filters import gauss_filter, abs_filter, filter_image
4
4
 
5
5
 
6
6
  class TestFilters(unittest.TestCase):
7
7
 
8
- @classmethod
9
- def setUpClass(self):
10
- self.img = np.ones((256,256),dtype=int)
11
- self.img[100:110,100:110] = 0
12
- self.gauss_sigma = 1.6
8
+ @classmethod
9
+ def setUpClass(self):
10
+ self.img = np.ones((256, 256), dtype=int)
11
+ self.img[100:110, 100:110] = 0
12
+ self.gauss_sigma = 1.6
13
13
 
14
- def test_gauss_filter_is_float(self):
15
- self.assertIsInstance(gauss_filter(self.img, self.gauss_sigma)[0,0], float)
16
-
17
- def test_gauss_filter_has_same_shape(self):
18
- self.assertEqual(gauss_filter(self.img, self.gauss_sigma).shape, self.img.shape)
14
+ def test_gauss_filter_is_float(self):
15
+ self.assertIsInstance(gauss_filter(self.img, self.gauss_sigma)[0, 0], float)
19
16
 
20
- def test_abs_filter_is_positive(self):
21
- self.assertTrue(np.all(abs_filter(self.img) >= 0.))
17
+ def test_gauss_filter_has_same_shape(self):
18
+ self.assertEqual(gauss_filter(self.img, self.gauss_sigma).shape, self.img.shape)
22
19
 
23
- if __name__=="__main__":
24
- unittest.main()
20
+ def test_abs_filter_is_positive(self):
21
+ self.assertTrue(np.all(abs_filter(self.img) >= 0.0))
22
+
23
+ def test_filter_image_none(self):
24
+ # Should return original image if filters is None
25
+ res = filter_image(self.img, filters=None)
26
+ np.testing.assert_array_equal(res, self.img)
27
+
28
+ def test_filter_image_single(self):
29
+ # Test with a single filter: e.g. abs
30
+ # Create an image with negatives
31
+ img_neg = self.img.copy() * -1
32
+ res = filter_image(img_neg, filters=[("abs",)])
33
+ self.assertTrue(np.all(res >= 0))
34
+ np.testing.assert_array_almost_equal(res, np.abs(img_neg))
35
+
36
+ def test_filter_image_chain(self):
37
+ # Test chaining: subtract 10 then abs
38
+ # Start with ones. Subtract 10 -> -9. Abs -> 9.
39
+ img = np.ones((5, 5), dtype=float)
40
+ filters = [("subtract", 10), ("abs",)]
41
+ res = filter_image(img, filters=filters)
42
+ expected = np.abs(img - 10)
43
+ np.testing.assert_array_almost_equal(res, expected)
44
+ self.assertTrue(np.allclose(res, 9.0))
45
+
46
+
47
+ if __name__ == "__main__":
48
+ unittest.main()
@@ -0,0 +1,8 @@
1
+ # test_notebooks.py
2
+ import nbformat
3
+ from nbclient import NotebookClient
4
+
5
+ def test_notebook_runs():
6
+ nb = nbformat.read("../demos/ADCC_analysis_demo.ipynb", as_version=4)
7
+ client = NotebookClient(nb, timeout=600, kernel_name="python3")
8
+ client.execute() # raises exception if any cell fails