Skip to content

Commit

Permalink
Conform to strict pyright checking
Browse files Browse the repository at this point in the history
- Need to drop 3.11 support
  • Loading branch information
joce committed Nov 15, 2023
1 parent 0444987 commit efc0ae7
Show file tree
Hide file tree
Showing 19 changed files with 77 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "0.2",
"language": "en",
"ignorePaths": [".cspell.json"],
"ignorePaths": [".cspell.json", "pyproject.toml"],
"dictionaryDefinitions": [],
"dictionaries": [],
"words": ["legault", "renderable", "triggerable"],
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
python-version: ["3.12"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
Expand Down
4 changes: 4 additions & 0 deletions .todo
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@
Other:
DevOps:
☐ review .pylintc file

Tests:
yfinance:
☐ test YClient
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
"editor.defaultFormatter": "ms-python.black-formatter"
},
"python.formatting.provider": "black",
"python.analysis.typeCheckingMode": "basic",
"python.analysis.typeCheckingMode": "strict",
"python.languageServer": "Pylance",
"autoDocstring.startOnNewLine": true,
"python.analysis.autoFormatStrings": true,
"python.analysis.completeFunctionParens": true,
"python.analysis.diagnosticMode": "workspace",
"python.analysis.importFormat": "relative"
"python.analysis.importFormat": "relative",
"python.analysis.autoImportCompletions": true
}
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[tool.pytest.ini_options]
pythonpath = "src"
testpaths = ["src", "tests"]

[tool.pyright]
include = ["src", "tests"]
typeCheckingMode = "strict"
2 changes: 0 additions & 2 deletions pytest.ini

This file was deleted.

