* Ruff fixes

* Ruff fixes

* from __future__ import annotations breaks MicroPython

* noqa: FURB188 because there is no str.replacesuffix() in MicroPython

* Add ruff to pre-commit
This commit is contained in:
Christian Clauss
2025-02-20 09:43:09 +01:00
committed by GitHub
parent 0366e48fad
commit 46239caa19
20 changed files with 117 additions and 100 deletions

View File

@@ -38,6 +38,11 @@ repos:
additional_dependencies: additional_dependencies:
- tomli - tomli
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.6
hooks:
- id: ruff
- repo: https://github.com/hoodmane/pyscript-prettier-precommit - repo: https://github.com/hoodmane/pyscript-prettier-precommit
rev: "v3.0.0-alpha.6" rev: "v3.0.0-alpha.6"
hooks: hooks:

View File

@@ -73,14 +73,14 @@ def _eval_formatter(obj, print_method):
""" """
if print_method == "__repr__": if print_method == "__repr__":
return repr(obj) return repr(obj)
elif hasattr(obj, print_method): if hasattr(obj, print_method):
if print_method == "savefig": if print_method == "savefig":
buf = io.BytesIO() buf = io.BytesIO()
obj.savefig(buf, format="png") obj.savefig(buf, format="png")
buf.seek(0) buf.seek(0)
return base64.b64encode(buf.read()).decode("utf-8") return base64.b64encode(buf.read()).decode("utf-8")
return getattr(obj, print_method)() return getattr(obj, print_method)()
elif print_method == "_repr_mimebundle_": if print_method == "_repr_mimebundle_":
return {}, {} return {}, {}
return None return None
@@ -107,7 +107,7 @@ def _format_mime(obj):
if output is None: if output is None:
continue continue
elif mime_type not in _MIME_RENDERERS: if mime_type not in _MIME_RENDERERS:
not_available.append(mime_type) not_available.append(mime_type)
continue continue
break break
@@ -149,9 +149,11 @@ def display(*values, target=None, append=True):
if target is None: if target is None:
target = current_target() target = current_target()
elif not isinstance(target, str): elif not isinstance(target, str):
raise TypeError(f"target must be str or None, not {target.__class__.__name__}") msg = f"target must be str or None, not {target.__class__.__name__}"
raise TypeError(msg)
elif target == "": elif target == "":
raise ValueError("Cannot have an empty target") msg = "Cannot have an empty target"
raise ValueError(msg)
elif target.startswith("#"): elif target.startswith("#"):
# note: here target is str and not None! # note: here target is str and not None!
# align with @when behavior # align with @when behavior
@@ -161,9 +163,8 @@ def display(*values, target=None, append=True):
# If target cannot be found on the page, a ValueError is raised # If target cannot be found on the page, a ValueError is raised
if element is None: if element is None:
raise ValueError( msg = f"Invalid selector with id={target}. Cannot be found in the page."
f"Invalid selector with id={target}. Cannot be found in the page." raise ValueError(msg)
)
# if element is a <script type="py">, it has a 'target' attribute which # if element is a <script type="py">, it has a 'target' attribute which
# points to the visual element holding the displayed values. In that case, # points to the visual element holding the displayed values. In that case,

View File

@@ -36,7 +36,8 @@ class Event:
if listener not in self._listeners: if listener not in self._listeners:
self._listeners.append(listener) self._listeners.append(listener)
else: else:
raise ValueError("Listener must be callable or awaitable.") msg = "Listener must be callable or awaitable."
raise ValueError(msg)
def remove_listener(self, *args): def remove_listener(self, *args):
""" """
@@ -76,7 +77,8 @@ def when(target, *args, **kwargs):
# Extract the selector from the arguments or keyword arguments. # Extract the selector from the arguments or keyword arguments.
selector = args[0] if args else kwargs.pop("selector") selector = args[0] if args else kwargs.pop("selector")
if not selector: if not selector:
raise ValueError("No selector provided.") msg = "No selector provided."
raise ValueError(msg)
# Grab the DOM elements to which the target event will be attached. # Grab the DOM elements to which the target event will be attached.
from pyscript.web import Element, ElementCollection from pyscript.web import Element, ElementCollection

