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

Enable strict type checking #7497

Merged
merged 34 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b7037ec
Align types of linked_properties
philippjfr Nov 14, 2024
6e2479b
Begin typing everything
philippjfr Nov 15, 2024
d887943
More typing
philippjfr Nov 15, 2024
19b786f
More fixes
philippjfr Nov 15, 2024
e051df1
Add Tabulator types
philippjfr Nov 15, 2024
95e0b52
More types
philippjfr Nov 15, 2024
750a217
Small fix
philippjfr Nov 15, 2024
9e602c9
More types
philippjfr Nov 15, 2024
396ad6d
More types
philippjfr Nov 16, 2024
71912de
Upgrade type definitions and apply pyupgrade
philippjfr Nov 16, 2024
52bef7c
More updates
philippjfr Nov 16, 2024
b865a10
Update type imports for generics
philippjfr Nov 16, 2024
d9e1f1f
Add more types
philippjfr Nov 16, 2024
949b37c
More minor fixes
philippjfr Nov 16, 2024
6e38a3c
More types
philippjfr Nov 17, 2024
2158148
More types
philippjfr Nov 17, 2024
5264b2f
More types
philippjfr Nov 17, 2024
60b9eb4
More types
philippjfr Nov 17, 2024
ef2fd79
Fix up type error
philippjfr Nov 17, 2024
4b8da9b
Fix tests
philippjfr Nov 17, 2024
04ca6fe
Apply suggestions from code review
philippjfr Nov 17, 2024
19d68bf
Fix up type
philippjfr Nov 17, 2024
5218def
fix textual tests
philippjfr Nov 17, 2024
c867531
Merge branch 'main' into linked_props_types
philippjfr Nov 18, 2024
1a7f4d1
Fix JupyterServerContext
philippjfr Nov 18, 2024
197ff5a
Back out of JupyterServerContext
philippjfr Nov 18, 2024
daa8493
Address review comments
philippjfr Nov 18, 2024
060f415
revert version upgrade
philippjfr Nov 18, 2024
46c1107
Apply suggestions from code review
philippjfr Dec 2, 2024
770be06
Resolve review comments
philippjfr Dec 2, 2024
8c7443a
Small fix
philippjfr Dec 2, 2024
875aeca
Fix types
philippjfr Dec 2, 2024
b8253e3
Another fix
philippjfr Dec 2, 2024
16bd060
Merge branch 'main' into linked_props_types
philippjfr Dec 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ jobs:
environments: ${{ matrix.environment }}
- name: Test Type
run: |
pixi run -e ${{ matrix.environment }} test-type || echo "Failed"
pixi run -e ${{ matrix.environment }} test-type

result_test_suite:
name: result:test
Expand Down
8 changes: 4 additions & 4 deletions doc/generate_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def write_file(name, text, opts):
"""Write the output file for module/package <name>."""
if opts.dryrun:
return
fname = os.path.join(opts.destdir, "%s.%s" % (name, opts.suffix))
fname = os.path.join(opts.destdir, "{}.{}".format(name, opts.suffix))
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
if not opts.force and os.path.isfile(fname):
print('File %s already exists, skipping.' % fname)
else:
Expand All @@ -69,7 +69,7 @@ def write_file(name, text, opts):
def format_heading(level, text):
"""Create a heading of <level> [1, 2 or 3 supported]."""
underlining = ['=', '-', '~', ][level-1] * len(text)
return '%s\n%s\n\n' % (text, underlining)
return '{}\n{}\n\n'.format(text, underlining)

def format_directive(module, package=None):
"""Create the automodule directive and add the options."""
Expand All @@ -89,7 +89,7 @@ def create_module_file(package, module, opts):
def create_package_file(root, master_package, subroot, py_files, opts, subs):
"""Build the text of the file and write the file."""
package = os.path.split(root)[-1]
text = format_heading(1, '%s.%s Package' % (master_package, package))
text = format_heading(1, '{}.{} Package'.format(master_package, package))
text += '\n---------\n\n'
# add each package's module
for py_file in py_files:
Expand All @@ -114,7 +114,7 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs):
text += format_heading(2, 'Subpackages')
text += '.. toctree::\n\n'
for sub in subs:
text += ' %s.%s\n' % (makename(master_package, subroot), sub)
text += ' {}.{}\n'.format(makename(master_package, subroot), sub)
text += '\n'

write_file(makename(master_package, subroot), text, opts)
Expand Down
2 changes: 1 addition & 1 deletion examples/apps/django/sliders/sinewave.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SineWave(param.Parameterized):
y_range = param.Range(default=(-2.5,2.5),bounds=(-10,10))

