maidr 1.2.1__py3-none-any.whl → 1.3.0__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.
maidr/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.2.1"
1
+ __version__ = "1.3.0"
2
2
 
3
3
  from .api import close, render, save_html, show, stacked
4
4
  from .core import Maidr
maidr/core/maidr.py CHANGED
@@ -8,7 +8,7 @@ import os
8
8
  import tempfile
9
9
  import uuid
10
10
  import webbrowser
11
- from typing import Any, Literal
11
+ from typing import Any, Literal, cast
12
12
 
13
13
  import matplotlib.pyplot as plt
14
14
  from htmltools import HTML, HTMLDocument, Tag, tags
@@ -102,14 +102,20 @@ class Maidr:
102
102
  The renderer to use for the HTML preview.
103
103
  """
104
104
  html = self._create_html_tag(use_iframe=True) # Always use iframe for display
105
- _renderer = Environment.get_renderer()
106
- if _renderer == "browser" or (
107
- Environment.is_interactive_shell() and not Environment.is_notebook()
108
- ):
105
+
106
+ # Use the passed renderer parameter, fallback to auto-detection
107
+ if renderer == "auto":
108
+ _renderer = cast(Literal["ipython", "browser"], Environment.get_renderer())
109
+ else:
110
+ _renderer = renderer
111
+
112
+ # Only try browser opening if explicitly requested as browser and not in notebook
113
+ if _renderer == "browser" and not Environment.is_notebook():
109
114
  return self._open_plot_in_browser()
115
+
110
116
  if clear_fig:
111
117
  plt.close()
112
- return html.show(renderer)
118
+ return html.show(_renderer)
113
119
 
114
120
  def clear(self):
115
121
  self._plots = []
@@ -8,6 +8,7 @@ from maidr.core.enum.plot_type import PlotType
8
8
  from maidr.core.plot.maidr_plot import MaidrPlot
9
9
  from maidr.exception.extraction_error import ExtractionError
10
10
  from maidr.util.mixin.extractor_mixin import LineExtractorMixin
11
+ import uuid
11
12
 
12
13
 
13
14
  class MultiLinePlot(MaidrPlot, LineExtractorMixin):
@@ -42,62 +43,75 @@ class MultiLinePlot(MaidrPlot, LineExtractorMixin):
42
43
  super().__init__(ax, PlotType.LINE)
43
44
 
44
45
  def _get_selector(self) -> Union[str, List[str]]:
45
- return ["g[maidr='true'] > path"]
46
+ # Return selectors for all lines that have data
47
+ all_lines = self.ax.get_lines()
48
+ if not all_lines:
49
+ return ["g[maidr='true'] > path"]
50
+
51
+ selectors = []
52
+ for line in all_lines:
53
+ # Only create selectors for lines that have data (same logic as _extract_line_data)
54
+ xydata = line.get_xydata()
55
+ if xydata is None or not xydata.size: # type: ignore
56
+ continue
57
+ gid = line.get_gid()
58
+ if gid:
59
+ selectors.append(f"g[id='{gid}'] path")
60
+ else:
61
+ selectors.append("g[maidr='true'] > path")
62
+
63
+ if not selectors:
64
+ return ["g[maidr='true'] > path"]
65
+
66
+ return selectors
46
67
 
47
- def _extract_plot_data(self) -> list[dict]:
48
- plot = self.extract_lines(self.ax)
49
- data = self._extract_line_data(plot)
68
+ def _extract_plot_data(self) -> Union[List[List[dict]], None]:
69
+ data = self._extract_line_data()
50
70
 
51
71
  if data is None:
52
- raise ExtractionError(self.type, plot)
72
+ raise ExtractionError(self.type, None)
53
73
 
54
74
  return data
55
75
 
56
- def _extract_line_data(
57
- self, plot: Union[List[Line2D], None]
58
- ) -> Union[List[dict], None]:
76
+ def _extract_line_data(self) -> Union[List[List[dict]], None]:
59
77
  """