View File

@@ -31,7 +31,7 @@ def _object_keys(value):
def _is_array(value): def _is_array(value):
return isinstance(value, list) or isinstance(value, tuple) return isinstance(value, (list, tuple))
def _is_object(value): def _is_object(value):
@@ -60,10 +60,10 @@ def _loop(keys, input, known, output):
def _ref(key, value, input, known, output): def _ref(key, value, input, known, output):
if _is_array(value) and not value in known: if _is_array(value) and value not in known:
known.append(value) known.append(value)
value = _loop(_array_keys(value), input, known, value) value = _loop(_array_keys(value), input, known, value)
elif _is_object(value) and not value in known: elif _is_object(value) and value not in known:
known.append(value) known.append(value)
value = _loop(_object_keys(value), input, known, value) value = _loop(_object_keys(value), input, known, value)

View File

@@ -25,6 +25,7 @@ class JSModule:
# avoid pyodide looking for non existent fields # avoid pyodide looking for non existent fields
if not field.startswith("_"): if not field.startswith("_"):
return getattr(getattr(js_modules, self.name), field) return getattr(getattr(js_modules, self.name), field)
return None
# generate N modules in the system that will proxy the real value # generate N modules in the system that will proxy the real value

View File

@@ -44,8 +44,7 @@ class Device:
for k in video: for k in video:
setattr(options.video, k, to_js(video[k])) setattr(options.video, k, to_js(video[k]))
stream = await window.navigator.mediaDevices.getUserMedia(options) return await window.navigator.mediaDevices.getUserMedia(options)
return stream
async def get_stream(self): async def get_stream(self):
key = self.kind.replace("input", "").replace("output", "") key = self.kind.replace("input", "").replace("output", "")

View File

@@ -10,10 +10,11 @@ def _to_idb(value):
if isinstance(value, (bool, float, int, str, list, dict, tuple)): if isinstance(value, (bool, float, int, str, list, dict, tuple)):
return _stringify(["generic", value]) return _stringify(["generic", value])
if isinstance(value, bytearray): if isinstance(value, bytearray):
return _stringify(["bytearray", [v for v in value]]) return _stringify(["bytearray", list(value)])
if isinstance(value, memoryview): if isinstance(value, memoryview):
return _stringify(["memoryview", [v for v in value]]) return _stringify(["memoryview", list(value)])
raise TypeError(f"Unexpected value: {value}") msg = f"Unexpected value: {value}"
raise TypeError(msg)
# convert an IndexedDB compatible entry into a Python value # convert an IndexedDB compatible entry into a Python value
@@ -56,5 +57,6 @@ class Storage(dict):
async def storage(name="", storage_class=Storage): async def storage(name="", storage_class=Storage):
if not name: if not name:
raise ValueError("The storage name must be defined") msg = "The storage name must be defined"
raise ValueError(msg)
return storage_class(await _storage(f"@pyscript/{name}")) return storage_class(await _storage(f"@pyscript/{name}"))

View File

@@ -11,7 +11,7 @@ def as_bytearray(buffer):
ui8a = js.Uint8Array.new(buffer) ui8a = js.Uint8Array.new(buffer)
size = ui8a.length size = ui8a.length
ba = bytearray(size) ba = bytearray(size)
for i in range(0, size): for i in range(size):
ba[i] = ui8a[i] ba[i] = ui8a[i]
return ba return ba

View File