def __init__(self, **params):
super(SineWave, self).__init__(**params)
super().__init__(**params)
x, y = self.sine()
self.cds = ColumnDataSource(data=dict(x=x, y=y))
self.plot = figure(height=400, width=400,
Expand Down
2 changes: 1 addition & 1 deletion examples/apps/django_multi_apps/gbm/pn_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class GBM(param.Parameterized):
refresh = pn.widgets.Button(name="Refresh", button_type='primary')

def __init__(self, **params):
super(GBM, self).__init__(**params)
super().__init__(**params)

# update the plot for every changes
# @param.depends('mean', 'volatility', 'maturity', 'n_observations', 'n_simulations', watch=True)
Expand Down
2 changes: 1 addition & 1 deletion examples/apps/django_multi_apps/sliders/pn_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SineWave(param.Parameterized):
y_range = param.Range(default=(-2.5, 2.5), bounds=(-10, 10))

def __init__(self, **params):
super(SineWave, self).__init__(**params)
super().__init__(**params)
x, y = self.sine()
self.cds = ColumnDataSource(data=dict(x=x, y=y))
self.plot = figure(height=400, width=400,
Expand Down
2 changes: 1 addition & 1 deletion examples/apps/django_multi_apps/stockscreener/pn_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class StockScreener(param.Parameterized):
def __init__(self, df, **params):
start = dt.date(year=df.index[0].year, month=df.index[0].month, day=df.index[0].day)
end = dt.date(year=df.index[-1].year, month=df.index[-1].month, day=df.index[-1].day)
super(StockScreener, self).__init__(df=df, start=start, **params)
super().__init__(df=df, start=start, **params)
self.param.start.bounds = (start, end)
columns = list(self.df.columns)
self.param.index.objects = columns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class StockScreener(param.Parameterized):
From = pn.widgets.DateSlider()

def __init__(self, df, **params):
super(StockScreener, self).__init__(**params)
super().__init__(**params)
# init df
self.df = df
self.start_date = dt.datetime(year=df.index[0].year, month=df.index[0].month, day=df.index[0].day)
Expand Down
2 changes: 1 addition & 1 deletion examples/apps/fastApi/sliders/sinewave.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SineWave(param.Parameterized):
y_range = param.Range(default=(-2.5, 2.5), bounds=(-10, 10))

def __init__(self, **params):
super(SineWave, self).__init__(**params)
super().__init__(**params)
x, y = self.sine()
self.cds = ColumnDataSource(data=dict(x=x, y=y))
self.plot = figure(height=400, width=400,
Expand Down
2 changes: 1 addition & 1 deletion examples/apps/fastApi_multi_apps/sliders/sinewave.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SineWave(param.Parameterized):
y_range = param.Range(default=(-2.5, 2.5), bounds=(-10, 10))

def __init__(self, **params):
super(SineWave, self).__init__(**params)
super().__init__(**params)
x, y = self.sine()
self.cds = ColumnDataSource(data=dict(x=x, y=y))
self.plot = figure(height=400, width=400,
Expand Down
2 changes: 1 addition & 1 deletion examples/apps/fastApi_multi_apps/sliders2/sinewave.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SineWave(param.Parameterized):
y_range = param.Range(default=(-2.5, 2.5), bounds=(-10, 10))

def __init__(self, **params):
super(SineWave, self).__init__(**params)
super().__init__(**params)
x, y = self.sine()
self.cds = ColumnDataSource(data=dict(x=x, y=y))
self.plot = figure(height=400, width=400,
Expand Down
2 changes: 1 addition & 1 deletion panel/__main__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys


def main():
def main() -> None:
from panel.command import main as _main

# Main entry point (see setup.py)
Expand Down
50 changes: 36 additions & 14 deletions panel/_param.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
from __future__ import annotations

from typing import (
TYPE_CHECKING, Any, Literal, TypeAlias, cast,
)

from bokeh.core.enums import enumeration
from param import Parameter, _is_number

if TYPE_CHECKING:
MarginType: TypeAlias = int | tuple[int, int] | tuple[int, int, int] | tuple[int, int, int, int]

AlignmentType = Literal["auto", "start", "center", "end"]
Alignment = enumeration(AlignmentType)


class Align(Parameter):
"""
Expand All @@ -8,16 +21,20 @@ class Align(Parameter):
to the (vertical, horizontal) alignment.
"""

def __init__(self, default='start', **params):
def __init__(
self,
default: AlignmentType | tuple[AlignmentType, AlignmentType] = "start",
**params: Any
):
super().__init__(default=default, **params)
self._validate(default)

def _validate(self, val):
def _validate(self, val: Any) -> None:
self._validate_value(val, self.allow_None)

def _validate_value(self, val, allow_None, valid=('auto', 'start', 'center', 'end')):
if ((val is None and allow_None) or val in valid or
(isinstance(val, tuple) and len(val) == 2 and all(v in valid for v in val))):
def _validate_value(self, val: Any, allow_None: bool) -> None:
if ((val is None and allow_None) or val in Alignment or
(isinstance(val, tuple) and len(val) == 2 and all(v in Alignment for v in val))):
return
raise ValueError(
f"Align parameter {self.name!r} must be one of 'start', "
Expand All @@ -36,10 +53,10 @@ def __init__(self, default=None, allow_None=True, **params):
super().__init__(default=default, allow_None=allow_None, **params)
self._validate(default)

def _validate(self, val):
def _validate(self, val: Any) -> None:
self._validate_value(val, self.allow_None)

def _validate_value(self, val, allow_None):
def _validate_value(self, val: Any, allow_None: bool) -> None:
if (val is None and allow_None) or val == 'auto' or _is_number(val):
return
raise ValueError(
Expand All @@ -60,7 +77,7 @@ def __init__(self, default=None, allow_None=True, **params):
super().__init__(default=default, allow_None=allow_None, **params)
self._validate(default)

def _validate_value(self, val, allow_None):
def _validate_value(self, val: Any, allow_None: bool) -> None:
if val is None and allow_None:
return
if not isinstance(val, (tuple, int)):
Expand All @@ -69,27 +86,32 @@ def _validate_value(self, val, allow_None):
f'tuple values, not values of not {type(val)!r}.'
)

def _validate_length(self, val):
if not isinstance(val, tuple) or len(val) in (2, 4):
def _validate_length(self, val: Any) -> None:
if not isinstance(val, tuple) or (1 < len(val) < 5):
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
return
raise ValueError(
f'Margin parameter {self.name!r} only takes integer and '
f'tuple values of length 2 (vertical, horizontal) or 4 '
'(top, right, bottom, left).'
)

def _validate(self, val):
def _validate(self, val: Any) -> None:
self._validate_value(val, self.allow_None)
self._validate_length(val)

@classmethod
def serialize(cls, value):
def serialize(cls, value: MarginType) -> Literal['null'] | list[int] | int:
if value is None:
return 'null'
return list(value) if isinstance(value, tuple) else value

@classmethod
def deserialize(cls, value):
def deserialize(cls, value: Literal['null'] | list[int] | int) -> MarginType | None:
if value == 'null':
return None
return tuple(value) if isinstance(value, list) else value
elif isinstance(value, list):
n = len(value)
if (n < 2 or n > 5):
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError(f'Cannot deserialize list of length {n}.')
return cast(MarginType, tuple(value))
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
return value
7 changes: 4 additions & 3 deletions panel/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from base64 import urlsafe_b64encode
from functools import partial
from typing import ClassVar

import tornado

Expand Down Expand Up @@ -101,13 +102,13 @@ class OAuthLoginHandler(tornado.web.RequestHandler, OAuth2Mixin):
'grant_type': 'authorization_code'
}

_access_token_header = None
_access_token_header: ClassVar[str | None] = None

_state_cookie = None
_state_cookie: ClassVar[str | None] = None

_error_template = ERROR_TEMPLATE

_login_endpoint = '/login'
_login_endpoint: ClassVar[str] = '/login'

@property
def _SCOPE(self):
Expand Down
29 changes: 16 additions & 13 deletions panel/chat/feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
import asyncio
import traceback

from collections.abc import Callable
from enum import Enum
from inspect import (
getfullargspec, isasyncgen, isasyncgenfunction, isawaitable,
iscoroutinefunction, isgenerator, isgeneratorfunction, ismethod,
)
from io import BytesIO
from typing import (
TYPE_CHECKING, Any, Callable, ClassVar, Literal,
TYPE_CHECKING, Any, ClassVar, Literal,
)

import param
Expand Down Expand Up @@ -409,7 +410,7 @@ def _upsert_message(
is_stopped = self._callback_future is not None and self._callback_future.cancelled()
if value is None:
# don't add new message if the callback returns None
return
return None
elif is_stopping or is_stopped:
raise StopCallback("Callback was stopped.")

Expand Down Expand Up @@ -491,12 +492,14 @@ async def _serialize_response(self, response: Any) -> ChatMessage | None:
self._callback_state = CallbackState.GENERATING
async for token in response:
response_message = self._upsert_message(token, response_message)
response_message.show_activity_dot = self.show_activity_dot
if response_message is not None:
response_message.show_activity_dot = self.show_activity_dot
elif isgenerator(response):
self._callback_state = CallbackState.GENERATING
for token in response:
response_message = self._upsert_message(token, response_message)
response_message.show_activity_dot = self.show_activity_dot
if response_message is not None:
response_message.show_activity_dot = self.show_activity_dot
elif isawaitable(response):
response_message = self._upsert_message(await response, response_message)
else:
Expand Down Expand Up @@ -527,7 +530,7 @@ async def _schedule_placeholder(
return
await asyncio.sleep(0.1)

async def _handle_callback(self, message, loop: asyncio.BaseEventLoop):
async def _handle_callback(self, message, loop: asyncio.AbstractEventLoop):
callback_args, callback_kwargs = self._gather_callback_args(message)
if iscoroutinefunction(self.callback):
response = await self.callback(*callback_args, **callback_kwargs)
Expand Down Expand Up @@ -561,16 +564,16 @@ async def _prepare_response(self, *_) -> None:

num_entries = len(self._chat_log)
loop = asyncio.get_event_loop()
future = loop.create_task(self._handle_callback(message, loop))
self._callback_future = future
task = loop.create_task(self._handle_callback(message, loop))
self._callback_future = task
await asyncio.gather(
self._schedule_placeholder(future, num_entries), future,
self._schedule_placeholder(task, num_entries), task,
)
except StopCallback:
# callback was stopped by user
self._callback_state = CallbackState.STOPPED
except Exception as e:
send_kwargs = dict(user="Exception", respond=False)
send_kwargs: dict[str, Any] = dict(user="Exception", respond=False)
if self.callback_exception == "summary":
self.send(
f"Encountered `{e!r}`. "
Expand Down Expand Up @@ -633,7 +636,7 @@ def send(
"Cannot set user or avatar when explicitly sending "
"a ChatMessage. Set them directly on the ChatMessage."
)
message = value
message: ChatMessage | None = value
else:
if not isinstance(value, dict):
value = {"object": value}
Expand Down Expand Up @@ -690,7 +693,7 @@ def stream(
"a ChatMessage. Set them directly on the ChatMessage."
)
elif message:
if isinstance(value, (str, dict)):
if isinstance(value, str):
message.stream(value, replace=replace)
if user:
message.user = user
Expand Down Expand Up @@ -865,7 +868,7 @@ async def _prepare_prompt(*_) -> None:
send_kwargs["user"] = "Input"
self.send(form, respond=False, **send_kwargs)

for _ in range(timeout * 10): # sleeping for 0.1 seconds
for __ in range(timeout * 10): # sleeping for 0.1 seconds
philippjfr marked this conversation as resolved.
Show resolved Hide resolved
is_fulfilled = predicate(component) if predicate else True
submit_button.disabled = not is_fulfilled
if submit_button.clicks > 0:
Expand Down Expand Up @@ -958,7 +961,7 @@ def _serialize_for_transformers(
self,
messages: list[ChatMessage],
role_names: dict[str, str | list[str]] | None = None,
default_role: str | None = "assistant",
default_role: str = "assistant",
custom_serializer: Callable | None = None,
**serialize_kwargs
) -> list[dict[str, Any]]:
Expand Down
13 changes: 6 additions & 7 deletions panel/chat/input.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from __future__ import annotations

from typing import (
TYPE_CHECKING, Any, ClassVar, Mapping, Optional,
)
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, ClassVar

import param

Expand Down Expand Up @@ -79,17 +78,17 @@ class ChatAreaInput(_PnTextAreaInput):
**_PnTextAreaInput._rename,
}

def _get_properties(self, doc: Document) -> dict[str, Any]:
def _get_properties(self, doc: Document | None = None) -> dict[str, Any]:
props = super()._get_properties(doc)
props.update({"value_input": self.value, "value": self.value})
return props

def _get_model(
self,
doc: Document,
root: Optional[Model] = None,
parent: Optional[Model] = None,
comm: Optional[Comm] = None,
root: Model | None = None,
parent: Model | None = None,
comm: Comm | None = None,
) -> Model:
model = super()._get_model(doc, root, parent, comm)
self._register_events("chat_message_event", model=model, doc=doc, comm=comm)
Expand Down
Loading
Loading