60
- Extract data from multiple line objects.
61
-
62
- Parameters
63
- ----------
64
- plot : list[Line2D] | None
65
- List of Line2D objects to extract data from.
78
+ Extract data from all line objects and return as separate arrays.
66
79
 
67
80
  Returns
68
81
  -------
69
- list[dict] | None
70
- List of dictionaries containing x,y coordinates and line identifiers,
71
- or None if the plot data is invalid.
82
+ list[list[dict]] | None
83
+ List of lists, where each inner list contains dictionaries with x,y coordinates
84
+ and line identifiers for one line, or None if the plot data is invalid.
72
85
  """
73
- if plot is None or len(plot) == 0:
86
+ all_lines = self.ax.get_lines()
87
+ if not all_lines:
74
88
  return None
75
89
 
76
- all_line_data = []
90
+ all_lines_data = []
77
91
 
78
- # Process each line in the plot
79
- for i, line in enumerate(plot):
80
- if line.get_xydata() is None:
92
+ for line in all_lines:
93
+ xydata = line.get_xydata()
94
+ if xydata is None or not xydata.size: # type: ignore
81
95
  continue
82
96
 
83
- # Tag the element for highlighting
84
97
  self._elements.append(line)
85
98
 
86
- # Extract data from this line
99
+ # Assign unique GID to each line if not already set
100
+ if line.get_gid() is None:
101
+ unique_gid = f"maidr-{uuid.uuid4()}"
102
+ line.set_gid(unique_gid)
87
103
 
88
104
  label: str = line.get_label() # type: ignore
89
105
  line_data = [
90
106
  {
91
107
  MaidrKey.X: float(x),
92
108
  MaidrKey.Y: float(y),
93
- # Replace labels starting with '_child'
94
- # with an empty string to exclude
95
- # internal or non-relevant labels from being used as identifiers.
96
109
  MaidrKey.FILL: (label if not label.startswith("_child") else ""),
97
110
  }
98
111
  for x, y in line.get_xydata() # type: ignore
99
112
  ]
100
- if len(line_data) > 0:
101
- all_line_data.append(line_data)
102
113
 
103
- return all_line_data if all_line_data else None
114
+ if line_data:
115
+ all_lines_data.append(line_data)
116
+
117
+ return all_lines_data if all_lines_data else None
maidr/patch/lineplot.py CHANGED
@@ -7,29 +7,39 @@ from matplotlib.lines import Line2D
7
7
  from maidr.core.enum import PlotType
8
8
  from maidr.patch.common import common
9
9
  from maidr.core.enum.smooth_keywords import SMOOTH_KEYWORDS
10
+ from maidr.core.context_manager import ContextManager
11
+ from maidr.core.figure_manager import FigureManager
10
12
 
11
13
 
12
14
  def line(wrapped, instance, args, kwargs) -> Axes | list[Line2D]:
13
15
  """