@@ -2,7 +2,10 @@
# `when` is not used in this module. It is imported here save the user an additional # `when` is not used in this module. It is imported here save the user an additional
# import (i.e. they can get what they need from `pyscript.web`). # import (i.e. they can get what they need from `pyscript.web`).
from pyscript import document, when, Event # NOQA
# from __future__ import annotations # CAUTION: This is not supported in MicroPython.
from pyscript import document, when, Event # noqa: F401
from pyscript.ffi import create_proxy from pyscript.ffi import create_proxy
@@ -100,7 +103,7 @@ class Element:
If `key` is an integer or a slice we use it to index/slice the element's If `key` is an integer or a slice we use it to index/slice the element's
children. Otherwise, we use `key` as a query selector. children. Otherwise, we use `key` as a query selector.
""" """
if isinstance(key, int) or isinstance(key, slice): if isinstance(key, (int, slice)):
return self.children[key] return self.children[key]
return self.find(key) return self.find(key)
@@ -120,7 +123,7 @@ class Element:
# attribute `for` which is a Python keyword, so you can access it on the # attribute `for` which is a Python keyword, so you can access it on the
# Element instance via `for_`). # Element instance via `for_`).
if name.endswith("_"): if name.endswith("_"):
name = name[:-1] name = name[:-1] # noqa: FURB188 No str.removesuffix() in MicroPython.
return getattr(self._dom_element, name) return getattr(self._dom_element, name)
def __setattr__(self, name, value): def __setattr__(self, name, value):
@@ -138,7 +141,7 @@ class Element:
# attribute `for` which is a Python keyword, so you can access it on the # attribute `for` which is a Python keyword, so you can access it on the
# Element instance via `for_`). # Element instance via `for_`).
if name.endswith("_"): if name.endswith("_"):
name = name[:-1] name = name[:-1] # noqa: FURB188 No str.removesuffix() in MicroPython.
if name.startswith("on_"): if name.startswith("on_"):
# Ensure on-events are cached in the _on_events dict if the # Ensure on-events are cached in the _on_events dict if the
@@ -152,10 +155,12 @@ class Element:
Get an `Event` instance for the specified event name. Get an `Event` instance for the specified event name.
""" """
if not name.startswith("on_"): if not name.startswith("on_"):
raise ValueError("Event names must start with 'on_'.") msg = "Event names must start with 'on_'."
raise ValueError(msg)
event_name = name[3:] # Remove the "on_" prefix. event_name = name[3:] # Remove the "on_" prefix.
if not hasattr(self._dom_element, event_name): if not hasattr(self._dom_element, event_name):
raise ValueError(f"Element has no '{event_name}' event.") msg = f"Element has no '{event_name}' event."
raise ValueError(msg)
if name in self._on_events: if name in self._on_events:
return self._on_events[name] return self._on_events[name]
# Such an on-event exists in the DOM element, but we haven't yet # Such an on-event exists in the DOM element, but we haven't yet
@@ -203,7 +208,7 @@ class Element:
# We check for list/tuple here and NOT for any iterable as it will match # We check for list/tuple here and NOT for any iterable as it will match
# a JS Nodelist which is handled explicitly below. # a JS Nodelist which is handled explicitly below.
# NodeList. # NodeList.
elif isinstance(item, list) or isinstance(item, tuple): elif isinstance(item, (list, tuple)):
for child in item: for child in item:
self.append(child) self.append(child)
@@ -227,10 +232,11 @@ class Element:
except AttributeError: except AttributeError:
# Nope! This is not an element or a NodeList. # Nope! This is not an element or a NodeList.
raise TypeError( msg = (
f'Element "{item}" is a proxy object, "' f'Element "{item}" is a proxy object, "'
f"but not a valid element or a NodeList." f"but not a valid element or a NodeList."
) )
raise TypeError(msg)
def clone(self, clone_id=None): def clone(self, clone_id=None):
"""Make a clone of the element (clones the underlying DOM object too).""" """Make a clone of the element (clones the underlying DOM object too)."""
@@ -401,9 +407,8 @@ class Options:
new_option = option(**kwargs) new_option = option(**kwargs)
if before: if before and isinstance(before, Element):
if isinstance(before, Element): before = before._dom_element
before = before._dom_element
self._element._dom_element.add(new_option._dom_element, before) self._element._dom_element.add(new_option._dom_element, before)
@@ -463,7 +468,7 @@ class ContainerElement(Element):
) )
for child in list(args) + (children or []): for child in list(args) + (children or []):
if isinstance(child, Element) or isinstance(child, ElementCollection): if isinstance(child, (Element, ElementCollection)):
self.append(child) self.append(child)
else: else:
@@ -493,14 +498,13 @@ class ClassesCollection:
) )
def __iter__(self): def __iter__(self):
for class_name in self._all_class_names(): yield from self._all_class_names()
yield class_name
def __len__(self): def __len__(self):
return len(self._all_class_names()) return len(self._all_class_names())
def __repr__(self): def __repr__(self):
return f"ClassesCollection({repr(self._collection)})" return f"ClassesCollection({self._collection!r})"
def __str__(self): def __str__(self):
return " ".join(self._all_class_names()) return " ".join(self._all_class_names())
@@ -553,7 +557,7 @@ class StyleCollection:
element.style[key] = value element.style[key] = value
def __repr__(self): def __repr__(self):
return f"StyleCollection({repr(self._collection)})" return f"StyleCollection({self._collection!r})"
def remove(self, key): def remove(self, key):
"""Remove a CSS property from the elements in the collection.""" """Remove a CSS property from the elements in the collection."""
@@ -588,7 +592,7 @@ class ElementCollection:
if isinstance(key, int): if isinstance(key, int):
return self._elements[key] return self._elements[key]
elif isinstance(key, slice): if isinstance(key, slice):
return ElementCollection(self._elements[key]) return ElementCollection(self._elements[key])
return self.find(key) return self.find(key)
@@ -1125,7 +1129,8 @@ class video(ContainerElement):
elif isinstance(to, Element): elif isinstance(to, Element):
if to.tag != "canvas": if to.tag != "canvas":
raise TypeError("Element to snap to must be a canvas.") msg = "Element to snap to must be a canvas."
raise TypeError(msg)
elif getattr(to, "tagName", "") == "CANVAS": elif getattr(to, "tagName", "") == "CANVAS":
to = canvas(dom_element=to) to = canvas(dom_element=to)
@@ -1134,10 +1139,12 @@ class video(ContainerElement):
elif isinstance(to, str): elif isinstance(to, str):
nodelist = document.querySelectorAll(to) # NOQA nodelist = document.querySelectorAll(to) # NOQA
if nodelist.length == 0: if nodelist.length == 0:
raise TypeError("No element with selector {to} to snap to.") msg = "No element with selector {to} to snap to."
raise TypeError(msg)
if nodelist[0].tagName != "CANVAS": if nodelist[0].tagName != "CANVAS":
raise TypeError("Element to snap to must be a canvas.") msg = "Element to snap to must be a canvas."
raise TypeError(msg)
to = canvas(dom_element=nodelist[0]) to = canvas(dom_element=nodelist[0])

