Skip to content

Commit

Permalink
feat: improve type hinting (#240)
Browse files Browse the repository at this point in the history
  • Loading branch information
merydian committed May 15, 2024
1 parent 0e0ad02 commit 85ebed2
Show file tree
Hide file tree
Showing 21 changed files with 251 additions and 160 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ RELEASING:
- Vertices on canvas not depicted fully with n having more than one digit in length ([#235](https://github.com/GIScience/orstools-qgis-plugin/issues/235))
- Replace qt QSettings with QgsSettings for centralized configuration management ([#239](https://github.com/GIScience/orstools-qgis-plugin/issues/239))
- Fix: Point Annotations stay after saving project and not deleting them manually([#229](https://github.com/GIScience/orstools-qgis-plugin/issues/229))
- Improved type hints

### Added
- Add support for decimal ranges with isochrones([#237](https://github.com/GIScience/orstools-qgis-plugin/issues/237))
Expand Down
7 changes: 4 additions & 3 deletions ORStools/ORStoolsPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
***************************************************************************/
"""

from qgis.gui import QgisInterface
from qgis.core import QgsApplication, QgsSettings
from PyQt5.QtCore import QTranslator, qVersion, QCoreApplication
import os.path
Expand All @@ -40,7 +41,7 @@ class ORStools:

# noinspection PyTypeChecker,PyArgumentList,PyCallByClass

def __init__(self, iface):
def __init__(self, iface: QgisInterface) -> None:
"""Constructor.
:param iface: An interface instance that will be passed to this class
Expand All @@ -65,13 +66,13 @@ def __init__(self, iface):
if qVersion() > "4.3.3":
QCoreApplication.installTranslator(self.translator)

def initGui(self):
def initGui(self) -> None:
"""Create the menu entries and toolbar icons inside the QGIS GUI."""

QgsApplication.processingRegistry().addProvider(self.provider)
self.dialog.initGui()

def unload(self):
def unload(self) -> None:
"""remove menu entry and toolbar icons"""
QgsApplication.processingRegistry().removeProvider(self.provider)
self.dialog.unload()
16 changes: 12 additions & 4 deletions ORStools/common/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import random
import time
from datetime import datetime, timedelta
from typing import Union, Dict, List, Optional
from urllib.parse import urlencode

from PyQt5.QtCore import QObject, pyqtSignal
Expand All @@ -48,7 +49,7 @@
class Client(QObject):
"""Performs requests to the ORS API services."""

def __init__(self, provider=None, agent=None):
def __init__(self, provider: Optional[dict] = None, agent: Optional[str] = None) -> None:
"""
:param provider: A openrouteservice provider from config.yml
:type provider: dict
Expand Down Expand Up @@ -87,7 +88,14 @@ def __init__(self, provider=None, agent=None):

overQueryLimit = pyqtSignal()

def request(self, url, params, first_request_time=None, retry_counter=0, post_json=None):
def request(
self,
url: str,
params: dict,
first_request_time: Optional[datetime.time] = None,
retry_counter: int = 0,
post_json: Optional[dict] = None,
):
"""Performs HTTP GET/POST with credentials, returning the body as
JSON.
Expand Down Expand Up @@ -194,7 +202,7 @@ def request(self, url, params, first_request_time=None, retry_counter=0, post_js

return json.loads(content.decode("utf-8"))

def _check_status(self):
def _check_status(self) -> None:
"""
Casts JSON response to dict
Expand Down Expand Up @@ -231,7 +239,7 @@ def _check_status(self):
elif status_code != 200:
raise exceptions.GenericServerError(str(status_code), message)

def _generate_auth_url(self, path, params):
def _generate_auth_url(self, path: str, params: Union[Dict, List]) -> str:
"""Returns the path and query string portion of the request URL, first
adding any necessary parameters.
Expand Down
35 changes: 21 additions & 14 deletions ORStools/common/directions_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@

from itertools import product
from qgis.core import QgsPoint, QgsPointXY, QgsGeometry, QgsFeature, QgsFields, QgsField
from typing import List
from typing import List, Generator, Tuple, Any, Optional

from PyQt5.QtCore import QVariant

from ORStools.utils import convert


def get_request_point_features(route_dict, row_by_row):
def get_request_point_features(route_dict: dict, row_by_row: str) -> Generator[List, Tuple, None]:
"""
Processes input point features depending on the layer to layer relation in directions settings
Expand Down Expand Up @@ -75,12 +75,12 @@ def get_request_point_features(route_dict, row_by_row):


def get_fields(
from_type=QVariant.String,
to_type=QVariant.String,
from_name="FROM_ID",
to_name="TO_ID",
line=False,
):
from_type: QVariant.Type = QVariant.String,
to_type: QVariant.Type = QVariant.String,
from_name: str = "FROM_ID",
to_name: str = "TO_ID",
line: bool = False,
) -> QgsFields:
"""
Builds output fields for directions response layer.
Expand Down Expand Up @@ -117,8 +117,13 @@ def get_fields(


def get_output_feature_directions(
response, profile, preference, options=None, from_value=None, to_value=None
):
response: dict,
profile: str,
preference: str,
options: Optional[str] = None,
from_value: Any = None,
to_value: Any = None,
) -> QgsFeature:
"""
Build output feature based on response attributes for directions endpoint.
Expand Down Expand Up @@ -165,7 +170,9 @@ def get_output_feature_directions(
return feat


def get_output_features_optimization(response, profile, from_value=None):
def get_output_features_optimization(
response: dict, profile: str, from_value: Any = None
) -> QgsFeature:
"""
Build output feature based on response attributes for optimization endpoint.
Expand Down Expand Up @@ -205,9 +212,9 @@ def get_output_features_optimization(response, profile, from_value=None):

def build_default_parameters(
preference: str,
point_list: List[QgsPointXY] = None,
coordinates: list = None,
options: dict = None,
point_list: Optional[List[QgsPointXY]] = None,
coordinates: Optional[list] = None,
options: Optional[dict] = None,
) -> dict:
"""
Build default parameters for directions endpoint. Either uses a list of QgsPointXY to create the coordinates
Expand Down
22 changes: 16 additions & 6 deletions ORStools/common/isochrones_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
***************************************************************************/
"""

from typing import Any, Generator

from qgis._core import QgsMapLayer
from qgis.core import (
QgsPointXY,
QgsFeature,
Expand All @@ -49,7 +52,7 @@
class Isochrones:
"""convenience class to build isochrones"""

def __init__(self):
def __init__(self) -> None:
# Will all be set in self.set_parameters(), bcs Processing Algo has to initialize this class before it
# knows about its own parameters
self.profile = None
Expand All @@ -60,8 +63,13 @@ def __init__(self):
self.field_dimension_name = None

def set_parameters(
self, profile, dimension, factor, id_field_type=QVariant.String, id_field_name="ID"
):
self,
profile: str,
dimension: str,
factor: int,
id_field_type: QVariant.String = QVariant.String,
id_field_name: str = "ID",
) -> None:
"""
Sets all parameters defined in __init__, because processing algorithm calls this class when it doesn't know
its parameters yet.
Expand Down Expand Up @@ -89,7 +97,7 @@ def set_parameters(

self.field_dimension_name = "AA_MINS" if self.dimension == "time" else "AA_METERS"

def get_fields(self):
def get_fields(self) -> QgsFields:
"""
Set all fields for output isochrone layer.
Expand All @@ -106,7 +114,9 @@ def get_fields(self):

return fields

def get_features(self, response, id_field_value):
def get_features(
self, response: dict, id_field_value: Any
) -> Generator[QgsFeature, None, None]:
"""
Generator to return output isochrone features from response.
Expand Down Expand Up @@ -158,7 +168,7 @@ def get_features(self, response, id_field_value):
#
# return dissolved

def stylePoly(self, layer):
def stylePoly(self, layer: QgsMapLayer) -> None:
"""
Style isochrone polygon layer.
Expand Down
22 changes: 12 additions & 10 deletions ORStools/common/networkaccessmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def __init__(
exception_class=None,
debug=True,
timeout=60,
):
) -> None:
self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
self.authid = authid
self.reply = None
Expand All @@ -175,17 +175,19 @@ def __init__(
)
self.timeout = timeout

def msg_log(self, msg):
def msg_log(self, msg: str) -> None:
if self.debug:
QgsMessageLog.logMessage(msg, "NetworkAccessManager")

def httpResult(self):
def httpResult(self) -> None:
return self.http_call_result

def auth_manager(self):
def auth_manager(self) -> None:
return QgsApplication.authManager()

def request(self, url, method="GET", body=None, headers=None, blocking=True):
def request(
self, url: str, method: str = "GET", body=None, headers=None, blocking: bool = True
):
"""
Make a network request by calling QgsNetworkAccessManager.
redirections argument is ignored and is here only for httplib2 compatibility.
Expand Down Expand Up @@ -279,19 +281,19 @@ def request(self, url, method="GET", body=None, headers=None, blocking=True):

return self.http_call_result, self.http_call_result.content

def downloadProgress(self, bytesReceived, bytesTotal):
def downloadProgress(self, bytesReceived, bytesTotal) -> None:
"""Keep track of the download progress"""
# self.msg_log("downloadProgress %s of %s ..." % (bytesReceived, bytesTotal))
pass

# noinspection PyUnusedLocal
def requestTimedOut(self, reply):
def requestTimedOut(self, reply) -> None:
"""Trap the timeout. In Async mode requestTimedOut is called after replyFinished"""
# adapt http_call_result basing on receiving qgs timer timout signal
self.exception_class = RequestsExceptionTimeout
self.http_call_result.exception = RequestsExceptionTimeout("Timeout error")

def replyFinished(self):
def replyFinished(self) -> None:
err = self.reply.error()
httpStatus = self.reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
httpStatusMessage = self.reply.attribute(QNetworkRequest.HttpReasonPhraseAttribute)
Expand Down Expand Up @@ -401,7 +403,7 @@ def replyFinished(self):
else:
self.msg_log("Reply was already deleted ...")

def sslErrors(self, ssl_errors):
def sslErrors(self, ssl_errors) -> None:
"""
Handle SSL errors, logging them if debug is on and ignoring them
if disable_ssl_certificate_validation is set.
Expand All @@ -412,7 +414,7 @@ def sslErrors(self, ssl_errors):
if self.disable_ssl_certificate_validation:
self.reply.ignoreSslErrors()

def abort(self):
def abort(self) -> None:
"""
Handle request to cancel HTTP call
"""
Expand Down
Loading

0 comments on commit 85ebed2

Please sign in to comment.