Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Andrew/type checks on input #295

Merged
merged 15 commits into from
Mar 25, 2025
1 change: 1 addition & 0 deletions src/py/CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
v1.0.0rc11
- Write mocker tool to parameterize opts in tests
- Crop page to pdf size
- Add type checks to user input for improved error messages
v1.0.0rc10
- Allow user to pass Figure-like dicts
- Fix bug by which calc fig rejected plotly figures
Expand Down
28 changes: 24 additions & 4 deletions src/py/kaleido/_fig_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@


def _is_figurish(o):
return hasattr(o, "to_dict") or (
isinstance(o, dict) and "data" in o and "layout" in o
)
valid = hasattr(o, "to_dict") or (isinstance(o, dict) and "data" in o)
if not valid:
_logger.debug(
f"Figure has to_dict? {hasattr(o, 'to_dict')} "
f"is dict? {isinstance(o, dict)} "
f"Keys: {o.keys() if hasattr(o, 'keys') else None!s}",
)
return valid


def _get_figure_dimensions(layout, width, height):
Expand Down Expand Up @@ -56,6 +61,16 @@ def to_spec(figure, layout_opts):
# Get figure layout
layout = figure.get("layout", {})

for k, v in layout_opts.items():
if k == "format":
if v is not None and not isinstance(v, (str)):
raise TypeError(f"{v} must be string or None")
elif k in ("scale", "height", "width"):
if v is not None and not isinstance(v, (float, int)):
raise TypeError(f"{v} must be numeric or None")
else:
raise AttributeError(f"Unknown key in layout options, {k}")

# Extract info
extension = _get_format(layout_opts.get("format") or DEFAULT_EXT)
width, height = _get_figure_dimensions(
Expand Down Expand Up @@ -86,15 +101,20 @@ def _next_filename(path, prefix, ext):
return f"{prefix}.{ext}" if n == 1 else f"{prefix}-{n}.{ext}"


def build_fig_spec(fig, path, opts):
def build_fig_spec(fig, path, opts): # noqa: C901
if not opts:
opts = {}

if not _is_figurish(fig):
raise TypeError("Figure supplied doesn't seem to be a valid plotly figure.")

if hasattr(fig, "to_dict"):
fig = fig.to_dict()

if isinstance(path, str):
path = Path(path)
elif path and not isinstance(path, Path):
raise TypeError("Path should be a string or `pathlib.Path` object (or None)")

if path and path.suffix and not opts.get("format"):
opts["format"] = path.suffix.lstrip(".")
Expand Down