View File

@@ -24,7 +24,7 @@ class EventMessage:
return value return value
class WebSocket(object): class WebSocket:
CONNECTING = 0 CONNECTING = 0
OPEN = 1 OPEN = 1
CLOSING = 2 CLOSING = 2

View File

@@ -25,10 +25,12 @@ async def create_named_worker(src="", name="", config=None, type="py"):
from json import dumps from json import dumps
if not src: if not src:
raise ValueError("Named workers require src") msg = "Named workers require src"
raise ValueError(msg)
if not name: if not name:
raise ValueError("Named workers require a name") msg = "Named workers require a name"
raise ValueError(msg)
s = _js.document.createElement("script") s = _js.document.createElement("script")
s.type = type s.type = type
@@ -37,7 +39,7 @@ async def create_named_worker(src="", name="", config=None, type="py"):
_set(s, "name", name) _set(s, "name", name)
if config: if config:
_set(s, "config", isinstance(config, str) and config or dumps(config)) _set(s, "config", (isinstance(config, str) and config) or dumps(config))
_js.document.body.append(s) _js.document.body.append(s)
return await workers[name] return await workers[name]

View File

@@ -1,6 +1,6 @@
import numpy import numpy as np
import matplotlib import matplotlib as mpl
# just do something with the packages # just do something with the packages
print(len(dir(numpy))) print(len(dir(np)))
print(len(dir(matplotlib))) print(len(dir(mpl)))

