Skip to content

Commit

Permalink
refactor: Several type utils -> _type_defence
Browse files Browse the repository at this point in the history
  • Loading branch information
r-leyshon committed Sep 11, 2023
1 parent 9c3f1b4 commit d9da952
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 85 deletions.
7 changes: 3 additions & 4 deletions src/transport_performance/gtfs/gtfs_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
from transport_performance.utils.defence import (
_is_expected_filetype,
_check_list,
_dataframe_defence,
_bool_defence,
_type_defence,
)


Expand Down Expand Up @@ -102,8 +101,8 @@ def convert_pandas_to_plotly(
}
}
# defences
_dataframe_defence(df, "df")
_bool_defence(return_html, "return_html")
_type_defence(df, "df", pd.DataFrame)
_type_defence(return_html, "return_html", bool)
if scheme not in list(schemes.keys()):
raise LookupError(
f"{scheme} is not a valid colour scheme."
Expand Down
9 changes: 4 additions & 5 deletions src/transport_performance/gtfs/report/report_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
import os

from transport_performance.utils.defence import (
_bool_defence,
_string_defence,
_type_defence,
_handle_path_like,
_check_parent_dir_exists,
)
Expand Down Expand Up @@ -92,9 +91,9 @@ def insert(
None
"""
_string_defence(placeholder, "placeholder")
_string_defence(value, "value")
_bool_defence(replace_multiple, "replace_multiple")
_type_defence(placeholder, "placeholder", str)
_type_defence(value, "value", str)
_type_defence(replace_multiple, "replace_multiple", bool)
occurences = len(self.template.split(f"[{placeholder}]")) - 1
if occurences > 1 and not replace_multiple:
raise ValueError(
Expand Down
4 changes: 2 additions & 2 deletions src/transport_performance/gtfs/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import requests
import warnings

from transport_performance.utils.defence import _url_defence, _bool_defence
from transport_performance.utils.defence import _url_defence, _type_defence

warnings.filterwarnings(
action="ignore", category=DeprecationWarning, module=".*pkg_resources"
Expand Down Expand Up @@ -98,7 +98,7 @@ def scrape_route_type_lookup(
for url in [gtfs_url, ext_spec_url]:
_url_defence(url)

_bool_defence(extended_schema, "extended_schema")
_type_defence(extended_schema, "extended_schema", bool)
# Get the basic scheme lookup
resp_txt = _get_response_text(gtfs_url)
soup = BeautifulSoup(resp_txt, "html.parser")
Expand Down
33 changes: 14 additions & 19 deletions src/transport_performance/gtfs/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,7 @@
_check_namespace_export,
_check_parent_dir_exists,
_check_column_in_df,
_bool_defence,
_integer_defence,
_string_defence,
_dataframe_defence,
_dict_defence,
_string_and_nonetype_defence,
_type_defence,
)

from transport_performance.gtfs.report.report_utils import (
Expand Down Expand Up @@ -733,17 +728,17 @@ def _plot_summary(
"""
# parameter type defences
_dataframe_defence(summary_df, "summary_df")
_string_defence(day_column, "day_column")
_string_defence(target_column, "target_column")
_dict_defence(plotly_kwargs, "plotly_kwargs")
_bool_defence(return_html, "return_html")
_integer_defence(width, "width")
_integer_defence(height, "height")
_string_and_nonetype_defence(xlabel, "xlabel")
_string_and_nonetype_defence(ylabel, "ylabel")
_bool_defence(save_html, "save_html")
_bool_defence(save_image, "save_iamge")
_type_defence(summary_df, "summary_df", pd.DataFrame)
_type_defence(day_column, "day_column", str)
_type_defence(target_column, "target_column", str)
_type_defence(plotly_kwargs, "plotly_kwargs", dict)
_type_defence(return_html, "return_html", bool)
_type_defence(width, "width", int)
_type_defence(height, "height", int)
_type_defence(xlabel, "xlabel", (str, type(None)))
_type_defence(ylabel, "ylabel", (str, type(None)))
_type_defence(save_html, "save_html", bool)
_type_defence(save_image, "save_iamge", bool)
_check_parent_dir_exists(save_pth, "save_pth", create=True)

# orientation input defences
Expand Down Expand Up @@ -1241,8 +1236,8 @@ def html_report(
None
"""
_bool_defence(overwrite, "overwrite")
_string_defence(summary_type, "summary_type")
_type_defence(overwrite, "overwrite", bool)
_type_defence(summary_type, "summary_type", str)
set_up_report_dir(path=report_dir, overwrite=overwrite)
summary_type = summary_type.lower()
if summary_type not in ["mean", "min", "max", "median"]:
Expand Down
4 changes: 2 additions & 2 deletions src/transport_performance/osm/osm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pyprojroot import here

from transport_performance.utils.defence import (
_bool_defence,
_type_defence,
_check_list,
_check_parent_dir_exists,
_is_expected_filetype,
Expand Down Expand Up @@ -54,7 +54,7 @@ def filter_osm(
"tag_filter": tag_filter,
"install_osmosis": install_osmosis,
}.items():
_bool_defence(val, param_nm=nm)
_type_defence(val, nm, bool)
# check bbox values makes sense, else osmosis will error
if not bbox[0] < bbox[2]:
raise ValueError(
Expand Down
68 changes: 20 additions & 48 deletions src/transport_performance/utils/defence.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,68 +150,40 @@ def _check_namespace_export(pkg=np, func=np.min):

def _url_defence(url):
"""Defence checking. Not exported."""
if not isinstance(url, str):
raise TypeError(f"url {url} expected string, instead got {type(url)}")
elif not url.startswith((r"http://", r"https://")):
_type_defence(url, "url", str)
if not url.startswith((r"http://", r"https://")):
raise ValueError(f"url string expected protocol, instead found {url}")

return None


def _bool_defence(some_bool, param_nm):
"""Defence checking. Not exported."""
if not isinstance(some_bool, bool):
raise TypeError(
f"`{param_nm}` expected boolean. Got {type(some_bool)}"
)

return None


def _string_defence(string, param_nm):
"""Defence checking. Not exported."""
if not isinstance(string, str):
raise TypeError(f"'{param_nm}' expected str. Got {type(string)}")

return None


def _integer_defence(some_int, param_nm):
"""Defence checking. Not exported."""
if not isinstance(some_int, int):
raise TypeError(f"'{param_nm}' expected int. Got {type(some_int)}")

return None
def _type_defence(some_object, param_nm, types) -> None:
"""Defence checking utility. Can handle NoneType.
Parameters
----------
some_object : Any
Object to test with isinstance.
param_nm : str
A name for the parameter. Useful when this utility is used in a wrapper
to inherit the parent's parameter name and present in error message.
types : type or tuple
A type or a tuple of types to test `some_object` against.
def _dict_defence(some_dict, param_nm):
"""Defence checking. Not exported."""
if not isinstance(some_dict, dict):
raise TypeError(f"'{param_nm}' expected dict. Got {type(some_dict)}")

return None

Raises
------
TypeError
`some_object` is not of type `types`.
def _dataframe_defence(some_df, param_nm):
"""Defence checking. Not exported."""
if not isinstance(some_df, pd.DataFrame):
"""
if not isinstance(some_object, types):
raise TypeError(
f"'{param_nm}' expected pd.DataFrame. Got {type(some_df)}"
f"`{param_nm}` expected {types}. Got {type(some_object)}"
)

return None


# main use case for this is to avoid
# complexity limits in
# GtfsInstance._plot_summary()
def _string_and_nonetype_defence(some_value, param_nm):
if not isinstance(some_value, (str, type(None))):
raise TypeError(
f"'{param_nm}' expected type str. Found type {type(some_value)}"
)


def _check_list(ls, param_nm, check_elements=True, exp_type=str):
"""Check a list and its elements for type.
Expand Down
6 changes: 3 additions & 3 deletions tests/gtfs/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ def test_defensive_exceptions(self):
"""Test the defensive checks raise as expected."""
with pytest.raises(
TypeError,
match=r"url 1 expected string, instead got <class 'int'>",
match=r"`url` expected <class 'str'>. Got <class 'int'>",
):
scrape_route_type_lookup(gtfs_url=1)
with pytest.raises(
TypeError,
match=r"url False expected string, instead got <class 'bool'>",
match=r"`url` expected <class 'str'>. Got <class 'bool'>",
):
scrape_route_type_lookup(ext_spec_url=False)
with pytest.raises(
Expand All @@ -62,7 +62,7 @@ def test_defensive_exceptions(self):
scrape_route_type_lookup(gtfs_url="foobar")
with pytest.raises(
TypeError,
match=r"`extended_schema` expected boolean. Got <class 'str'>",
match=r"`extended_schema` .* <class 'bool'>. Got <class 'str'>",
):
scrape_route_type_lookup(extended_schema="True")

Expand Down
5 changes: 3 additions & 2 deletions tests/osm/test_osm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ def test_filter_osm_defense(self):
# out_pth is not a path_like
filter_osm(out_pth=False)
with pytest.raises(
TypeError, match="`tag_filter` expected boolean. Got <class 'int'>"
TypeError,
match="`tag_filter` expected <class 'bool'>. Got <class 'int'>",
):
# check for boolean defense
filter_osm(tag_filter=1)
with pytest.raises(
TypeError,
match="`install_osmosis` expected boolean. Got <class 'str'>",
match="`install_osmosis` .* <class 'bool'>. Got <class 'str'>",
):
# check for boolean defense
filter_osm(install_osmosis="False")
Expand Down

0 comments on commit d9da952

Please sign in to comment.