18 changes: 10 additions & 8 deletions src/appui/_quote_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
This module contains the QuoteTable class which is a DataTable for displaying quotes.
"""

from typing import Any
from typing import Any, override

from rich.style import Style
from rich.text import Text
Expand All @@ -14,7 +14,7 @@
from .quote_table_state import QuoteTableState


class QuoteTable(DataTable):
class QuoteTable(DataTable[Text]):
"""A DataTable for displaying quotes."""

_GAINING_COLOR: str = "#00DD00"
Expand All @@ -33,8 +33,7 @@ def __init__(self, state: QuoteTableState) -> None:

def __del__(self) -> None:
# Make sure the query thread is stopped
if self._state is not None:
self._state.query_thread_running = False
self._state.query_thread_running = False

def on_mount(self) -> None:
"""The event handler called when the widget is added to the app."""
Expand All @@ -55,6 +54,7 @@ def on_mount(self) -> None:
self._version = self._state.version - 1
self._state.query_thread_running = True

@override
def _on_unmount(self) -> None:
"""
The event handler called when the widget is removed from the app.
Expand Down Expand Up @@ -85,7 +85,7 @@ def _update_table(self) -> None:
# rows as needed
quote_key: str = str(i)
# Update existing rows
if quote_key in self.rows:
if quote_key in self.rows: # pyright: ignore [reportUnnecessaryContains]
for j, cell in enumerate(quote.values):
self.update_cell(
quote_key,
Expand Down Expand Up @@ -179,6 +179,7 @@ def watch_hover_coordinate(self, old: Coordinate, value: Coordinate) -> None:

super().watch_hover_coordinate(old, value)

@override
def _render_cell(
self,
row_index: int,
Expand All @@ -199,18 +200,19 @@ def _render_cell(
row_index, column_index, base_style, width, cursor, hover
)

@override
def watch_cursor_coordinate(
self, _: Coordinate, new_coordinate: Coordinate
self, old_coordinate: Coordinate, new_coordinate: Coordinate
) -> None:
"""
Watch the cursor coordinate and update the current row accordingly.
Args:
_ (Coordinate): The old coordinate. Unused.
old_coordinate (Coordinate): The old coordinate. Unused.
new_coordinate (Coordinate): The current cursor coordinate.
"""

super().watch_cursor_coordinate(_, new_coordinate)
super().watch_cursor_coordinate(old_coordinate, new_coordinate)
self._state.current_row = new_coordinate.row

def on_data_table_header_selected(self, evt: DataTable.HeaderSelected) -> None:
Expand Down
1 change: 0 additions & 1 deletion src/appui/quote_table_state.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""The state of the quote table."""

import logging
from re import S
from threading import Lock, Thread
from time import monotonic, sleep
from typing import Any, Callable, Optional
Expand Down
18 changes: 11 additions & 7 deletions src/appui/stockyardapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import json
import logging
from io import TextIOWrapper
from typing import Any, Optional
from typing import Any, ClassVar, Optional

from textual import work
from textual.app import App, ComposeResult
from textual.binding import BindingType
from textual.containers import Horizontal
from textual.css.query import NoMatches
from textual.logging import TextualHandler
from textual.widgets import Footer, LoadingIndicator
from textual.worker import Worker
Expand All @@ -25,12 +26,12 @@
)


class StockyardApp(App):
class StockyardApp(App[None]):
"""A Textual app for the Stockyard application."""

CSS_PATH = "./stockyardapp.tcss"

BINDINGS: list[BindingType] = [
BINDINGS: ClassVar[list[BindingType]] = [
("q", "exit", "Exit"),
]

Expand Down Expand Up @@ -109,11 +110,14 @@ def _prime_yfinance(self) -> None:
def _finish_loading(self) -> None:
"""Finish loading."""

indicator: LoadingIndicator = self.query_one(
"LoadingIndicator", LoadingIndicator
)
if indicator is not None:
try:
indicator: LoadingIndicator = self.query_one(
"LoadingIndicator", LoadingIndicator
)
indicator.remove()
except NoMatches:
# No indicator was found
pass

qt: QuoteTable = QuoteTable(self._state.quote_table_state)
self.mount(qt, before="#clock-footer")
Expand Down
3 changes: 1 addition & 2 deletions src/appui/stockyardapp_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ def load_config(self, config: dict[str, Any]) -> None:
else None
)

if quote_table_config is not None:
self._quote_table_state.load_config(quote_table_config)
self._quote_table_state.load_config(quote_table_config)

try:
self._time_display = get_enum_member(TimeFormat, time_format)
Expand Down
16 changes: 8 additions & 8 deletions src/yfinance/_yclient.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""This module provides a client for the Yahoo! Finance API."""

# pylint: disable=line-too-long

import json
import logging
from datetime import datetime, timedelta
Expand All @@ -10,8 +12,6 @@
import requests
from requests.cookies import RequestsCookieJar

# pylint: disable=line-too-long


class YClient:
"""Yahoo! Finance API client."""
Expand Down Expand Up @@ -173,7 +173,7 @@ def __get_cookies_eu(self) -> RequestsCookieJar:
# Look in the history to find the right cookie
gucs_cookie: RequestsCookieJar = RequestsCookieJar()
for hist in response.history:
if hist.cookies.get("GUCS") is not None:
if hist.cookies.get("GUCS") is not None: # pyright: ignore
gucs_cookie: RequestsCookieJar = hist.cookies
break

Expand Down Expand Up @@ -219,7 +219,7 @@ def __get_cookies_eu(self) -> RequestsCookieJar:
allow_redirects=True,
) as response:
for hist in response.history:
if hist.cookies.get("A3") is not None:
if hist.cookies.get("A3") is not None: # pyright: ignore
return hist.cookies

return RequestsCookieJar()
Expand All @@ -239,7 +239,7 @@ def __refresh_crumb(self) -> None:
except requests.exceptions.HTTPError as e:
logging.exception("Can't fetch crumb: %s", e)

if self._crumb is not None and self._crumb != "":
if self._crumb != "":
logging.debug(
"Crumb refreshed: %s. Expires on %s",
self._crumb,
Expand Down Expand Up @@ -275,7 +275,7 @@ def _execute_request(self, api_call: str) -> dict[str, Any]:
res_body: str = response.text
logging.debug("Response: %s", res_body)

if res_body is None or res_body == "":
if res_body == "":
logging.error("Can't parse response")
return {}

Expand Down Expand Up @@ -314,10 +314,10 @@ def call(
if query_params is None:
query_params = {}

if self._crumb is not None and self._crumb != "":
if self._crumb != "":
query_params["crumb"] = self._crumb

if query_params is not None and len(query_params) > 0:
if len(query_params) > 0:
query_string = urlencode(query_params)
api_url += "?" + query_string

Expand Down
4 changes: 2 additions & 2 deletions src/yfinance/yfinance.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def retrieve_quotes(self, symbols: list[str]) -> list[YQuote]:
list[YQuote]: The quotes for the given symbols.
"""

if symbols is None or len(symbols) == 0:
if len(symbols) == 0:
logging.error("No symbols provided")
raise ValueError("No symbols provided")

Expand All @@ -43,7 +43,7 @@ def retrieve_quotes(self, symbols: list[str]) -> list[YQuote]:
self._QUOTE_API, {"symbols": ",".join([s.strip() for s in symbols])}
)

if json_data is None or "quoteResponse" not in json_data:
if "quoteResponse" not in json_data:
logging.error("No quote response from Yahoo!")
return []

Expand Down
3 changes: 1 addition & 2 deletions src/yfinance/yquote.py
Original file line number Diff line number Diff line change
Expand Up @@ -1118,8 +1118,6 @@ def __init__(self, input_data: dict[str, Any]) -> None:
Raises:
ValueError: if input_data is None
"""
if input_data is None:
raise ValueError("input_data cannot be None")

# load the values of the dictionary into the object, if they exist. Otherwise set them to None

Expand Down Expand Up @@ -1501,6 +1499,7 @@ def __str__(self) -> str:
Returns:
str: a string representation of the object
"""

return (
f"YQuote({self._symbol}: {self._regular_market_price}"
+ f" ({self._regular_market_change_percent:.2f}%)"
Expand Down
2 changes: 2 additions & 0 deletions tests/appui/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Test helpers.
"""

# pyright: reportPrivateUsage=none

from appui._formatting import _NO_VALUE


Expand Down
4 changes: 2 additions & 2 deletions tests/appui/test_enums.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pylint: disable=missing-module-docstring
# pylint: disable=missing-function-docstring
# pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring
# pylint: disable=missing-module-docstring

from enum import Enum
from typing import Any, Type
Expand Down
12 changes: 8 additions & 4 deletions tests/appui/test_formatting.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# pylint: disable=protected-access
# pylint: disable=missing-module-docstring
# pylint: disable=protected-access

# pyright: reportPrivateUsage=none

from typing import Optional

import pytest

Expand All @@ -19,7 +23,7 @@
(-892.76324765, "-892.76%"),
],
)
def test_as_percent(input_value, expected_output):
def test_as_percent(input_value: float, expected_output: str):
assert fmt.as_percent(input_value) == expected_output


Expand All @@ -31,7 +35,7 @@ def test_as_percent(input_value, expected_output):
(1234.5678, 3, "1234.568"),
],
)
def test_as_float(input_value, precision, expected_output):
def test_as_float(input_value: float, precision: Optional[int], expected_output: str):
if precision is None:
assert fmt.as_float(input_value) == expected_output
else:
Expand All @@ -51,5 +55,5 @@ def test_as_float(input_value, precision, expected_output):
(1000000000000, "1.00T"),
],
)
def test_as_shrunk_int(input_value, expected_output):
def test_as_shrunk_int(input_value: int, expected_output: str):
assert fmt.as_shrunk_int(input_value) == expected_output
6 changes: 4 additions & 2 deletions tests/appui/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# pylint: disable=protected-access
# pylint: disable=missing-module-docstring
# pylint: disable=protected-access

# pyright: reportPrivateUsage=none

import pytest

Expand Down Expand Up @@ -51,5 +53,5 @@
("974.01T", "999.87T", -1),
],
)
def test_compare_shrunken_ints(a, b, expected_output):
def test_compare_shrunken_ints(a: str, b: str, expected_output: int):
assert compare_shrunken_ints(a, b) == expected_output
Loading

0 comments on commit efc0ae7

Please sign in to comment.