View File

@@ -4,4 +4,4 @@ def runtime_version():
return sys.version return sys.version
__export__ = ['runtime_version'] __export__ = ["runtime_version"]

View File

@@ -8,7 +8,7 @@ if TEST == "implicit":
await fs.mount("/persistent") await fs.mount("/persistent")
print( print(
RUNNING_IN_WORKER and "Worker" or "Main", (RUNNING_IN_WORKER and "Worker") or "Main",
os.listdir("/persistent"), os.listdir("/persistent"),
) )

View File

@@ -35,7 +35,8 @@ import pygame
# see if we can load more than standard BMP # see if we can load more than standard BMP
if not pygame.image.get_extended(): if not pygame.image.get_extended():
raise SystemExit("Sorry, extended image module required") msg = "Sorry, extended image module required"
raise SystemExit(msg)
# game constants # game constants
@@ -56,7 +57,8 @@ def load_image(file):
try: try:
surface = pygame.image.load(file) surface = pygame.image.load(file)
except pygame.error: except pygame.error:
raise SystemExit(f'Could not load image "{file}" {pygame.get_error()}') msg = f'Could not load image "{file}" {pygame.get_error()}'
raise SystemExit(msg)
return surface.convert() return surface.convert()
@@ -66,8 +68,7 @@ def load_sound(file):
return None return None
file = os.path.join(main_dir, "data", file) file = os.path.join(main_dir, "data", file)
try: try:
sound = pygame.mixer.Sound(file) return pygame.mixer.Sound(file)
return sound
except pygame.error: except pygame.error:
print(f"Warning, unable to load, {file}") print(f"Warning, unable to load, {file}")
return None return None
@@ -227,7 +228,7 @@ class Score(pygame.sprite.Sprite):
def update(self): def update(self):
"""We only update the score in update() when it has changed.""" """We only update the score in update() when it has changed."""
if SCORE != self.lastscore: if self.lastscore != SCORE:
self.lastscore = SCORE self.lastscore = SCORE
msg = "Score: %d" % SCORE msg = "Score: %d" % SCORE
self.image = self.font.render(msg, 0, self.color) self.image = self.font.render(msg, 0, self.color)
@@ -296,7 +297,7 @@ async def main(winstyle=0):
# Create Some Starting Values # Create Some Starting Values
global score global score
alienreload = ALIEN_RELOAD alienreload = ALIEN_RELOAD
clock = pygame.Clock() _clock = pygame.Clock()
# initialize our starting sprites # initialize our starting sprites
global SCORE global SCORE
@@ -313,24 +314,23 @@ async def main(winstyle=0):
return return
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
return return
elif event.type == pygame.KEYDOWN: if event.type == pygame.KEYDOWN and event.key == pygame.K_f:
if event.key == pygame.K_f: if not fullscreen:
if not fullscreen: print("Changing to FULLSCREEN")
print("Changing to FULLSCREEN") screen_backup = screen.copy()
screen_backup = screen.copy() screen = pygame.display.set_mode(
screen = pygame.display.set_mode( SCREENRECT.size, winstyle | pygame.FULLSCREEN, bestdepth
SCREENRECT.size, winstyle | pygame.FULLSCREEN, bestdepth )
) screen.blit(screen_backup, (0, 0))
screen.blit(screen_backup, (0, 0)) else:
else: print("Changing to windowed mode")
print("Changing to windowed mode") screen_backup = screen.copy()
screen_backup = screen.copy() screen = pygame.display.set_mode(
screen = pygame.display.set_mode( SCREENRECT.size, winstyle, bestdepth
SCREENRECT.size, winstyle, bestdepth )
) screen.blit(screen_backup, (0, 0))
screen.blit(screen_backup, (0, 0)) pygame.display.flip()
pygame.display.flip() fullscreen = not fullscreen
fullscreen = not fullscreen
keystate = pygame.key.get_pressed() keystate = pygame.key.get_pressed()
@@ -371,7 +371,7 @@ async def main(winstyle=0):
player.kill() player.kill()
# See if shots hit the aliens. # See if shots hit the aliens.
for alien in pygame.sprite.groupcollide(aliens, shots, 1, 1).keys(): for alien in pygame.sprite.groupcollide(aliens, shots, 1, 1):
if pygame.mixer: if pygame.mixer:
boom_sound.play() boom_sound.play()
Explosion(alien) Explosion(alien)

