Skip to content

Commit

Permalink
Various typing fixes/improvements; setup pyright to run on CI (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
cpsievert authored Nov 19, 2024
1 parent 72d16f5 commit fb077be
Show file tree
Hide file tree
Showing 9 changed files with 39 additions and 44 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ jobs:
- name: Install
run: |
make install
- name: pyright
run: |
make pyright
#- name: Run unit tests
# run: |
# make test
# - name: pyright, flake8, black and isort
# run: |
# make check

deploy:
name: "Deploy to PyPI"
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ install: dist ## install the package to the active Python's site-packages
python3 -m pip install dist/shinywidgets*.whl

pyright: ## type check with pyright
pyright --pythonversion=3.7
pyright --pythonversion=3.11

check: pyright lint ## check code quality with pyright, flake8, black and isort
Expand Down
4 changes: 3 additions & 1 deletion pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"ignore": ["examples", "sandbox", "build", "dist", "typings"],
"typeCheckingMode": "strict",
"typeCheckingMode": "basic",
"reportPrivateUsage": "none",
"reportUnknownMemberType": "none",
"reportMissingTypeStubs": "none"
}
7 changes: 7 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ dev =
isort>=5.11.2
pyright>=1.1.284
wheel
altair
bokeh
jupyter_bokeh
plotly
pydeck



[options.packages.find]
include = shinywidgets, shinywidgets.*
Expand Down
18 changes: 9 additions & 9 deletions shinywidgets/_as_widget.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Optional

from ipywidgets.widgets.widget import Widget # pyright: ignore[reportMissingTypeStubs]
from ipywidgets.widgets.widget import Widget

from ._dependencies import widget_pkg

Expand Down Expand Up @@ -35,7 +35,7 @@ def as_widget(x: object) -> Widget:

def as_widget_altair(x: object) -> Optional[Widget]:
try:
from altair import JupyterChart # pyright: ignore[reportMissingTypeStubs]
from altair import JupyterChart
except ImportError:
raise RuntimeError(
"Failed to import altair.JupyterChart (do you need to pip install -U altair?)"
Expand All @@ -46,7 +46,7 @@ def as_widget_altair(x: object) -> Optional[Widget]:

def as_widget_bokeh(x: object) -> Optional[Widget]:
try:
from jupyter_bokeh import BokehModel # pyright: ignore[reportMissingTypeStubs]
from jupyter_bokeh import BokehModel
except ImportError:
raise ImportError(
"Install the jupyter_bokeh package to use bokeh with shinywidgets."
Expand All @@ -55,19 +55,19 @@ def as_widget_bokeh(x: object) -> Optional[Widget]:
# TODO: ideally we'd do this in set_layout_defaults() but doing
# `BokehModel(x)._model.sizing_mode = "stretch_both"`
# there, but that doesn't seem to work??
from bokeh.plotting import figure # pyright: ignore[reportMissingTypeStubs]
from bokeh.plotting import figure

if isinstance(x, figure): # type: ignore
x.sizing_mode = "stretch_both" # pyright: ignore[reportGeneralTypeIssues]
if isinstance(x, figure):
x.sizing_mode = "stretch_both" # type: ignore

return BokehModel(x) # type: ignore


def as_widget_plotly(x: object) -> Optional[Widget]:
# Don't need a try import here since this won't be called unless x is a plotly object
import plotly.graph_objects as go # pyright: ignore[reportMissingTypeStubs]
import plotly.graph_objects as go

if not isinstance(x, go.Figure): # type: ignore
if not isinstance(x, go.Figure):
raise TypeError(
f"Don't know how to coerce {x} into a plotly.graph_objects.FigureWidget object."
)
Expand All @@ -93,7 +93,7 @@ def as_widget_pydeck(x: object) -> Optional[Widget]:
"For more, see https://github.com/visgl/deck.gl/pull/8854"
)

return res # type: ignore
return res


AS_WIDGET_MAP = {
Expand Down
5 changes: 1 addition & 4 deletions shinywidgets/_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@
import packaging.version
from htmltools import HTMLDependency, tags
from htmltools._core import HTMLDependencySource
from ipywidgets._version import (
__html_manager_version__, # pyright: ignore[reportUnknownVariableType]
)
from ipywidgets.widgets.domwidget import DOMWidget
from ipywidgets.widgets.widget import Widget
from jupyter_core.paths import jupyter_path # type: ignore
from jupyter_core.paths import jupyter_path
from shiny import Session, ui

from . import __version__
Expand Down
10 changes: 4 additions & 6 deletions shinywidgets/_render_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
from htmltools import Tag

if TYPE_CHECKING:
from altair import JupyterChart # pyright: ignore[reportMissingTypeStubs]
from jupyter_bokeh import BokehModel # pyright: ignore[reportMissingTypeStubs]
from plotly.graph_objects import ( # pyright: ignore[reportMissingTypeStubs]
FigureWidget,
)
from pydeck.widget import DeckGLWidget # pyright: ignore[reportMissingTypeStubs]
from altair import JupyterChart
from jupyter_bokeh import BokehModel
from plotly.graph_objects import FigureWidget
from pydeck.widget import DeckGLWidget
else:
JupyterChart = BokehModel = FigureWidget = DeckGLWidget = object

Expand Down
20 changes: 5 additions & 15 deletions shinywidgets/_render_widget_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@
from typing import Generic, Optional, Tuple, TypeVar, cast

from htmltools import Tag
from ipywidgets.widgets import ( # pyright: ignore[reportMissingTypeStubs]
DOMWidget,
Layout,
Widget,
)
from ipywidgets.widgets import DOMWidget, Layout, Widget
from shiny import req
from shiny.reactive._core import Context, get_current_context
from shiny.render.renderer import Jsonifiable, Renderer, ValueFn
from traitlets import Unicode

from ._as_widget import as_widget
from ._dependencies import widget_pkg
Expand Down Expand Up @@ -94,12 +89,7 @@ async def render(self) -> Jsonifiable | None:
return None

return {
"model_id": str(
cast(
Unicode,
widget.model_id, # pyright: ignore[reportUnknownMemberType]
)
),
"model_id": str(widget.model_id),
"fill": fill,
}

Expand Down Expand Up @@ -168,7 +158,7 @@ def set_layout_defaults(widget: Widget) -> Tuple[Widget, bool]:
# If the ipywidget Layout() height is set to something other than "auto", then
# don't do filling layout https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Layout.html
if isinstance(layout, Layout):
if layout.height is not None and layout.height != "auto": # type: ignore
if layout.height is not None and layout.height != "auto":
fill = False

pkg = widget_pkg(widget)
Expand All @@ -178,7 +168,7 @@ def set_layout_defaults(widget: Widget) -> Tuple[Widget, bool]:
from plotly.graph_objs import Layout as PlotlyLayout # pyright: ignore

if isinstance(layout, PlotlyLayout):
if layout.height is not None: # pyright: ignore[reportUnknownMemberType]
if layout.height is not None:
fill = False
# Default margins are also way too big
layout.template.layout.margin = dict( # pyright: ignore
Expand All @@ -196,7 +186,7 @@ def set_layout_defaults(widget: Widget) -> Tuple[Widget, bool]:
# container since it'll be contained within the Layout() container, which has a
# full-fledged sizing API.
if pkg == "altair":
import altair as alt # pyright: ignore[reportMissingTypeStubs]
import altair as alt

# Since as_widget() has already happened, we only need to handle JupyterChart
if isinstance(widget, alt.JupyterChart):
Expand Down
10 changes: 5 additions & 5 deletions shinywidgets/_shinywidgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from uuid import uuid4
from weakref import WeakSet

import ipywidgets # pyright: ignore[reportMissingTypeStubs]
import ipywidgets

from ._render_widget import render_widget

Expand Down Expand Up @@ -110,7 +110,7 @@ def _cleanup_session_state():
if getattr(w, "_model_id", None) is None:
w._model_id = uuid4().hex

id = cast(str, w._model_id) # pyright: ignore[reportUnknownMemberType]
id = cast(str, w._model_id)

# Initialize the comm...this will also send the initial state of the widget
with widget_comm_patch():
Expand Down Expand Up @@ -162,7 +162,7 @@ def _cleanup_session_state():

# Dictionary of all "active" widgets (ipywidgets automatically adds to this dictionary as
# new widgets are created, but they won't get removed until the widget is explictly closed)
WIDGET_INSTANCE_MAP = cast(dict[str, Widget], Widget.widgets) # pyright: ignore[reportUnknownMemberType]
WIDGET_INSTANCE_MAP = cast(dict[str, Widget], Widget.widgets)

# --------------------------------------
# Reactivity
Expand Down Expand Up @@ -197,7 +197,7 @@ def reactive_depend(
names = [names]

for name in names:
if not widget.has_trait(name): # pyright: ignore[reportUnknownMemberType]
if not widget.has_trait(name):
raise ValueError(
f"The '{name}' attribute of {widget.__class__.__name__} is not a "
"widget trait, and so it's not possible to reactively read it. "
Expand Down Expand Up @@ -253,7 +253,7 @@ def widget_comm_patch():
Widget.comm.klass = comm_klass


def is_traitlet_instance(x: object) -> "TypeGuard[Instance]":
def is_traitlet_instance(x: object) -> "TypeGuard[Instance[Any]]":
try:
from traitlets.traitlets import Instance
except ImportError:
Expand Down

0 comments on commit fb077be

Please sign in to comment.