14
- Wrapper function for line plotting functions in matplotlib and seaborn.
15
-
16
- Parameters
17
- ----------
18
- wrapped : callable
19
- The wrapped function (plot or lineplot)
20
- instance : object
21
- The object to which the wrapped function belongs
22
- args : tuple
23
- Positional arguments passed to the wrapped function
24
- kwargs : dict
25
- Keyword arguments passed to the wrapped function
26
-
27
- Returns
28
- -------
29
- Axes | list[Line2D]
30
- The result of the wrapped function after processing
16
+ Wrapper for line plotting functions that creates a single MAIDR plot per axes to handle
17
+ multiline plots (matplotlib) and single-call plots (seaborn) correctly by preventing
18
+ multiple MAIDR layers and using internal context to avoid cyclic processing.
31
19
  """
32
- return common(PlotType.LINE, wrapped, instance, args, kwargs)
20
+ # Don't proceed if the call is made internally by the patched function.
21
+ if ContextManager.is_internal_context():
22
+ return wrapped(*args, **kwargs)
23
+
24
+ # Set the internal context to avoid cyclic processing.
25
+ with ContextManager.set_internal_context():
26
+ # Patch the plotting function.
27
+ plot = wrapped(*args, **kwargs)
28
+
29
+ # Get the axes from the plot result (works for both matplotlib and seaborn)
30
+ ax = FigureManager.get_axes(plot)
31
+ if ax is None:
32
+ # If we can't get axes from plot, try from instance
33
+ ax = instance if isinstance(instance, Axes) else getattr(instance, "axes", None)
34
+
35
+ # Check if a MAIDR plot already exists for this axes
36
+ if ax is not None and not hasattr(ax, "_maidr_plot_created"):
37
+ # Create MAIDR plot only once for this axes using common()
38
+ common(PlotType.LINE, lambda *a, **k: plot, instance, args, kwargs)
39
+ # Mark that a MAIDR plot has been created for this axes
40
+ setattr(ax, "_maidr_plot_created", True)
41
+
42
+ return plot
33
43
 
34
44
 
35
45
  # Patch matplotlib function.
maidr/util/environment.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import os
3
+ import sys
3
4
 
4
5
 
5
6
  class Environment:
@@ -24,10 +25,19 @@ class Environment:
24
25
  try:
25
26
  from IPython import get_ipython # type: ignore
26
27
 
27
- return get_ipython() is not None and (
28
- "ipykernel" in str(get_ipython())
29
- or "google.colab" in str(get_ipython())
30
- )
28
+ ipy = get_ipython()
29
+ if ipy is not None:
30
+ # Check for Pyodide/JupyterLite specific indicators
31
+ ipy_str = str(ipy).lower()
32
+ if "pyodide" in ipy_str or "jupyterlite" in ipy_str:
33
+ return True
34
+ # Check for other notebook indicators
35
+ if "ipykernel" in str(ipy) or "google.colab" in str(ipy):
36
+ return True
37
+ # Check for Pyodide platform
38
+ if sys.platform == "emscripten":
39
+ return True
40
+ return False
31
41
  except ImportError:
32
42
  return False
33
43
 
@@ -61,10 +71,19 @@ class Environment:
61
71
  ipy = ( # pyright: ignore[reportUnknownVariableType]
62
72
  IPython.get_ipython() # pyright: ignore[reportUnknownMemberType, reportPrivateImportUsage]
63
73
  )
64
- renderer = "ipython" if ipy else "browser"
74
+ if ipy is not None:
75
+ # Check for Pyodide/JupyterLite
76
+ ipy_str = str(ipy).lower()
77
+ if "pyodide" in ipy_str or "jupyterlite" in ipy_str:
78
+ return "ipython"
79
+ # Check for Pyodide platform
80
+ if sys.platform == "emscripten":
81
+ return "ipython"
82
+ return "ipython"
83
+ else:
84
+ return "browser"
65
85
  except ImportError:
66
- renderer = "browser"
67
- return renderer
86
+ return "browser"
68
87
 
69
88
  @staticmethod
70
89
  def initialize_llm_secrets(unique_id: str) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: maidr
3
- Version: 1.2.1
3
+ Version: 1.3.0
4
4
  Summary: Multimodal Access and Interactive Data Representations
5
5
  License: GPL-3.0-or-later
6
6
  Keywords: accessibility,visualization,sonification,braille,tactile,multimodal,data representation,blind,low vision,visual impairments
@@ -26,14 +26,11 @@ Classifier: Topic :: Scientific/Engineering :: Visualization
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
27
  Classifier: Topic :: Text Processing :: Markup :: HTML
28
28
  Requires-Dist: htmltools (>=0.5)
29
- Requires-Dist: jupyter (>=1.0.0,<2.0.0)
30
29
  Requires-Dist: lxml (>=5.1.0)
31
30
  Requires-Dist: matplotlib (>=3.8)
32
31
  Requires-Dist: mplfinance (>=0.12.10b0,<0.13.0)
33
32
  Requires-Dist: numpy (>=1.26)
34
33
  Requires-Dist: seaborn (>=0.12)
35
- Requires-Dist: statsmodels (>=0.14.4,<0.15.0)
36
- Requires-Dist: virtualenv (>=20.26.6,<21.0.0)
37
34
  Requires-Dist: wrapt (>=1.16.0,<2.0.0)
38
35
  Project-URL: Homepage, https://xability.github.io/py-maidr
39
36
  Project-URL: Repository, https://github.com/xability/py-maidr
@@ -1,4 +1,4 @@
1
- maidr/__init__.py,sha256=BD-1CK6PLPM8AoGLiLzB7ok1Gn_7LLWv0Z-iwaP5CGE,399
1
+ maidr/__init__.py,sha256=q3J6Xlb_5wM0iRIYZnMkvD0ValcEFxltt6-kbGrRKR4,399
2
2
  maidr/api.py,sha256=F43mXWsxc7tHdlZqbRlEWkc-RjJVo_zgxCn3NiLBY58,1764
3
3
  maidr/core/__init__.py,sha256=WgxLpSEYMc4k3OyEOf1shOxfEq0ASzppEIZYmE91ThQ,25
4
4
  maidr/core/context_manager.py,sha256=6cT7ZGOApSpC-SLD2XZWWU_H08i-nfv-JUlzXOtvWYw,3374
@@ -8,7 +8,7 @@ maidr/core/enum/maidr_key.py,sha256=ljG0omqzd8K8Yk213N7i7PXGvG-IOlnE5v7o6RoGJzc,
8
8
  maidr/core/enum/plot_type.py,sha256=7Orx3b_0NdpI_PtVJfLyJPh4qBqYMTsYBBr8VwOtiAM,347
9
9
  maidr/core/enum/smooth_keywords.py,sha256=VlpIX1BaoX8efwIrT72GIptxguTpiPtJvvJUPMoaFSQ,194
10
10
  maidr/core/figure_manager.py,sha256=jXs-Prkeru1Pahj21hjh8BAwXM9ZFUZ3GFfKUfIRX_M,4117
11
- maidr/core/maidr.py,sha256=t0o8dCoIcj6Oa6HGktPM7l9EN8pplvZX-Xdtp8hg19U,13996
11
+ maidr/core/maidr.py,sha256=kpN5axfJnWyaqXvyA1maioPOFh1rqod3XIvhIefcjOs,14225
12
12
  maidr/core/plot/__init__.py,sha256=xDIpRGM-4DfaSSL3nKcXrjdMecCHJ6en4K4nA_fPefQ,83
13
13
  maidr/core/plot/barplot.py,sha256=1HfoqyDGKIXkYQnCHN83Ye_faKpNQ3R4wjlbjD5jUyk,2092
14
14
  maidr/core/plot/boxplot.py,sha256=i11GdNuz_c-hilmhydu3ah-bzyVdFoBkNvRi5lpMrrY,9946
@@ -16,7 +16,7 @@ maidr/core/plot/candlestick.py,sha256=8YKjNmxJx7TEvaH5v4lYDzA11VHpVHpZrv9EJG2Ter
16
16
  maidr/core/plot/grouped_barplot.py,sha256=bRcQcvwkF3Q3aZ3PlhbZ6bHI_AfcqdKUMVvlLL94wXM,2078
17
17
  maidr/core/plot/heatmap.py,sha256=yMS-31tS2GW4peds9LtZesMxmmTV_YfqYO5M_t5KasQ,2521
18
18
  maidr/core/plot/histogram.py,sha256=QV5W-6ZJQQcZsrM91JJBX-ONktJzH7yg_et5_bBPfQQ,1525
19
- maidr/core/plot/lineplot.py,sha256=_Sg290rk5h1LvtdaY_VKraTLXyiXGi58t-WxLFht5Ds,3331
19
+ maidr/core/plot/lineplot.py,sha256=C3xz6uWXYM_mbTq_geb5bP0JdvhcQf6cpfTs78Y6fCM,3852
20
20
  maidr/core/plot/maidr_plot.py,sha256=B6hjsu-jSWlevEqJawgwjMOJr51nBBNh7yqJdSTkNhw,3681
21
21
  maidr/core/plot/maidr_plot_factory.py,sha256=5SC8Nc3IfVYn-jQU_SD9vw7R5yhTHTBgQwGnAc7_DmA,2507
22
22
  maidr/core/plot/regplot.py,sha256=b7u6bGTz1IxKahplNUrfwIr_OGSwMJ2BuLgFAVjL0s0,2744
@@ -33,12 +33,12 @@ maidr/patch/heatmap.py,sha256=uxLLg530Ql9KVC5rxk8vnwPHXBWWHwYgJRkyHY-tJzs,1048
33
33
  maidr/patch/highlight.py,sha256=I1dGFHJAnVd0AHVnMJzk_TE8BC8Uv-I6fTzSrJLU5QM,1155
34
34
  maidr/patch/histogram.py,sha256=k3N0RUf1SQ2402pwbaY5QyS98KnLWvr9glCHQw9NTko,2378
35
35
  maidr/patch/kdeplot.py,sha256=qv-OKzuop2aTrkZgUe2OnLxvV-KMyeXt1Td0_uZeHzE,2338
36
- maidr/patch/lineplot.py,sha256=BCc3tyTrQbdLPUDWIO7hu4h1W71JRPSt2d4tYP54Li4,1085
36
+ maidr/patch/lineplot.py,sha256=og42V0tWBKCnf6idT3pLsIj3QBvKjg8aUN-k1udPRVw,1901
37
37
  maidr/patch/regplot.py,sha256=Ciz43C5XZfWK6wtVWrlV0WNz4R__rcgdqVE9OCaXXRk,3236
38
38
  maidr/patch/scatterplot.py,sha256=kln6zZwjVsdQzICalo-RnBOJrx1BnIB2xYUwItHvSNY,525
39
39
  maidr/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  maidr/util/dedup_utils.py,sha256=RpgPL5p-3oULUHaTCZJaQKhPHfyPkvBLHMt8lAGpJ5A,438
41
- maidr/util/environment.py,sha256=NJzbcFUCIk7OF29eIae8jyHax9p8fQgFLmxM6Af0fUY,4465
41
+ maidr/util/environment.py,sha256=-2LyZUpHojBCMEbkr_xkcC-_IDqtGDALB8683v7kTdI,5253
42
42
  maidr/util/mixin/__init__.py,sha256=aGJZNhtWh77yIVPc7ipIZm1OajigjMtCWYKPuDWTC-c,217
43
43
  maidr/util/mixin/extractor_mixin.py,sha256=oHtwpmS5kARvaLrSO3DKTPQxyFUw9nOcKN7rzTj1q4g,5192
44
44
  maidr/util/mixin/merger_mixin.py,sha256=V0qLw_6DUB7X6CQ3BCMpsCQX_ZuwAhoSTm_E4xAJFKM,712
@@ -46,7 +46,7 @@ maidr/util/regression_line_utils.py,sha256=P8RQLixTby2JLz73XZgNiu96C2Ct3pNe4ENRW
46
46
  maidr/util/svg_utils.py,sha256=2gyzBtNKFHs0utrw1iOlxTmznzivOWQMV2aW8zu2c8E,1442
47
47
  maidr/widget/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
48
  maidr/widget/shiny.py,sha256=wrrw2KAIpE_A6CNQGBtNHauR1DjenA_n47qlFXX9_rk,745
49
- maidr-1.2.1.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
50
- maidr-1.2.1.dist-info/METADATA,sha256=KqIStmufpv9JUOW9VOeKMbiOPaYl3T2DTWiIpImRyJ0,2796
51
- maidr-1.2.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
52
- maidr-1.2.1.dist-info/RECORD,,
49
+ maidr-1.3.0.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
50
+ maidr-1.3.0.dist-info/METADATA,sha256=m0inXLNCctWjHu0KGU-tQd7q459C2xskdw3LC8-KTsw,2664
51
+ maidr-1.3.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
52
+ maidr-1.3.0.dist-info/RECORD,,
File without changes
File without changes