View File

@@ -13,10 +13,7 @@ def test_current_target():
""" """
expected = "py-0" expected = "py-0"
if is_micropython: if is_micropython:
if RUNNING_IN_WORKER: expected = "mpy-w0-target" if RUNNING_IN_WORKER else "mpy-0"
expected = "mpy-w0-target"
else:
expected = "mpy-0"
elif RUNNING_IN_WORKER: elif RUNNING_IN_WORKER:
expected = "py-w0-target" expected = "py-w0-target"
assert current_target() == expected, f"Expected {expected} got {current_target()}" assert current_target() == expected, f"Expected {expected} got {current_target()}"

View File

@@ -256,7 +256,7 @@ async def test_image_display():
""" """
Check an image is displayed correctly. Check an image is displayed correctly.
""" """
mpl = await py_import("matplotlib") _mpl = await py_import("matplotlib")
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
xpoints = [3, 6, 9] xpoints = [3, 6, 9]

View File

@@ -65,7 +65,6 @@ async def test_storage_types():
assert test_store["string"] == "hello" assert test_store["string"] == "hello"
assert isinstance(test_store["string"], str) assert isinstance(test_store["string"], str)
assert test_store["none"] is None assert test_store["none"] is None
assert isinstance(test_store["none"], type(None))
assert test_store["list"] == [1, 2, 3] assert test_store["list"] == [1, 2, 3]
assert isinstance(test_store["list"], list) assert isinstance(test_store["list"], list)
assert test_store["dict"] == {"a": 1, "b": 2} assert test_store["dict"] == {"a": 1, "b": 2}

View File

