Skip to content

Commit 00bd01b

Browse files
committed
Add typechecking
1 parent 8d829df commit 00bd01b

7 files changed

Lines changed: 83 additions & 32 deletions

File tree

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ lint-fix:
6565

6666
fix: format-fix lint-fix
6767

68+
typecheck:
69+
$(UVRUN) pyright
70+
6871
test: clean-test
6972
$(UVRUN) pytest
7073

pyproject.toml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dev = [
3131
"pytest-cov>=4.0.0",
3232
"coveralls",
3333
"matplotlib",
34+
"pyright>=1.1.405",
3435
]
3536

3637
[project.urls]
@@ -130,3 +131,45 @@ exclude = [
130131

131132
# Allow unused variables when underscore-prefixed.
132133
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
134+
135+
########## Tool - Pyright ##########
136+
[tool.pyright]
137+
# Paths of directories or files that should be included. If no paths
138+
# are specified, pyright defaults to the directory that contains the
139+
# config file. Paths may contain wildcard characters ** (a directory or
140+
# multiple levels of directories), * (a sequence of zero or more
141+
# characters), or ? (a single character). If no include paths are
142+
# specified, the root path for the workspace is assumed.
143+
include = [
144+
"src/gnuplot_kernel/"
145+
]
146+
147+
# Paths of directories or files whose diagnostic output (errors and
148+
# warnings) should be suppressed even if they are an included file or
149+
# within the transitive closure of an included file. Paths may contain
150+
# wildcard characters ** (a directory or multiple levels of
151+
# directories), * (a sequence of zero or more characters), or ? (a
152+
# single character).
153+
ignore = []
154+
155+
# Set of identifiers that should be assumed to contain a constant
156+
# value wherever used within this program. For example, { "DEBUG": true
157+
# } indicates that pyright should assume that the identifier DEBUG will
158+
# always be equal to True. If this identifier is used within a
159+
# conditional expression (such as if not DEBUG:) pyright will use the
160+
# indicated value to determine whether the guarded block is reachable
161+
# or not. Member expressions that reference one of these constants
162+
# (e.g. my_module.DEBUG) are also supported.
163+
defineConstant = { DEBUG = true }
164+
165+
# typeCheckingMode = "strict"
166+
useLibraryCodeForTypes = true
167+
reportUnnecessaryTypeIgnoreComment = true
168+
169+
# Specifies a list of execution environments (see below). Execution
170+
# environments are searched from start to finish by comparing the path
171+
# of a source file with the root path specified in the execution
172+
# environment.
173+
executionEnvironments = []
174+
175+
stubPath = ""

src/gnuplot_kernel/kernel.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
from __future__ import annotations
2+
13
import contextlib
24
import sys
35
import uuid
46
from itertools import chain
57
from pathlib import Path
8+
from typing import cast
69

710
from IPython.display import SVG, Image
811
from metakernel import MetaKernel, ProcessMetaKernel, pexpect
@@ -25,8 +28,8 @@ class GnuplotKernel(ProcessMetaKernel):
2528
implementation = "Gnuplot Kernel"
2629
implementation_version = get_version("gnuplot_kernel")
2730
language = "gnuplot"
28-
language_version = "5.0"
29-
banner = "Gnuplot Kernel"
31+
_banner = "Gnuplot Kernel"
32+
language_version = "5.0" # pyright: ignore[reportAssignmentType,reportIncompatibleMethodOverride]
3033
language_info = {
3134
"mimetype": "text/x-gnuplot",
3235
"name": "gnuplot",
@@ -50,20 +53,20 @@ class GnuplotKernel(ProcessMetaKernel):
5053
inline_plotting = True
5154
reset_code = ""
5255
_first = True
53-
_image_files = []
56+
_image_files: list[Path] = []
5457
_error = False
5558

59+
wrapper: GnuplotREPLWrapper
60+
5661
def bad_prompt_warning(self):
5762
"""
5863
Print warning if the prompt is not 'gnuplot>'
5964
"""
60-
if not self.wrapper.prompt.startswith("gnuplot>"):
61-
msg = "Warning: The prompt is currently set to '{}'".format(
62-
self.wrapper.prompt
63-
)
64-
print(msg)
65+
prompt = cast("str", self.wrapper.prompt).strip()
66+
if not prompt.endswith("gnuplot>"):
67+
print(f"Warning: The prompt is currently set to '{prompt}'")
6568

66-
def do_execute_direct(self, code):
69+
def do_execute_direct(self, code, silent=False):
6770
# We wrap the real function so that gnuplot_kernel can
6871
# give a message when an exception occurs. Without
6972
# this, an exception happens silently
@@ -73,7 +76,7 @@ def do_execute_direct(self, code):
7376
print(f"Error: {err}")
7477
raise err
7578

76-
def _do_execute_direct(self, code):
79+
def _do_execute_direct(self, code: str) -> TextOutput | None:
7780
"""
7881
Execute gnuplot code
7982
"""
@@ -105,7 +108,7 @@ def _do_execute_direct(self, code):
105108
# No empty strings
106109
return result if (result and result.output) else None
107110

108-
def add_inline_image_statements(self, code):
111+
def add_inline_image_statements(self, code: str) -> str:
109112
"""
110113
Add 'set output ...' before every plotting statement
111114
@@ -188,6 +191,8 @@ def display_images(self):
188191
settings = self.plot_settings
189192
if self.inline_plotting:
190193
_Image = SVG if settings["format"] == "svg" else Image
194+
else:
195+
return
191196

192197
for filename in self.iter_image_files():
193198
try:
@@ -238,12 +243,11 @@ def makeWrapper(self):
238243
else:
239244
command = program
240245

241-
d = dict(
246+
wrapper = GnuplotREPLWrapper(
242247
cmd_or_spawn=command,
243248
prompt_regex=PROMPT_RE,
244249
prompt_change_cmd=None,
245250
)
246-
wrapper = GnuplotREPLWrapper(**d)
247251
# No sleeping before sending commands to gnuplot
248252
wrapper.child.delaybeforesend = 0
249253
return wrapper
@@ -258,11 +262,8 @@ def do_shutdown(self, restart):
258262
def get_kernel_help_on(self, info, level=0, none_on_fail=False):
259263
obj = info.get("help_obj", "")
260264
if not obj or len(obj.split()) > 1:
261-
if none_on_fail:
262-
return None
263-
else:
264-
return ""
265-
res = self.do_execute_direct("help %s" % obj)
265+
return None if none_on_fail else ""
266+
res = cast("TextOutput", self.do_execute_direct("help %s" % obj))
266267
text = PROMPT_REMOVE_RE.sub("", res.output)
267268
self.bad_prompt_warning()
268269
return text

src/gnuplot_kernel/magics/gnuplot_magic.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,23 +108,21 @@ def register_ipython_magics():
108108
# not the main kernel and it may not have access
109109
# to some functionality. This connects it to the
110110
# main kernel.
111-
from IPython import get_ipython
111+
from IPython.core.getipython import get_ipython
112112

113113
ip = get_ipython()
114-
kernel.makeSubkernel(ip.parent)
114+
kernel.makeSubkernel(ip.parent) # pyright: ignore[reportOptionalMemberAccess]
115115

116116
# Make magics callable:
117117
kernel.line_magics["gnuplot"] = magic
118118
kernel.cell_magics["gnuplot"] = magic
119119

120120
@register_line_magic
121-
def gnuplot(line):
121+
def _(line):
122122
magic.line_gnuplot(line)
123123

124-
del gnuplot
125-
126124
@register_cell_magic
127-
def gnuplot(line, cell):
125+
def _(line, cell):
128126
magic.code = cell
129127
magic.cell_gnuplot()
130128

src/gnuplot_kernel/py.typed

Whitespace-only changes.

src/gnuplot_kernel/replwrap.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
import signal
33
import textwrap
4+
from typing import cast
45

56
from metakernel import REPLWrapper
67
from metakernel.pexpect import TIMEOUT
@@ -84,7 +85,7 @@ def validate_input(self, code):
8485
def send(self, cmd):
8586
self.child.send(cmd + "\r")
8687

87-
def _force_prompt(self, timeout=30, n=4):
88+
def _force_prompt(self, timeout: float=30, n=4):
8889
"""
8990
Force prompt
9091
"""
@@ -109,7 +110,7 @@ def patient_prompt():
109110

110111
# Eagerly try to get a prompt quickly,
111112
# If that fails wait a while
112-
for i in range(n):
113+
for _ in range(n):
113114
if quick_prompt():
114115
break
115116

@@ -209,27 +210,32 @@ def _splitlines(self, code):
209210

210211
return lines
211212

212-
def run_command(
213-
self, code, timeout=-1, stream_handler=None, stdin_handler=None
213+
def run_command( # pyright: ignore[reportIncompatibleMethodOverride]
214+
self,
215+
command,
216+
timeout=-1,
217+
stream_handler=None,
218+
line_handler=None,
219+
stdin_handler=None,
214220
):
215221
"""
216222
Run code
217223
218224
This overrides the baseclass method to allow for
219225
input validation and error handling.
220226
"""
221-
code = self.validate_input(code)
227+
command = self.validate_input(command)
222228

223229
# Split up multiline commands and feed them in bit-by-bit
224-
stmts = self._splitlines(code)
230+
stmts = self._splitlines(command)
225231
output_lines = []
226232
for line in stmts:
227233
self.send(line)
228234
self._force_prompt()
229235

230236
# Removing any crlfs makes subsequent
231237
# processing cleaner
232-
retval = self.child.before.replace(CRLF, "\n")
238+
retval = cast("str", self.child.before).replace(CRLF, "\n")
233239
self.prompt = self.child.after
234240
if self.is_error_output(retval):
235241
msg = "{}\n{}".format(line, textwrap.dedent(retval))

src/gnuplot_kernel/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from importlib.metadata import version
66

77

8-
def get_version(package):
8+
def get_version(package: str) -> str:
99
"""
1010
Return the package version
1111

0 commit comments

Comments
 (0)