@@ -248,7 +248,7 @@ class TestCollection:
def test_iter_eq_children(self): def test_iter_eq_children(self):
elements = web.page.find(".multi-elems") elements = web.page.find(".multi-elems")
assert [el for el in elements] == [el for el in elements.elements] assert list(elements) == list(elements.elements)
assert len(elements) == 3 assert len(elements) == 3
def test_slices(self): def test_slices(self):
@@ -427,18 +427,18 @@ class TestInput:
class TestSelect: class TestSelect:
def test_select_options_iter(self): def test_select_options_iter(self):
select = web.page.find(f"#test_select_element_w_options")[0] select = web.page.find("#test_select_element_w_options")[0]
for i, option in enumerate(select.options, 1): for i, option in enumerate(select.options, 1):
assert option.value == f"{i}" assert option.value == f"{i}"
assert option.innerHTML == f"Option {i}" assert option.innerHTML == f"Option {i}"
def test_select_options_len(self): def test_select_options_len(self):
select = web.page.find(f"#test_select_element_w_options")[0] select = web.page.find("#test_select_element_w_options")[0]
assert len(select.options) == 2 assert len(select.options) == 2
def test_select_options_clear(self): def test_select_options_clear(self):
select = web.page.find(f"#test_select_element_to_clear")[0] select = web.page.find("#test_select_element_to_clear")[0]
assert len(select.options) == 3 assert len(select.options) == 3
select.options.clear() select.options.clear()
@@ -447,7 +447,7 @@ class TestSelect:
def test_select_element_add(self): def test_select_element_add(self):
# GIVEN the existing select element with no options # GIVEN the existing select element with no options
select = web.page.find(f"#test_select_element")[0] select = web.page.find("#test_select_element")[0]
# EXPECT the select element to have no options # EXPECT the select element to have no options
assert len(select.options) == 0 assert len(select.options) == 0
@@ -498,20 +498,14 @@ class TestSelect:
# EXPECT the middle option to have the value and html we passed in # EXPECT the middle option to have the value and html we passed in
assert select.options[0].value == "1" assert select.options[0].value == "1"
assert select.options[0].innerHTML == "Option 1" assert select.options[0].innerHTML == "Option 1"
assert ( assert select.options[0].selected == select.options[0]._dom_element.selected
select.options[0].selected assert select.options[0].selected is False
== select.options[0]._dom_element.selected
== False
)
assert select.options[1].value == "2" assert select.options[1].value == "2"
assert select.options[1].innerHTML == "Option 2" assert select.options[1].innerHTML == "Option 2"
assert select.options[2].value == "3" assert select.options[2].value == "3"
assert select.options[2].innerHTML == "Option 3" assert select.options[2].innerHTML == "Option 3"
assert ( assert select.options[2].selected == select.options[2]._dom_element.selected
select.options[2].selected assert select.options[2].selected is True
== select.options[2]._dom_element.selected
== True
)
assert select.options[3].value == "" assert select.options[3].value == ""
assert select.options[3].innerHTML == "" assert select.options[3].innerHTML == ""
@@ -538,7 +532,7 @@ class TestSelect:
def test_select_options_remove(self): def test_select_options_remove(self):
# GIVEN the existing select element with 3 options # GIVEN the existing select element with 3 options
select = web.page.find(f"#test_select_element_to_remove")[0] select = web.page.find("#test_select_element_to_remove")[0]
# EXPECT the select element to have 3 options # EXPECT the select element to have 3 options
assert len(select.options) == 4 assert len(select.options) == 4
@@ -560,7 +554,7 @@ class TestSelect:
def test_select_get_selected_option(self): def test_select_get_selected_option(self):
# GIVEN the existing select element with one selected option # GIVEN the existing select element with one selected option
select = web.page.find(f"#test_select_element_w_options")[0] select = web.page.find("#test_select_element_w_options")[0]
# WHEN we get the selected option # WHEN we get the selected option
selected_option = select.options.selected selected_option = select.options.selected
@@ -568,7 +562,8 @@ class TestSelect:
# EXPECT the selected option to be correct # EXPECT the selected option to be correct
assert selected_option.value == "2" assert selected_option.value == "2"
assert selected_option.innerHTML == "Option 2" assert selected_option.innerHTML == "Option 2"
assert selected_option.selected == selected_option._dom_element.selected == True assert selected_option.selected == selected_option._dom_element.selected
assert selected_option.selected is True
class TestElements: class TestElements:
@@ -625,7 +620,8 @@ class TestElements:
el = klass(*args, **kwargs) el = klass(*args, **kwargs)
container.append(el) container.append(el)
except Exception as e: except Exception as e:
assert False, f"Failed to create element {el_type}: {e}" msg = f"Failed to create element {el_type}: {e}"
raise AssertionError(msg)
# Let's keep the tag in 2 variables, one for the selector and another to # Let's keep the tag in 2 variables, one for the selector and another to
# check the return tag from the selector # check the return tag from the selector

View File

@@ -1,3 +1,9 @@
[tool.codespell] [tool.codespell]
ignore-words-list = "afterall" ignore-words-list = "afterall"
skip = "*.js,*.json" skip = "*.js,*.json"
[tool.ruff]
line-length = 114
lint.select = ["C4", "C90", "E", "EM", "F", "PIE", "PYI", "PLC", "Q", "RET", "W"]
lint.ignore = ["E402", "E722", "E731", "E741", "F401", "F704", "F811", "F821"]
lint.mccabe.max-complexity = 27