diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2d180fc1..53643236 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,13 +8,35 @@ repos: - id: pyupgrade args: ["--py310-plus"] + - repo: https://github.com/frostming/fix-future-annotations + rev: 0.5.0 + hooks: + - id: fix-future-annotations + - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.12 + rev: v0.13.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.18.2 + hooks: + - id: mypy + args: [--config-file=pyproject.toml] + additional_dependencies: + - boto3-stubs[essential,lambda]==1.40.41 + - decouple-types==1.0.2 + - types-PyYAML==6.0.12.20250915 + - types-beautifulsoup4==4.12.0.20250516 + - types-html5lib==1.1.11.20250917 + - types-openpyxl==3.1.5.20250919 + - types-python-dateutil==2.9.0.20250822 + - types-toml==0.10.8.20240310 + - types-xmltodict==1.0.1.20250920 + exclude: "tests" + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: diff --git a/bandit.yml b/bandit.yml new file mode 100644 index 00000000..75d550c3 --- /dev/null +++ b/bandit.yml @@ -0,0 +1 @@ +skips: ['B101'] diff --git a/benedict/core/clean.py b/benedict/core/clean.py index 3246ef7a..49f1578b 100644 --- a/benedict/core/clean.py +++ b/benedict/core/clean.py @@ -1,4 +1,16 @@ -def _clean_dict(d, strings, collections): +from __future__ import annotations + +from collections.abc import MutableMapping, MutableSequence +from typing import Any, TypeVar + +_K = TypeVar("_K") +_V = TypeVar("_V") +_T = TypeVar("_T") + + +def _clean_dict( + d: MutableMapping[_K, _V], strings: bool, collections: bool +) -> MutableMapping[_K, _V]: keys = list(d.keys()) for key in keys: d[key] = _clean_value(d[key], strings=strings, collections=collections) @@ -7,7 +19,9 @@ def _clean_dict(d, strings, collections): return d -def _clean_list(ls, strings, collections): +def _clean_list( + ls: MutableSequence[_T], strings: bool, collections: bool +) -> MutableSequence[_T]: for i in range(len(ls) - 1, -1, -1): ls[i] = _clean_value(ls[i], strings=strings, collections=collections) if ls[i] is None: @@ -15,7 +29,7 @@ def _clean_list(ls, strings, collections): return ls -def _clean_set(values, strings, collections): +def _clean_set(values: set[_T], strings: bool, collections: bool) -> set[_T]: return { value for value in values @@ -23,11 +37,13 @@ def _clean_set(values, strings, collections): } -def _clean_str(s, strings, collections): +def _clean_str(s: str, strings: bool, collections: bool) -> str | None: return s if s and s.strip() else None -def _clean_tuple(values, strings, collections): +def _clean_tuple( + values: tuple[_T, ...], strings: bool, collections: bool +) -> tuple[_T, ...]: return tuple( value for value in values @@ -35,13 +51,15 @@ def _clean_tuple(values, strings, collections): ) -def _clean_value(value, strings, collections): +def _clean_value(value: Any, strings: bool, collections: bool) -> Any: if value is None: return value - elif isinstance(value, list) and collections: + elif isinstance(value, MutableSequence) and collections: value = _clean_list(value, strings=strings, collections=collections) or None - elif isinstance(value, dict) and collections: - value = _clean_dict(value, strings=strings, collections=collections) or None + elif isinstance(value, MutableMapping) and collections: + value = ( + _clean_dict(dict(value), strings=strings, collections=collections) or None + ) elif isinstance(value, set) and collections: value = _clean_set(value, strings=strings, collections=collections) or None elif isinstance(value, str) and strings: @@ -51,5 +69,5 @@ def _clean_value(value, strings, collections): return value -def clean(d, strings=True, collections=True): +def clean(d: Any, strings: bool = True, collections: bool = True) -> Any: return _clean_dict(d, strings=strings, collections=collections) diff --git a/benedict/core/clone.py b/benedict/core/clone.py index ed34f84f..38539be3 100644 --- a/benedict/core/clone.py +++ b/benedict/core/clone.py @@ -1,8 +1,18 @@ +from __future__ import annotations + import copy +from collections.abc import MutableMapping +from typing import Any, TypeVar + +_T = TypeVar("_T") -def clone(obj, empty=False, memo=None): +def clone( + obj: _T, + empty: bool = False, + memo: dict[int, Any] | None = None, +) -> _T: d = copy.deepcopy(obj, memo) - if empty: + if empty and isinstance(d, MutableMapping): d.clear() return d diff --git a/benedict/core/dump.py b/benedict/core/dump.py index 735ffe70..c6117ef3 100644 --- a/benedict/core/dump.py +++ b/benedict/core/dump.py @@ -1,7 +1,9 @@ +from typing import Any + from benedict.serializers import JSONSerializer -def dump(obj, **kwargs): +def dump(obj: Any, **kwargs: Any) -> str: serializer = JSONSerializer() options = {"indent": 4, "sort_keys": True} options.update(**kwargs) diff --git a/benedict/core/filter.py b/benedict/core/filter.py index d823a576..1032b86e 100644 --- a/benedict/core/filter.py +++ b/benedict/core/filter.py @@ -1,13 +1,21 @@ -from benedict.core import clone +from collections.abc import Callable, MutableMapping +from typing import TypeVar +from benedict.core.clone import clone -def filter(d, predicate): +_K = TypeVar("_K") +_V = TypeVar("_V") + + +def filter( + d: MutableMapping[_K, _V], predicate: Callable[[_K, _V], bool] +) -> MutableMapping[_K, _V]: if not callable(predicate): raise ValueError("predicate argument must be a callable.") new_dict = clone(d, empty=True) keys = list(d.keys()) for key in keys: - value = d.get(key, None) + value = d[key] if predicate(key, value): new_dict[key] = value return new_dict diff --git a/benedict/core/find.py b/benedict/core/find.py index 5228256b..29449904 100644 --- a/benedict/core/find.py +++ b/benedict/core/find.py @@ -1,5 +1,16 @@ -def find(d, keys, default=None): +from __future__ import annotations + +from collections.abc import Iterable, Mapping +from typing import TypeVar + +_K = TypeVar("_K") +_V = TypeVar("_V") + + +def find( + d: Mapping[_K, _V], keys: Iterable[_K], default: _V | None = None +) -> _V | None: for key in keys: if key in d: - return d.get(key, default) + return d[key] return default diff --git a/benedict/core/flatten.py b/benedict/core/flatten.py index 3795aa05..d4b5cda8 100644 --- a/benedict/core/flatten.py +++ b/benedict/core/flatten.py @@ -1,20 +1,27 @@ -from benedict.core import clone -from benedict.utils import type_util +from collections.abc import Mapping +from typing import Any +from benedict.core.clone import clone -def _flatten_key(base_key, key, separator): + +def _flatten_key(base_key: str, key: str, separator: str) -> str: if base_key and separator: return f"{base_key}{separator}{key}" return key -def _flatten_item(d, base_dict, base_key, separator): +def _flatten_item( + d: Any, + base_dict: Any, + base_key: str, + separator: str, +) -> Any: new_dict = base_dict keys = list(d.keys()) for key in keys: new_key = _flatten_key(base_key, key, separator) value = d.get(key, None) - if type_util.is_dict(value): + if isinstance(value, Mapping): new_value = _flatten_item( value, base_dict=new_dict, base_key=new_key, separator=separator ) @@ -26,6 +33,6 @@ def _flatten_item(d, base_dict, base_key, separator): return new_dict -def flatten(d, separator="_"): +def flatten(d: Any, separator: str = "_") -> Any: new_dict = clone(d, empty=True) return _flatten_item(d, base_dict=new_dict, base_key="", separator=separator) diff --git a/benedict/core/groupby.py b/benedict/core/groupby.py index 5d378ba1..04439ff7 100644 --- a/benedict/core/groupby.py +++ b/benedict/core/groupby.py @@ -1,10 +1,18 @@ +from __future__ import annotations + +from collections.abc import Mapping, MutableSequence, Sequence +from typing import Any, TypeVar + from benedict.utils import type_util +_K = TypeVar("_K") +_V = TypeVar("_V", bound=MutableSequence[Any]) + -def groupby(items, key): +def groupby(items: Sequence[Mapping[_K, Any]], key: _K) -> dict[Any, Any]: if not type_util.is_list(items): raise ValueError("items should be a list of dicts.") - items_grouped = {} + items_grouped: dict[Any, Any] = {} for item in items: if not type_util.is_dict(item): raise ValueError("item should be a dict.") diff --git a/benedict/core/invert.py b/benedict/core/invert.py index 6d33f4e9..d5bc39d2 100644 --- a/benedict/core/invert.py +++ b/benedict/core/invert.py @@ -1,20 +1,28 @@ -from benedict.core import clone +from collections.abc import MutableMapping, Sequence +from typing import Any, TypeVar + +from benedict.core.clone import clone from benedict.utils import type_util +_K = TypeVar("_K") +_V = TypeVar("_V") + -def _invert_item(d, key, value, flat): +def _invert_item(d: MutableMapping[Any, Any], key: _K, value: _V, flat: bool) -> None: if flat: d.setdefault(value, key) else: d.setdefault(value, []).append(key) -def _invert_list(d, key, value, flat): +def _invert_list( + d: MutableMapping[Any, Any], key: _K, value: Sequence[Any], flat: bool +) -> None: for value_item in value: _invert_item(d, key, value_item, flat) -def invert(d, flat=False): +def invert(d: MutableMapping[_K, _V], flat: bool = False) -> MutableMapping[Any, Any]: new_dict = clone(d, empty=True) for key, value in d.items(): if type_util.is_list_or_tuple(value): diff --git a/benedict/core/items_sorted.py b/benedict/core/items_sorted.py index 4237ffe2..cc587aaf 100644 --- a/benedict/core/items_sorted.py +++ b/benedict/core/items_sorted.py @@ -1,10 +1,25 @@ -def _items_sorted_by_item_at_index(d, index, reverse): +from __future__ import annotations + +from collections.abc import Mapping + +from useful_types import SupportsRichComparisonT + + +def _items_sorted_by_item_at_index( + d: Mapping[SupportsRichComparisonT, SupportsRichComparisonT], + index: int, + reverse: bool, +) -> list[tuple[SupportsRichComparisonT, SupportsRichComparisonT]]: return sorted(d.items(), key=lambda item: item[index], reverse=reverse) -def items_sorted_by_keys(d, reverse=False): +def items_sorted_by_keys( + d: Mapping[SupportsRichComparisonT, SupportsRichComparisonT], reverse: bool = False +) -> list[tuple[SupportsRichComparisonT, SupportsRichComparisonT]]: return _items_sorted_by_item_at_index(d, 0, reverse) -def items_sorted_by_values(d, reverse=False): +def items_sorted_by_values( + d: Mapping[SupportsRichComparisonT, SupportsRichComparisonT], reverse: bool = False +) -> list[tuple[SupportsRichComparisonT, SupportsRichComparisonT]]: return _items_sorted_by_item_at_index(d, 1, reverse) diff --git a/benedict/core/keylists.py b/benedict/core/keylists.py index 9d516dd8..d45f8b7d 100644 --- a/benedict/core/keylists.py +++ b/benedict/core/keylists.py @@ -1,7 +1,14 @@ +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import Any + from benedict.utils import type_util -def _get_keylist_for_dict(d, parent_keys, indexes): +def _get_keylist_for_dict( + d: Mapping[Any, Any], parent_keys: list[Any], indexes: bool +) -> list[list[Any]]: keylist = [] for key, value in d.items(): keys = parent_keys + [key] @@ -10,7 +17,9 @@ def _get_keylist_for_dict(d, parent_keys, indexes): return keylist -def _get_keylist_for_list(ls, parent_keys, indexes): +def _get_keylist_for_list( + ls: Sequence[Any], parent_keys: list[Any], indexes: bool +) -> list[list[Any]]: keylist = [] for key, value in enumerate(ls): keys = list(parent_keys) @@ -20,7 +29,9 @@ def _get_keylist_for_list(ls, parent_keys, indexes): return keylist -def _get_keylist_for_value(value, parent_keys, indexes): +def _get_keylist_for_value( + value: Mapping[Any, Any] | Sequence[Any], parent_keys: list[Any], indexes: bool +) -> list[list[Any]]: if type_util.is_dict(value): return _get_keylist_for_dict(value, parent_keys, indexes) elif type_util.is_list(value) and indexes: @@ -28,5 +39,7 @@ def _get_keylist_for_value(value, parent_keys, indexes): return [] -def keylists(d, indexes=False): +def keylists( + d: Mapping[Any, Any] | Sequence[Any], indexes: bool = False +) -> list[list[Any]]: return _get_keylist_for_value(d, [], indexes) diff --git a/benedict/core/keypaths.py b/benedict/core/keypaths.py index 696689f9..ca4c9dea 100644 --- a/benedict/core/keypaths.py +++ b/benedict/core/keypaths.py @@ -1,8 +1,18 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any + from benedict.core.keylists import keylists from benedict.utils import type_util -def keypaths(d, separator=".", indexes=False, sort=True): +def keypaths( + d: Mapping[Any, Any], + separator: str | None = ".", + indexes: bool = False, + sort: bool = True, +) -> list[str]: separator = separator or "." if not type_util.is_string(separator): raise ValueError("separator argument must be a (non-empty) string.") diff --git a/benedict/core/match.py b/benedict/core/match.py index 53643c74..514fe914 100644 --- a/benedict/core/match.py +++ b/benedict/core/match.py @@ -1,10 +1,19 @@ +from __future__ import annotations + import re +from collections.abc import Mapping +from typing import Any from benedict.core.keypaths import keypaths from benedict.utils import type_util -def match(d, pattern, separator=".", indexes=True): +def match( + d: Mapping[Any, Any], + pattern: str | re.Pattern[str], + separator: str | None = ".", + indexes: bool = True, +) -> list[Any]: if type_util.is_regex(pattern): regex = pattern elif type_util.is_string(pattern): diff --git a/benedict/core/merge.py b/benedict/core/merge.py index 0156e675..2d6fe46a 100644 --- a/benedict/core/merge.py +++ b/benedict/core/merge.py @@ -1,25 +1,44 @@ +from collections.abc import Mapping, MutableMapping +from typing import Any, TypeVar + from benedict.utils import type_util +_K = TypeVar("_K") +_V = TypeVar("_V") + -def _merge_dict(d, other, overwrite=True, concat=False): +def _merge_dict( + d: MutableMapping[_K, _V], + other: Mapping[_K, _V], + overwrite: bool = True, + concat: bool = False, +) -> None: for key, value in other.items(): _merge_item(d, key, value, overwrite=overwrite, concat=concat) -def _merge_item(d, key, value, overwrite=True, concat=False): +def _merge_item( + d: MutableMapping[_K, _V], + key: _K, + value: _V, + overwrite: bool = True, + concat: bool = False, +) -> None: if key in d: - item = d.get(key, None) + item = d.get(key) if type_util.is_dict(item) and type_util.is_dict(value): _merge_dict(item, value, overwrite=overwrite, concat=concat) elif concat and type_util.is_list(item) and type_util.is_list(value): - item += value + item += value # type: ignore[assignment] elif overwrite: d[key] = value else: d[key] = value -def merge(d, other, *args, **kwargs): +def merge( + d: MutableMapping[_K, _V], other: Mapping[_K, _V], *args: Any, **kwargs: Any +) -> Any: overwrite = kwargs.get("overwrite", True) concat = kwargs.get("concat", False) others = [other] + list(args) diff --git a/benedict/core/move.py b/benedict/core/move.py index 1bf4c56c..7fcb3cce 100644 --- a/benedict/core/move.py +++ b/benedict/core/move.py @@ -1,4 +1,12 @@ -def move(d, key_src, key_dest, overwrite=True): +from collections.abc import MutableMapping +from typing import Any, TypeVar + +_K = TypeVar("_K") + + +def move( + d: MutableMapping[_K, Any], key_src: _K, key_dest: _K, overwrite: bool = True +) -> None: if key_dest == key_src: return if key_dest in d and not overwrite: diff --git a/benedict/core/nest.py b/benedict/core/nest.py index 1f13930c..c0196feb 100644 --- a/benedict/core/nest.py +++ b/benedict/core/nest.py @@ -1,14 +1,29 @@ +from __future__ import annotations + +from collections.abc import Mapping, MutableMapping, Sequence +from typing import Any + from benedict.core.groupby import groupby -def _nest_items(nested_items, item, id_key, children_key): +def _nest_items( + nested_items: MutableMapping[Any, list[Any]], + item: Any, + id_key: Any, + children_key: Any, +) -> None: children_items = nested_items.pop(item[id_key], []) item[children_key] = children_items for child_item in children_items: _nest_items(nested_items, child_item, id_key, children_key) -def nest(items, id_key, parent_id_key, children_key): +def nest( + items: Sequence[Mapping[Any, Any]], + id_key: Any, + parent_id_key: Any, + children_key: Any, +) -> list[Any] | None: if any( [id_key == parent_id_key, id_key == children_key, parent_id_key == children_key] ): diff --git a/benedict/core/remove.py b/benedict/core/remove.py index d68838f2..28b0a1d0 100644 --- a/benedict/core/remove.py +++ b/benedict/core/remove.py @@ -1,7 +1,10 @@ +from collections.abc import MutableMapping +from typing import Any + from benedict.utils import type_util -def remove(d, keys, *args): +def remove(d: MutableMapping[Any, Any], keys: Any, *args: Any) -> None: if type_util.is_string(keys): keys = [keys] keys += args diff --git a/benedict/core/rename.py b/benedict/core/rename.py index a616fc67..1c1361eb 100644 --- a/benedict/core/rename.py +++ b/benedict/core/rename.py @@ -1,5 +1,10 @@ +from collections.abc import MutableMapping +from typing import Any, TypeVar + from benedict.core.move import move +_K = TypeVar("_K") + -def rename(d, key, key_new): +def rename(d: MutableMapping[_K, Any], key: _K, key_new: _K) -> None: move(d, key, key_new, overwrite=False) diff --git a/benedict/core/search.py b/benedict/core/search.py index 34637246..51bcab78 100644 --- a/benedict/core/search.py +++ b/benedict/core/search.py @@ -1,26 +1,45 @@ +from __future__ import annotations + +from collections.abc import Mapping, MutableMapping, Sequence +from typing import Any, TypeVar + from benedict.core.traverse import traverse from benedict.utils import type_util +_K = TypeVar("_K") +_V = TypeVar("_V") + -def _get_term(value, case_sensitive): +def _get_term(value: Any, case_sensitive: bool) -> tuple[Any, bool]: v_is_str = type_util.is_string(value) v = value.lower() if (v_is_str and not case_sensitive) else value return (v, v_is_str) -def _get_match(query, value, exact, case_sensitive): +def _get_match(query: Any, value: Any, exact: bool, case_sensitive: bool) -> bool: q, q_is_str = _get_term(query, case_sensitive) v, v_is_str = _get_term(value, case_sensitive) # TODO: add regex support if q_is_str and v_is_str and not exact: return q in v - return q == v + return bool(q == v) -def search(d, query, in_keys=True, in_values=True, exact=False, case_sensitive=True): +def search( + d: MutableMapping[_K, _V], + query: Any, + in_keys: bool = True, + in_values: bool = True, + exact: bool = False, + case_sensitive: bool = True, +) -> list[tuple[Any, Any, Any]]: items = [] - def _search_item(item_dict, item_key, item_value): + def _search_item( + item_dict: Mapping[_K, _V] | Sequence[Any] | None, + item_key: _K | int, + item_value: _V | None, + ) -> None: match_key = in_keys and _get_match(query, item_key, exact, case_sensitive) match_val = in_values and _get_match(query, item_value, exact, case_sensitive) if any([match_key, match_val]): diff --git a/benedict/core/standardize.py b/benedict/core/standardize.py index d255ad66..6847fe5a 100644 --- a/benedict/core/standardize.py +++ b/benedict/core/standardize.py @@ -1,4 +1,6 @@ import re +from collections.abc import MutableMapping +from typing import Any from slugify import slugify @@ -7,7 +9,7 @@ from benedict.utils import type_util -def _standardize_item(d, key, value): +def _standardize_item(d: MutableMapping[Any, Any], key: Any, value: Any) -> None: if type_util.is_string(key): # https://stackoverflow.com/a/12867228/2096218 norm_key = re.sub(r"((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))", r"_\1", key) @@ -15,5 +17,5 @@ def _standardize_item(d, key, value): rename(d, key, norm_key) -def standardize(d): - traverse(d, _standardize_item) +def standardize(d: MutableMapping[Any, Any]) -> None: + traverse(d, _standardize_item) # type: ignore[arg-type] diff --git a/benedict/core/subset.py b/benedict/core/subset.py index 6be3c8a2..3035d3a6 100644 --- a/benedict/core/subset.py +++ b/benedict/core/subset.py @@ -1,12 +1,24 @@ -from benedict.core import clone +from __future__ import annotations + +from collections.abc import MutableMapping +from typing import Any, TypeVar + +from benedict.core.clone import clone from benedict.utils import type_util +_K = TypeVar("_K") +_V = TypeVar("_V") + -def subset(d, keys, *args): - new_dict = clone(d, empty=True) +def subset( + d: MutableMapping[_K, _V], keys: _K | list[_K], *args: Any +) -> MutableMapping[_K, _V | None]: + new_dict: MutableMapping[_K, _V | None] = clone(d, empty=True) # type: ignore[arg-type] if type_util.is_string(keys): - keys = [keys] - keys += args - for key in keys: - new_dict[key] = d.get(key, None) + key_list: list[_K] = [keys] + else: + key_list = list(keys) if isinstance(keys, list) else [keys] + key_list.extend(args) + for key in key_list: + new_dict[key] = d.get(key) return new_dict diff --git a/benedict/core/swap.py b/benedict/core/swap.py index 5c6e5d79..fe728417 100644 --- a/benedict/core/swap.py +++ b/benedict/core/swap.py @@ -1,4 +1,10 @@ -def swap(d, key1, key2): +from collections.abc import MutableMapping +from typing import Any, TypeVar + +_K = TypeVar("_K") + + +def swap(d: MutableMapping[_K, Any], key1: _K, key2: _K) -> None: if key1 == key2: return val1 = d[key1] diff --git a/benedict/core/traverse.py b/benedict/core/traverse.py index 8019b235..1340283d 100644 --- a/benedict/core/traverse.py +++ b/benedict/core/traverse.py @@ -1,29 +1,46 @@ +from __future__ import annotations + +from collections.abc import Callable, MutableMapping, Sequence +from typing import Any, TypeVar + from benedict.utils import type_util +_K = TypeVar("_K") +_V = TypeVar("_V") + +TraverseCallback = Callable[ + [MutableMapping[_K, _V] | Sequence[Any] | None, _K | int, _V | None], None +] + -def _traverse_collection(d, callback): +def _traverse_collection( + d: MutableMapping[_K, _V] | Sequence[Any] | _V | None, + callback: TraverseCallback[_K, _V], +) -> None: if type_util.is_dict(d): _traverse_dict(d, callback) elif type_util.is_list_or_tuple(d): _traverse_list(d, callback) -def _traverse_dict(d, callback): +def _traverse_dict( + d: MutableMapping[_K, _V], callback: TraverseCallback[_K, _V] +) -> None: keys = list(d.keys()) for key in keys: - value = d.get(key, None) + value = d.get(key) callback(d, key, value) _traverse_collection(value, callback) -def _traverse_list(ls, callback): +def _traverse_list(ls: Sequence[Any], callback: TraverseCallback[_K, _V]) -> None: items = list(enumerate(ls)) for index, value in items: callback(ls, index, value) _traverse_collection(value, callback) -def traverse(d, callback): +def traverse(d: MutableMapping[_K, _V], callback: TraverseCallback[_K, _V]) -> None: if not callable(callback): raise ValueError("callback argument must be a callable.") _traverse_collection(d, callback) diff --git a/benedict/core/unflatten.py b/benedict/core/unflatten.py index 23002e12..68b625f6 100644 --- a/benedict/core/unflatten.py +++ b/benedict/core/unflatten.py @@ -1,16 +1,21 @@ -from benedict.core import clone +from __future__ import annotations + +from collections.abc import MutableMapping +from typing import Any + +from benedict.core.clone import clone from benedict.dicts.keylist import keylist_util from benedict.utils import type_util -def _unflatten_item(key, value, separator): +def _unflatten_item(key: str, value: Any, separator: str) -> tuple[list[Any], Any]: keys = key.split(separator) if type_util.is_dict(value): return (keys, unflatten(value, separator=separator)) return (keys, value) -def unflatten(d, separator="_"): +def unflatten(d: MutableMapping[Any, Any], separator: str = "_") -> Any: new_dict = clone(d, empty=True) keys = list(d.keys()) for key in keys: diff --git a/benedict/core/unique.py b/benedict/core/unique.py index bd22e6bc..a9e69ffb 100644 --- a/benedict/core/unique.py +++ b/benedict/core/unique.py @@ -1,4 +1,8 @@ -def unique(d): +from collections.abc import MutableMapping +from typing import Any + + +def unique(d: MutableMapping[Any, Any]) -> None: values = [] keys = list(d.keys()) for key in keys: diff --git a/benedict/dicts/__init__.py b/benedict/dicts/__init__.py index 55c784f8..c8f604b3 100644 --- a/benedict/dicts/__init__.py +++ b/benedict/dicts/__init__.py @@ -1,3 +1,20 @@ +from __future__ import annotations + +import re +from collections.abc import ( + Callable, + Iterable, + Iterator, + Mapping, + Sequence, +) +from typing import ( + Any, + cast, +) + +from typing_extensions import Self, TypeVar + from benedict.core import clean as _clean from benedict.core import clone as _clone from benedict.core import dump as _dump @@ -22,6 +39,7 @@ from benedict.core import traverse as _traverse from benedict.core import unflatten as _unflatten from benedict.core import unique as _unique +from benedict.core.traverse import TraverseCallback from benedict.dicts.io import IODict from benedict.dicts.keyattr import KeyattrDict from benedict.dicts.keylist import KeylistDict @@ -38,9 +56,17 @@ "ParseDict", ] +_KPT = keypath_util.KeyType +_K = TypeVar("_K", default=str) +_V = TypeVar("_V", default=Any) + -class benedict(KeyattrDict, KeypathDict, IODict, ParseDict): - def __init__(self, *args, **kwargs): +class benedict(KeyattrDict[_K, _V], KeypathDict[_V], IODict[_K, _V], ParseDict[_K, _V]): + def __init__( + self, + *args: Any, + **kwargs: Any, + ) -> None: """ Constructs a new instance. """ @@ -53,7 +79,7 @@ def __init__(self, *args, **kwargs): return super().__init__(*args, **kwargs) - def __deepcopy__(self, memo): + def __deepcopy__(self, memo: dict[int, Any]) -> Self: obj_type = type(self) obj = obj_type( keyattr_enabled=self._keyattr_enabled, @@ -64,19 +90,19 @@ def __deepcopy__(self, memo): obj[key] = _clone(value, memo=memo) return obj - def __getitem__(self, key): + def __getitem__(self, key: _KPT) -> Any: # type: ignore[override] return self._cast(super().__getitem__(key)) - def __setitem__(self, key, value): - return super().__setitem__(key, self._cast(value)) + def __setitem__(self, key: _KPT, value: _V) -> None: # type: ignore[override] + super().__setitem__(key, self._cast(value)) - def _cast(self, value): + def _cast(self, value: Any) -> Any: """ Cast a dict instance to a benedict instance keeping the pointer to the original dict. """ obj_type = type(self) - if isinstance(value, dict) and not isinstance(value, obj_type): + if isinstance(value, Mapping) and not isinstance(value, obj_type): return obj_type( value, keyattr_enabled=self._keyattr_enabled, @@ -89,7 +115,7 @@ def _cast(self, value): value[index] = self._cast(item) return value - def clean(self, strings=True, collections=True): + def clean(self, strings: bool = True, collections: bool = True) -> None: """ Clean the current dict instance removing all empty values: None, '', {}, [], (). If strings or collections (dict, list, set, tuple) flags are False, @@ -97,52 +123,52 @@ def clean(self, strings=True, collections=True): """ _clean(self, strings=strings, collections=collections) - def clone(self): + def clone(self) -> Self: """ Creates and return a clone of the current dict instance (deep copy). """ - return self._cast(_clone(self)) + return cast("Self", self._cast(_clone(self))) - def copy(self): + def copy(self) -> Self: """ Creates and return a copy of the current instance (shallow copy). """ - return self._cast(super().copy()) + return cast("Self", self._cast(super().copy())) - def deepcopy(self): + def deepcopy(self) -> Self: """ Alias of 'clone' method. """ return self.clone() - def deepupdate(self, other, *args): + def deepupdate(self, other: Mapping[str, _V], *args: Any) -> None: """ Alias of 'merge' method. """ self.merge(other, *args) - def dump(self, data=None): + def dump(self, data: Any = None) -> str: """ Return a readable string representation of any dict/list. This method can be used both as static method or instance method. """ return _dump(data or self) - def filter(self, predicate): + def filter(self, predicate: Callable[[_KPT, _V], bool]) -> Self: """ Return a new filtered dict using the given predicate function. Predicate function receives key, value arguments and should return a bool value. """ - return _filter(self, predicate) + return cast("Self", _filter(self, predicate)) - def find(self, keys, default=None): + def find(self, keys: Iterable[str], default: _V | None = None) -> _V | None: """ Return the first match searching for the given keys. If no result found, default value is returned. """ - return _find(self, keys, default) + return _find(self, keys, default) # type: ignore[misc] - def flatten(self, separator="_"): + def flatten(self, separator: str = "_") -> Self: """ Return a new flattened dict using the given separator to join nested dict keys to flatten keypaths. @@ -152,50 +178,52 @@ def flatten(self, separator="_"): f"Invalid flatten separator: {separator!r}, " "flatten separator must be different from keypath separator." ) - return _flatten(self, separator) + return cast("Self", _flatten(self, separator)) - def get(self, key, default=None): + def get(self, key: _KPT, default: _V | None = None) -> Any: # type: ignore[override] return self._cast(super().get(key, default)) - def get_dict(self, key, default=None): + def get_dict(self, key: _K, default: Any = None) -> Any: return self._cast(super().get_dict(key, default)) - def get_list_item(self, key, index=0, default=None, separator=","): + def get_list_item( + self, key: _K, index: int = 0, default: Any = None, separator: str = "," + ) -> Any: return self._cast(super().get_list_item(key, index, default, separator)) - def groupby(self, key, by_key): + def groupby(self, key: _KPT, by_key: _KPT) -> Self: """ Group a list of dicts at key by the value of the given by_key and return a new dict. """ - return self._cast(_groupby(self[key], by_key)) + return cast("Self", self._cast(_groupby(self[key], by_key))) - def invert(self, flat=False): + def invert(self, flat: bool = False) -> Self: """ Return a new inverted dict, where values become keys and keys become values. Since multiple keys could have the same value, each value will be a list of keys. If flat is True each value will be a single value (use this only if values are unique). """ - return _invert(self, flat) + return cast("Self", _invert(self, flat)) - def items(self): + def items(self) -> Iterator[tuple[_KPT, Any]]: # type: ignore[override] for key, value in super().items(): - yield (key, self._cast(value)) + yield (key, self._cast(value)) # type: ignore[misc] - def items_sorted_by_keys(self, reverse=False): + def items_sorted_by_keys(self, reverse: bool = False) -> list[tuple[Any, Any]]: """ Return items (key/value list) sorted by keys. If reverse is True, the list will be reversed. """ return _items_sorted_by_keys(self, reverse=reverse) - def items_sorted_by_values(self, reverse=False): + def items_sorted_by_values(self, reverse: bool = False) -> list[tuple[Any, Any]]: """ Return items (key/value list) sorted by values. If reverse is True, the list will be reversed. """ return _items_sorted_by_values(self, reverse=reverse) - def keypaths(self, indexes=False, sort=True): + def keypaths(self, indexes: bool = False, sort: bool = True) -> list[Any]: """ Return a list of all keypaths in the dict. If indexes is True, the output will include list values indexes. @@ -204,7 +232,7 @@ def keypaths(self, indexes=False, sort=True): self, separator=self._keypath_separator, indexes=indexes, sort=sort ) - def match(self, pattern, indexes=True): + def match(self, pattern: str | re.Pattern[str], indexes: bool = True) -> list[Any]: """ Return a list of all values whose keypath matches the given pattern (a regex or string). @@ -214,7 +242,7 @@ def match(self, pattern, indexes=True): """ return _match(self, pattern, separator=self._keypath_separator, indexes=indexes) - def merge(self, other, *args, **kwargs): + def merge(self, other: Mapping[str, _V], *args: Any, **kwargs: Any) -> None: """ Merge one or more dict objects into current instance (deepupdate). Sub-dictionaries will be merged together. @@ -226,90 +254,99 @@ def merge(self, other, *args, **kwargs): keypath_util.check_keys(other, self._keypath_separator) _merge(self, *others, **kwargs) - def move(self, key_src, key_dest): + def move(self, key_src: _KPT, key_dest: _KPT) -> None: """ Move a dict instance value item from 'key_src' to 'key_dst'. If key_dst exists, its value will be overwritten. """ - _move(self, key_src, key_dest) + _move(self, key_src, key_dest) # type: ignore[misc] def nest( - self, key, id_key="id", parent_id_key="parent_id", children_key="children" - ): + self, + key: _KPT, + id_key: _KPT = "id", + parent_id_key: _KPT = "parent_id", + children_key: _KPT = "children", + ) -> list[Any] | None: """ Nest a list of dicts at the given key and return a new nested list using the specified keys to establish the correct items hierarchy. """ return _nest(self[key], id_key, parent_id_key, children_key) - def pop(self, key, *args): - return self._cast(super().pop(key, *args)) + def pop(self, key: _KPT, *args: Any) -> _V: # type: ignore[override] + return cast("_V", self._cast(super().pop(key, *args))) - def remove(self, keys, *args): + def remove(self, keys: Iterable[_KPT], *args: Any) -> None: """ Remove multiple keys from the current dict instance. It is possible to pass a single key or more keys (as list or *args). """ _remove(self, keys, *args) - def setdefault(self, key, default=None): - return self._cast(super().setdefault(key, default)) + def setdefault(self, key: _KPT, default: _V | None = None) -> _V: # type: ignore[override] + return cast("_V", self._cast(super().setdefault(key, default))) - def rename(self, key, key_new): + def rename(self, key: _KPT, key_new: _KPT) -> None: """ Rename a dict item key from 'key' to 'key_new'. If key_new exists, a KeyError will be raised. """ - _rename(self, key, key_new) + _rename(self, key, key_new) # type: ignore[misc] def search( - self, query, in_keys=True, in_values=True, exact=False, case_sensitive=False - ): + self, + query: Any, + in_keys: bool = True, + in_values: bool = True, + exact: bool = False, + case_sensitive: bool = False, + ) -> list[tuple[dict[str, Any], str, Any]]: """ Search and return a list of items (dict, key, value, ) matching the given query. """ return _search(self, query, in_keys, in_values, exact, case_sensitive) - def standardize(self): + def standardize(self) -> None: """ Standardize all dict keys (e.g. 'Location Latitude' -> 'location_latitude'). """ _standardize(self) - def subset(self, keys, *args): + def subset(self, keys: Sequence[_KPT], *args: Any) -> Self: """ Return a new dict subset for the given keys. It is possible to pass a single key or multiple keys (as list or *args). """ - return _subset(self, keys, *args) + return cast("Self", _subset(self, keys, *args)) - def swap(self, key1, key2): + def swap(self, key1: _KPT, key2: _KPT) -> None: """ Swap items values at the given keys. """ - _swap(self, key1, key2) + _swap(self, key1, key2) # type: ignore[misc] - def traverse(self, callback): + def traverse(self, callback: TraverseCallback[_K, _V]) -> None: """ Traverse the current dict instance (including nested dicts), and pass each item (dict, key, value) to the callback function. """ _traverse(self, callback) - def unflatten(self, separator="_"): + def unflatten(self, separator: str = "_") -> Self: """ Return a new unflattened dict using the given separator to split dict keys to nested keypaths. """ - return _unflatten(self, separator) + return cast("Self", _unflatten(self, separator)) - def unique(self): + def unique(self) -> None: """ Remove duplicated values from the current dict instance. """ _unique(self) - def values(self): + def values(self) -> Iterator[Any]: # type: ignore[override] for value in super().values(): yield self._cast(value) diff --git a/benedict/dicts/base/base_dict.py b/benedict/dicts/base/base_dict.py index 031c3462..91940041 100644 --- a/benedict/dicts/base/base_dict.py +++ b/benedict/dicts/base/base_dict.py @@ -1,14 +1,30 @@ -from benedict.core import clone as _clone +from __future__ import annotations +from collections.abc import ( + ItemsView, + Iterator, + KeysView, + Mapping, + MutableMapping, + ValuesView, +) +from typing import Any, cast -class BaseDict(dict): - _dict = None - _pointer = False +from typing_extensions import Self, TypeVar + +from benedict.core.clone import clone as _clone + +_K = TypeVar("_K", default=str) +_V = TypeVar("_V", default=Any) + + +class BaseDict(dict[_K, _V]): + _dict: dict[_K, _V] | None = None @classmethod - def _get_dict_or_value(cls, value): + def _get_dict_or_value(cls, value: Any) -> Any: value = value.dict() if isinstance(value, cls) else value - if isinstance(value, dict): + if isinstance(value, MutableMapping): for key in value.keys(): key_val = value[key] if isinstance(key_val, cls): @@ -16,148 +32,147 @@ def _get_dict_or_value(cls, value): value[key] = key_val return value - def __init__(self, *args, **kwargs): - if len(args) == 1 and isinstance(args[0], dict): + def __init__(self, *args: Any, **kwargs: Any) -> None: + self._dict = None + if len(args) == 1 and isinstance(args[0], Mapping): self._dict = self._get_dict_or_value(args[0]) - self._pointer = True super().__init__(self._dict) return - self._dict = None - self._pointer = False super().__init__(*args, **kwargs) - def __bool__(self): - if self._pointer: + def __bool__(self) -> bool: + if self._dict is not None: return bool(self._dict) return len(self.keys()) > 0 - def __contains__(self, key): - if self._pointer: - return key in self._dict - return super().__contains__(key) + def __contains__(self, o: object) -> bool: + if self._dict is not None: + return o in self._dict + return super().__contains__(o) - def __deepcopy__(self, memo): + def __deepcopy__(self, memo: dict[int, Any]) -> Self: obj = self.__class__() for key, value in self.items(): obj[key] = _clone(value, memo=memo) return obj - def __delitem__(self, key): - if self._pointer: + def __delitem__(self, key: _K) -> None: + if self._dict is not None: del self._dict[key] return super().__delitem__(key) - def __eq__(self, other): - if self._pointer: + def __eq__(self, other: object) -> bool: + if self._dict is not None: return self._dict == other return super().__eq__(other) - def __getitem__(self, key): - if self._pointer: + def __getitem__(self, key: _K) -> _V: + if self._dict is not None: return self._dict[key] return super().__getitem__(key) - def __ior__(self, other): - if self._pointer: - return self._dict.__ior__(other) + def __ior__(self, other: Any) -> Self: # type: ignore[misc,override] + if self._dict is not None: + return cast("Self", self._dict.__ior__(other)) return super().__ior__(other) - def __iter__(self): - if self._pointer: + def __iter__(self) -> Iterator[_K]: + if self._dict is not None: return iter(self._dict) return super().__iter__() - def __len__(self): - if self._pointer: + def __len__(self) -> int: + if self._dict is not None: return len(self._dict) return super().__len__() - def __or__(self, other): - if self._pointer: - return self._dict.__or__(other) - return super().__or__(other) + def __or__(self, other: dict[_K, _V]) -> Self: # type: ignore[override] + if self._dict is not None: + return cast("Self", self._dict.__or__(other)) + return cast("Self", super().__or__(other)) - def __repr__(self): - if self._pointer: + def __repr__(self) -> str: + if self._dict is not None: return repr(self._dict) return super().__repr__() - def __setitem__(self, key, value): + def __setitem__(self, key: _K, value: _V) -> None: value = self._get_dict_or_value(value) - if self._pointer: + if self._dict is not None: is_dict_item = key in self._dict and isinstance(self._dict[key], dict) is_dict_value = isinstance(value, dict) if is_dict_item and is_dict_value: if self._dict[key] is value: # prevent clearing dict instance when assigning value to itself. fix #294 return - self._dict[key].clear() - self._dict[key].update(value) + self._dict[key].clear() # type: ignore[attr-defined] + self._dict[key].update(value) # type: ignore[attr-defined] return self._dict[key] = value return super().__setitem__(key, value) - def __setstate__(self, state): + def __setstate__(self, state: Mapping[str, Any]) -> None: + self._dict = state["_dict"] self._dict = state["_dict"] - self._pointer = state["_pointer"] - def __str__(self): - if self._pointer: + def __str__(self) -> str: + if self._dict is not None: return str(self._dict) return super().__str__() - def clear(self): - if self._pointer: + def clear(self) -> None: + if self._dict is not None: self._dict.clear() return super().clear() - def copy(self): - if self._pointer: - return self._dict.copy() - return super().copy() + def copy(self) -> Self: + if self._dict is not None: + return cast("Self", self._dict.copy()) + return cast("Self", super().copy()) - def dict(self): - if self._pointer: - return self._dict + def dict(self) -> Self: + if self._dict is not None: + return cast("Self", self._dict) return self - def get(self, key, default=None): - if self._pointer: + def get(self, key: _K, default: _V | None = None) -> _V | None: # type: ignore[override] + if self._dict is not None: return self._dict.get(key, default) return super().get(key, default) - def items(self): - if self._pointer: + def items(self) -> ItemsView[_K, _V]: # type: ignore[override] + if self._dict is not None: return self._dict.items() return super().items() - def keys(self): - if self._pointer: + def keys(self) -> KeysView[_K]: # type: ignore[override] + if self._dict is not None: return self._dict.keys() return super().keys() - def pop(self, key, *args): - if self._pointer: - return self._dict.pop(key, *args) - return super().pop(key, *args) + def pop(self, key: _K, *args: Any) -> _V: + if self._dict is not None: + return self._dict.pop(key, *args) # type: ignore[no-any-return] + return super().pop(key, *args) # type: ignore[no-any-return] - def setdefault(self, key, default=None): + def setdefault(self, key: _K, default: _V | None = None) -> _V: default = self._get_dict_or_value(default) - if self._pointer: + assert default is not None + if self._dict is not None: return self._dict.setdefault(key, default) return super().setdefault(key, default) - def update(self, other): + def update(self, other: Any) -> None: other = self._get_dict_or_value(other) - if self._pointer: + if self._dict is not None: self._dict.update(other) return super().update(other) - def values(self): - if self._pointer: + def values(self) -> ValuesView[_V]: # type: ignore[override] + if self._dict is not None: return self._dict.values() return super().values() diff --git a/benedict/dicts/io/io_dict.py b/benedict/dicts/io/io_dict.py index 584f37ec..c70a5d53 100644 --- a/benedict/dicts/io/io_dict.py +++ b/benedict/dicts/io/io_dict.py @@ -1,13 +1,22 @@ +from __future__ import annotations + import traceback +from pathlib import Path +from typing import Any, NoReturn, cast + +from typing_extensions import Self, TypeVar from benedict.dicts.base import BaseDict from benedict.dicts.io import io_util from benedict.exceptions import ExtrasRequireModuleNotFoundError from benedict.utils import type_util +_K = TypeVar("_K", default=str) +_V = TypeVar("_V", default=Any) -class IODict(BaseDict): - def __init__(self, *args, **kwargs): + +class IODict(BaseDict[_K, _V]): + def __init__(self, *args: Any, **kwargs: Any) -> None: """ Constructs a new instance. """ @@ -23,7 +32,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @staticmethod - def _decode_init(s, **kwargs): + def _decode_init(s: str | Path, **kwargs: Any) -> dict[str, Any]: autodetected_format = io_util.autodetect_format(s) default_format = autodetected_format or "json" format = kwargs.pop("format", default_format).lower() @@ -31,7 +40,7 @@ def _decode_init(s, **kwargs): return IODict._decode(s, format, **kwargs) @staticmethod - def _decode(s, format, **kwargs): + def _decode(s: str | Path, format: str, **kwargs: Any) -> dict[str, Any]: data = None try: data = io_util.decode(s, format, **kwargs) @@ -53,12 +62,14 @@ def _decode(s, format, **kwargs): raise ValueError(f"Invalid data type: {type(data)}, expected dict or list.") @staticmethod - def _encode(d, format, **kwargs): + def _encode(d: Any, format: str, **kwargs: Any) -> Any: s = io_util.encode(d, format, **kwargs) return s @classmethod - def from_base64(cls, s, subformat="json", encoding="utf-8", **kwargs): + def from_base64( + cls, s: str, subformat: str = "json", encoding: str = "utf-8", **kwargs: Any + ) -> Self: """ Load and decode Base64 data from url, filepath or data-string. Data is decoded according to subformat and encoding. @@ -70,7 +81,7 @@ def from_base64(cls, s, subformat="json", encoding="utf-8", **kwargs): return cls(s, format="base64", **kwargs) @classmethod - def from_cli(cls, s, **kwargs): + def from_cli(cls, s: str, **kwargs: Any) -> Self: """ Load and decode data from a string of CLI arguments. ArgumentParser specific options can be passed using kwargs: @@ -80,7 +91,9 @@ def from_cli(cls, s, **kwargs): return cls(s, format="cli", **kwargs) @classmethod - def from_csv(cls, s, columns=None, columns_row=True, **kwargs): + def from_csv( + cls, s: str, columns: Any = None, columns_row: bool = True, **kwargs: Any + ) -> Self: """ Load and decode CSV data from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -92,7 +105,7 @@ def from_csv(cls, s, columns=None, columns_row=True, **kwargs): return cls(s, format="csv", **kwargs) @classmethod - def from_html(cls, s, **kwargs): + def from_html(cls, s: str, **kwargs: Any) -> Self: """ Load and decode html data from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -102,7 +115,7 @@ def from_html(cls, s, **kwargs): return cls(s, format="html", **kwargs) @classmethod - def from_ini(cls, s, **kwargs): + def from_ini(cls, s: str, **kwargs: Any) -> Self: """ Load and decode INI data from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -112,7 +125,7 @@ def from_ini(cls, s, **kwargs): return cls(s, format="ini", **kwargs) @classmethod - def from_json(cls, s, **kwargs): + def from_json(cls, s: str, **kwargs: Any) -> Self: """ Load and decode JSON data from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -122,7 +135,7 @@ def from_json(cls, s, **kwargs): return cls(s, format="json", **kwargs) @classmethod - def from_pickle(cls, s, **kwargs): + def from_pickle(cls, s: str, **kwargs: Any) -> Self: """ Load and decode a pickle encoded in Base64 format from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -132,7 +145,7 @@ def from_pickle(cls, s, **kwargs): return cls(s, format="pickle", **kwargs) @classmethod - def from_plist(cls, s, **kwargs): + def from_plist(cls, s: str, **kwargs: Any) -> Self: """ Load and decode p-list data from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -142,7 +155,7 @@ def from_plist(cls, s, **kwargs): return cls(s, format="plist", **kwargs) @classmethod - def from_query_string(cls, s, **kwargs): + def from_query_string(cls, s: str, **kwargs: Any) -> Self: """ Load and decode query-string from url, filepath or data-string. Return a new dict instance. A ValueError is raised in case of failure. @@ -150,7 +163,7 @@ def from_query_string(cls, s, **kwargs): return cls(s, format="query_string", **kwargs) @classmethod - def from_toml(cls, s, **kwargs): + def from_toml(cls, s: str, **kwargs: Any) -> Self: """ Load and decode TOML data from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -160,7 +173,14 @@ def from_toml(cls, s, **kwargs): return cls(s, format="toml", **kwargs) @classmethod - def from_xls(cls, s, sheet=0, columns=None, columns_row=True, **kwargs): + def from_xls( + cls, + s: Path | str, + sheet: int = 0, + columns: Any = None, + columns_row: bool = True, + **kwargs: Any, + ) -> Self: """ Load and decode XLS files (".xls", ".xlsx", ".xlsm") from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -174,7 +194,7 @@ def from_xls(cls, s, sheet=0, columns=None, columns_row=True, **kwargs): return cls(s, format="xls", **kwargs) @classmethod - def from_xml(cls, s, **kwargs): + def from_xml(cls, s: str, **kwargs: Any) -> Self: """ Load and decode XML data from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -184,7 +204,7 @@ def from_xml(cls, s, **kwargs): return cls(s, format="xml", **kwargs) @classmethod - def from_yaml(cls, s, **kwargs): + def from_yaml(cls, s: str, **kwargs: Any) -> Self: """ Load and decode YAML data from url, filepath or data-string. Decoder specific options can be passed using kwargs: @@ -193,7 +213,9 @@ def from_yaml(cls, s, **kwargs): """ return cls(s, format="yaml", **kwargs) - def to_base64(self, subformat="json", encoding="utf-8", **kwargs): + def to_base64( + self, subformat: str = "json", encoding: str = "utf-8", **kwargs: Any + ) -> str: """ Encode the current dict instance in Base64 format using the given subformat and encoding. @@ -203,12 +225,18 @@ def to_base64(self, subformat="json", encoding="utf-8", **kwargs): """ kwargs["subformat"] = subformat kwargs["encoding"] = encoding - return self._encode(self.dict(), "base64", **kwargs) + return cast("str", self._encode(self.dict(), "base64", **kwargs)) - def to_cli(self, **kwargs): + def to_cli(self, **kwargs: Any) -> NoReturn: raise NotImplementedError - def to_csv(self, key="values", columns=None, columns_row=True, **kwargs): + def to_csv( + self, + key: _K | str = "values", + columns: Any = None, + columns_row: bool = True, + **kwargs: Any, + ) -> str: """ Encode a list of dicts in the current dict instance in CSV format. Encoder specific options can be passed using kwargs: @@ -218,12 +246,12 @@ def to_csv(self, key="values", columns=None, columns_row=True, **kwargs): """ kwargs["columns"] = columns kwargs["columns_row"] = columns_row - return self._encode(self.dict()[key], "csv", **kwargs) + return cast("str", self._encode(self.dict()[key], "csv", **kwargs)) # type: ignore[index] - def to_html(self, **kwargs): + def to_html(self, **kwargs: Any) -> NoReturn: raise NotImplementedError - def to_ini(self, **kwargs): + def to_ini(self, **kwargs: Any) -> str: """ Encode the current dict instance in INI format. Encoder specific options can be passed using kwargs: @@ -231,9 +259,9 @@ def to_ini(self, **kwargs): Return the encoded string and optionally save it at 'filepath'. A ValueError is raised in case of failure. """ - return self._encode(self.dict(), "ini", **kwargs) + return cast("str", self._encode(self.dict(), "ini", **kwargs)) - def to_json(self, **kwargs): + def to_json(self, **kwargs: Any) -> str: """ Encode the current dict instance in JSON format. Encoder specific options can be passed using kwargs: @@ -241,9 +269,9 @@ def to_json(self, **kwargs): Return the encoded string and optionally save it at 'filepath'. A ValueError is raised in case of failure. """ - return self._encode(self.dict(), "json", **kwargs) + return cast("str", self._encode(self.dict(), "json", **kwargs)) - def to_pickle(self, **kwargs): + def to_pickle(self, **kwargs: Any) -> str: """ Encode the current dict instance as pickle (encoded in Base64). The pickle protocol used by default is 2. @@ -252,9 +280,9 @@ def to_pickle(self, **kwargs): Return the encoded string and optionally save it at 'filepath'. A ValueError is raised in case of failure. """ - return self._encode(self.dict(), "pickle", **kwargs) + return cast("str", self._encode(self.dict(), "pickle", **kwargs)) - def to_plist(self, **kwargs): + def to_plist(self, **kwargs: Any) -> str: """ Encode the current dict instance as p-list. Encoder specific options can be passed using kwargs: @@ -262,17 +290,17 @@ def to_plist(self, **kwargs): Return the encoded string and optionally save it at 'filepath'. A ValueError is raised in case of failure. """ - return self._encode(self.dict(), "plist", **kwargs) + return cast("str", self._encode(self.dict(), "plist", **kwargs)) - def to_query_string(self, **kwargs): + def to_query_string(self, **kwargs: Any) -> str: """ Encode the current dict instance in query-string format. Return the encoded string and optionally save it at 'filepath'. A ValueError is raised in case of failure. """ - return self._encode(self.dict(), "query_string", **kwargs) + return cast("str", self._encode(self.dict(), "query_string", **kwargs)) - def to_toml(self, **kwargs): + def to_toml(self, **kwargs: Any) -> str: """ Encode the current dict instance in TOML format. Encoder specific options can be passed using kwargs: @@ -280,9 +308,9 @@ def to_toml(self, **kwargs): Return the encoded string and optionally save it at 'filepath'. A ValueError is raised in case of failure. """ - return self._encode(self.dict(), "toml", **kwargs) + return cast("str", self._encode(self.dict(), "toml", **kwargs)) - def to_xml(self, **kwargs): + def to_xml(self, **kwargs: Any) -> str: """ Encode the current dict instance in XML format. Encoder specific options can be passed using kwargs: @@ -290,17 +318,17 @@ def to_xml(self, **kwargs): Return the encoded string and optionally save it at 'filepath'. A ValueError is raised in case of failure. """ - return self._encode(self.dict(), "xml", **kwargs) + return cast("str", self._encode(self.dict(), "xml", **kwargs)) def to_xls( self, - key="values", - sheet=0, - columns=None, - columns_row=True, - format="xlsx", - **kwargs, - ): + key: _K | str = "values", + sheet: int = 0, + columns: Any = None, + columns_row: bool = True, + format: str = "xlsx", + **kwargs: Any, + ) -> NoReturn: """ Encode a list of dicts in the current dict instance in XLS format. Encoder specific options can be passed using kwargs: @@ -316,7 +344,7 @@ def to_xls( # return self._encode(self.dict()[key], "xls", **kwargs) raise NotImplementedError - def to_yaml(self, **kwargs): + def to_yaml(self, **kwargs: Any) -> str: """ Encode the current dict instance in YAML format. Encoder specific options can be passed using kwargs: @@ -324,4 +352,4 @@ def to_yaml(self, **kwargs): Return the encoded string and optionally save it at 'filepath'. A ValueError is raised in case of failure. """ - return self._encode(self.dict(), "yaml", **kwargs) + return cast("str", self._encode(self.dict(), "yaml", **kwargs)) diff --git a/benedict/dicts/io/io_util.py b/benedict/dicts/io/io_util.py index e1a94897..581c99f7 100644 --- a/benedict/dicts/io/io_util.py +++ b/benedict/dicts/io/io_util.py @@ -1,6 +1,11 @@ +from __future__ import annotations + import tempfile +from collections.abc import Mapping, Sequence +from pathlib import Path # from botocore.exceptions import ClientError +from typing import Any, cast from urllib.parse import urlparse try: @@ -13,18 +18,21 @@ import fsutil from benedict.extras import require_s3 -from benedict.serializers import get_format_by_path, get_serializer_by_format +from benedict.serializers import ( + get_format_by_path, + get_serializer_by_format, +) from benedict.utils import type_util -def autodetect_format(s): +def autodetect_format(s: Any) -> str | None: s = str(s) if any([is_url(s), is_s3(s), is_filepath(s)]): return get_format_by_path(s) return None -def check_source(source, allowed_sources): +def check_source(source: str, allowed_sources: str | Sequence[Any]) -> None: # enforce allowed_sources to be a list of strings if not allowed_sources: allowed_sources = ["*"] @@ -42,7 +50,7 @@ def check_source(source, allowed_sources): raise ValueError(f"Invalid source: '{source}' (source not allowed).") -def decode(s, format, **kwargs): +def decode(s: Any, format: str, **kwargs: Any) -> Any: s = str(s) serializer = get_serializer_by_format(format) if not serializer: @@ -55,7 +63,7 @@ def decode(s, format, **kwargs): return data -def encode(d, format, filepath=None, **kwargs): +def encode(d: Any, format: str, filepath: str | None = None, **kwargs: Any) -> Any: serializer = get_serializer_by_format(format) if not serializer: raise ValueError(f"Invalid format: {format}.") @@ -67,7 +75,7 @@ def encode(d, format, filepath=None, **kwargs): return content -def is_binary_format(format): +def is_binary_format(format: str | None) -> bool: return format in [ "xls", "xlsx", @@ -75,25 +83,30 @@ def is_binary_format(format): ] -def is_data(s): +def is_data(s: str | bytes) -> bool: return len(s.splitlines()) > 1 -def is_filepath(s): - return fsutil.is_file(s) or ( - get_format_by_path(s) and not is_data(s) and not is_s3(s) and not is_url(s) +def is_filepath(s: Path | str) -> bool: + if fsutil.is_file(s): + return True + return bool( + get_format_by_path(s) + and not is_data(cast("str", s)) + and not is_s3(cast("str", s)) + and not is_url(cast("str", s)) ) -def is_s3(s): +def is_s3(s: str) -> bool: return s.startswith("s3://") -def is_url(s): +def is_url(s: str) -> bool: return any(s.startswith(protocol) for protocol in ["http://", "https://"]) -def parse_s3_url(url): +def parse_s3_url(url: str) -> dict[str, str]: parsed = urlparse(url, allow_fragments=False) bucket = parsed.netloc key = parsed.path.lstrip("/") @@ -107,7 +120,9 @@ def parse_s3_url(url): } -def read_content(s, format=None, options=None): +def read_content( + s: str, format: str | None = None, options: dict[str, Any] | None = None +) -> str: # s -> filepath or url or data # options.setdefault("format", format) options = options or {} @@ -131,14 +146,16 @@ def read_content(s, format=None, options=None): return s -def read_content_from_file(filepath, format=None): +def read_content_from_file(filepath: str, format: str | None = None) -> str: binary_format = is_binary_format(format) if binary_format: return filepath - return fsutil.read_file(filepath) + return fsutil.read_file(filepath) # type: ignore[no-any-return] -def read_content_from_s3(url, s3_options, format=None): +def read_content_from_s3( + url: str, s3_options: Mapping[str, Any], format: str | None = None +) -> str: require_s3(installed=s3_installed) s3_url = parse_s3_url(url) dirpath = tempfile.gettempdir() @@ -151,27 +168,31 @@ def read_content_from_s3(url, s3_options, format=None): return content -def read_content_from_url(url, requests_options, format=None): +def read_content_from_url( + url: str, requests_options: Mapping[str, Any], format: str | None = None +) -> str: binary_format = is_binary_format(format) if binary_format: dirpath = tempfile.gettempdir() filepath = fsutil.download_file(url, dirpath=dirpath, **requests_options) - return filepath - return fsutil.read_file_from_url(url, **requests_options) + return filepath # type: ignore[no-any-return] + return fsutil.read_file_from_url(url, **requests_options) # type: ignore[no-any-return] -def write_content(filepath, content, **options): +def write_content(filepath: str, content: str, **options: Any) -> None: if is_s3(filepath): write_content_to_s3(filepath, content, **options) else: write_content_to_file(filepath, content, **options) -def write_content_to_file(filepath, content, **options): +def write_content_to_file(filepath: str, content: str, **options: Any) -> None: fsutil.write_file(filepath, content) -def write_content_to_s3(url, content, s3_options, **options): +def write_content_to_s3( + url: str, content: str, s3_options: Mapping[str, Any], **options: Any +) -> None: require_s3(installed=s3_installed) s3_url = parse_s3_url(url) dirpath = tempfile.gettempdir() diff --git a/benedict/dicts/keyattr/keyattr_dict.py b/benedict/dicts/keyattr/keyattr_dict.py index 6cae1d94..7415b392 100644 --- a/benedict/dicts/keyattr/keyattr_dict.py +++ b/benedict/dicts/keyattr/keyattr_dict.py @@ -1,62 +1,74 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, cast + +from typing_extensions import TypeVar + from benedict.dicts.base import BaseDict +_K = TypeVar("_K", default=str) +_V = TypeVar("_V", default=Any) + -class KeyattrDict(BaseDict): - _keyattr_enabled = None - _keyattr_dynamic = None +class KeyattrDict(BaseDict[_K, _V]): + _keyattr_enabled: bool | None = None + _keyattr_dynamic: bool | None = None - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: self._keyattr_enabled = kwargs.pop("keyattr_enabled", True) self._keyattr_dynamic = kwargs.pop("keyattr_dynamic", False) super().__init__(*args, **kwargs) @property - def keyattr_enabled(self): + def keyattr_enabled(self) -> bool | None: return self._keyattr_enabled @keyattr_enabled.setter - def keyattr_enabled(self, value): + def keyattr_enabled(self, value: bool) -> None: self._keyattr_enabled = value @property - def keyattr_dynamic(self): + def keyattr_dynamic(self) -> bool | None: return self._keyattr_dynamic @keyattr_dynamic.setter - def keyattr_dynamic(self, value): + def keyattr_dynamic(self, value: bool) -> None: self._keyattr_dynamic = value - def __getattr__(self, attr): + def __getattr__(self, attr: _K | str) -> Any: attr_message = f"{self.__class__.__name__!r} object has no attribute {attr!r}" + attr_k = cast("_K", attr) if not self._keyattr_enabled: raise AttributeError(attr_message) try: - return self.__getitem__(attr) + return self.__getitem__(attr_k) except KeyError: - if attr.startswith("_"): + if isinstance(attr, str) and attr.startswith("_"): raise AttributeError(attr_message) from None if not self._keyattr_dynamic: raise AttributeError(attr_message) from None - self.__setitem__(attr, {}) - return self.__getitem__(attr) + self.__setitem__(attr_k, cast("_V", {})) + return self.__getitem__(attr_k) - def __setattr__(self, attr, value): + def __setattr__(self, attr: _K | str, value: Any) -> None: attr_message = f"{self.__class__.__name__!r} object has no attribute {attr!r}" + attr_k = cast("_K", attr) if attr in self: # set existing key if not self._keyattr_enabled: raise AttributeError(attr_message) - self.__setitem__(attr, value) - elif hasattr(self.__class__, attr): + self.__setitem__(attr_k, value) + elif isinstance(attr, str) and hasattr(self.__class__, attr): # set existing attr super().__setattr__(attr, value) else: # set new key if not self._keyattr_enabled: raise AttributeError(attr_message) - self.__setitem__(attr, value) + self.__setitem__(attr_k, value) - def __setstate__(self, state): + def __setstate__(self, state: Mapping[str, Any]) -> None: super().__setstate__(state) self._keyattr_enabled = state["_keyattr_enabled"] self._keyattr_dynamic = state["_keyattr_dynamic"] diff --git a/benedict/dicts/keylist/keylist_dict.py b/benedict/dicts/keylist/keylist_dict.py index a337a017..3e330f45 100644 --- a/benedict/dicts/keylist/keylist_dict.py +++ b/benedict/dicts/keylist/keylist_dict.py @@ -1,95 +1,116 @@ +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any, cast + +from typing_extensions import TypeVar + from benedict.dicts.base import BaseDict from benedict.dicts.keylist import keylist_util from benedict.utils import type_util +_T = TypeVar("_T") +_K = TypeVar("_K", default=str) +_V = TypeVar("_V", default=Any) + -class KeylistDict(BaseDict): - def __init__(self, *args, **kwargs): +class KeylistDict(BaseDict[list[str | int] | tuple[str | int, ...] | _K, _V]): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - def __contains__(self, key): + def __contains__(self, key: object) -> bool: if type_util.is_list(key): - return self._contains_by_keys(key) + return self._contains_bystreys(key) return super().__contains__(key) - def _contains_by_keys(self, keys): + def _contains_bystreys(self, keys: Sequence[_K]) -> bool: parent, _, _ = keylist_util.get_item(self, keys) if type_util.is_dict_or_list_or_tuple(parent): return True return False - def __delitem__(self, key): + def __delitem__(self, key: _K | list[Any] | tuple[str | int, ...]) -> None: if type_util.is_list(key): - self._delitem_by_keys(key) + self._delitem_bystreys(key) return super().__delitem__(key) - def _delitem_by_keys(self, keys): + def _delitem_bystreys(self, keys: Any) -> None: parent, key, _ = keylist_util.get_item(self, keys) if type_util.is_dict_or_list(parent): del parent[key] return elif type_util.is_tuple(parent): # raise the standard TypeError - del parent[key] + del parent[key] # type: ignore[unreachable] raise KeyError(f"Invalid keys: {keys!r}") - def __getitem__(self, key): + def __getitem__(self, key: list[str | int] | _K) -> _V: # type: ignore[override] if type_util.is_list(key): - return self._getitem_by_keys(key) + return cast("_V", self._getitem_bystreys(key)) return super().__getitem__(key) - def _getitem_by_keys(self, keys): + def _getitem_bystreys(self, keys: Any) -> Any: parent, key, _ = keylist_util.get_item(self, keys) if type_util.is_dict_or_list_or_tuple(parent): return parent[key] raise KeyError(f"Invalid keys: {keys!r}") - def __setitem__(self, key, value): + def __setitem__( + self, key: list[str | int] | tuple[str | int, ...] | _K, value: _V + ) -> None: if type_util.is_list(key): - self._setitem_by_keys(key, value) + self._setitem_bystreys(key, value) return super().__setitem__(key, value) - def _setitem_by_keys(self, keys, value): + def _setitem_bystreys(self, keys: Any, value: _V) -> None: keylist_util.set_item(self, keys, value) - def get(self, key, default=None): + def get( # type: ignore[override] + self, + key: list[str | int] | tuple[str | int, ...] | _K, + default: _V | None = None, + ) -> _V | None: if type_util.is_list(key): - return self._get_by_keys(key, default) + return cast("_V | None", self._get_bystreys(key, default)) return super().get(key, default) - def _get_by_keys(self, keys, default=None): + def _get_bystreys(self, keys: Any, default: _T | _V | None = None) -> Any: parent, key, _ = keylist_util.get_item(self, keys) if type_util.is_dict(parent): return parent.get(key, default) elif type_util.is_list_or_tuple(parent): - return parent[key] + return parent[key] # type: ignore[unreachable] return default - def pop(self, key, *args): + def pop(self, key: _K | list[str], *args: Any) -> Any: # type: ignore[override] if type_util.is_list(key): - return self._pop_by_keys(key, *args) + return self._pop_bystreys(key, *args) return super().pop(key, *args) - def _pop_by_keys(self, keys, *args): + def _pop_bystreys(self, keys: Sequence[Any], *args: Any) -> Any: parent, key, _ = keylist_util.get_item(self, keys) if type_util.is_dict(parent): return parent.pop(key, *args) elif type_util.is_list(parent): - return parent.pop(key) + return parent.pop(key) # type: ignore[unreachable] elif type_util.is_tuple(parent): # raise the standard TypeError - del parent[key] + del parent[key] # type: ignore[unreachable] if args: return args[0] raise KeyError(f"Invalid keys: {keys!r}") - def set(self, key, value): + def set(self, key: _K, value: _V) -> None: self[key] = value - def setdefault(self, key, default=None): + def setdefault( # type: ignore[override] + self, + key: list[str | int] | tuple[str | int, ...] | _K, + default: _V | None = None, + ) -> _V | None: if key not in self: - self[key] = default + self[key] = default # type: ignore[assignment] return default - return self[key] + return self[key] # type: ignore[index] diff --git a/benedict/dicts/keylist/keylist_util.py b/benedict/dicts/keylist/keylist_util.py index bfce35cd..cfa773c1 100644 --- a/benedict/dicts/keylist/keylist_util.py +++ b/benedict/dicts/keylist/keylist_util.py @@ -1,13 +1,21 @@ +from __future__ import annotations + +from collections.abc import Iterable, Mapping, Sequence +from typing import Any, TypeVar + from benedict.utils import type_util +_K = TypeVar("_K") +_V = TypeVar("_V") + -def _get_index(key): +def _get_index(key: Any) -> int | None: if type_util.is_integer(key): return key return None -def _get_item_key_and_value(item, key): +def _get_item_key_and_value(item: Any, key: Any) -> tuple[Any, Any]: if type_util.is_list_or_tuple(item): index = _get_index(key) if index is not None: @@ -17,7 +25,7 @@ def _get_item_key_and_value(item, key): raise KeyError(f"Invalid key: {key!r}") -def _get_or_new_item_value(item, key, subkey): +def _get_or_new_item_value(item: Any, key: Any, subkey: Any) -> Any: try: _, value = _get_item_key_and_value(item, key) if not type_util.is_dict_or_list_or_tuple(value): @@ -28,12 +36,12 @@ def _get_or_new_item_value(item, key, subkey): return value -def _new_item_value(key): +def _new_item_value(key: Any) -> dict[Any, Any] | list[Any]: index = _get_index(key) return {} if index is None else [] -def _set_item_value(item, key, value): +def _set_item_value(item: Any, key: Any, value: Any) -> None: index = _get_index(key) if index is not None: try: @@ -47,13 +55,17 @@ def _set_item_value(item, key, value): item[key] = value -def get_item(d, keys): +def get_item( + d: Mapping[_K, _V], keys: Sequence[Any] +) -> tuple[Mapping[_K, _V], _K, _V] | tuple[None, None, None]: items = get_items(d, keys) return items[-1] if items else (None, None, None) -def get_items(d, keys): - items = [] +def get_items( + d: Mapping[_K, _V], keys: Iterable[Any] +) -> list[tuple[Mapping[_K, _V], _K, _V] | tuple[None, None, None]]: + items: list[tuple[Mapping[_K, _V], _K, _V] | tuple[None, None, None]] = [] item = d for key in keys: try: @@ -66,7 +78,7 @@ def get_items(d, keys): return items -def set_item(d, keys, value): +def set_item(d: Mapping[_K, _V], keys: Sequence[Any], value: _V) -> None: item = d i = 0 j = len(keys) diff --git a/benedict/dicts/keypath/keypath_dict.py b/benedict/dicts/keypath/keypath_dict.py index d372c144..d5d18d9f 100644 --- a/benedict/dicts/keypath/keypath_dict.py +++ b/benedict/dicts/keypath/keypath_dict.py @@ -1,11 +1,21 @@ +from __future__ import annotations + +from collections.abc import Iterable, Mapping +from typing import Any + +from typing_extensions import Self, TypeVar + from benedict.dicts import KeylistDict from benedict.dicts.keypath import keypath_util +_K = keypath_util.KeyType +_V = TypeVar("_V", default=Any) + -class KeypathDict(KeylistDict): - _keypath_separator = None +class KeypathDict(KeylistDict[_K, _V]): + _keypath_separator: str | None = None - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: self._keypath_separator = kwargs.pop("keypath_separator", ".") check_keys = kwargs.pop("check_keys", True) super().__init__(*args, **kwargs) @@ -13,32 +23,32 @@ def __init__(self, *args, **kwargs): keypath_util.check_keys(self, self._keypath_separator) @property - def keypath_separator(self): + def keypath_separator(self) -> str | None: return self._keypath_separator @keypath_separator.setter - def keypath_separator(self, value): + def keypath_separator(self, value: str) -> None: keypath_util.check_keys(self, value) self._keypath_separator = value - def __contains__(self, key): - return super().__contains__(self._parse_key(key)) + def __contains__(self, key: object) -> bool: + return super().__contains__(self._parse_key(key)) # type: ignore[arg-type] - def __delitem__(self, key): - super().__delitem__(self._parse_key(key)) + def __delitem__(self, key: object) -> None: + super().__delitem__(self._parse_key(key)) # type: ignore[arg-type] - def __getitem__(self, key): + def __getitem__(self, key: _K) -> _V: return super().__getitem__(self._parse_key(key)) - def __setitem__(self, key, value): + def __setitem__(self, key: _K, value: _V) -> None: keypath_util.check_keys(value, self._keypath_separator) super().__setitem__(self._parse_key(key), value) - def __setstate__(self, state): + def __setstate__(self, state: Mapping[str, Any]) -> None: super().__setstate__(state) self._keypath_separator = state["_keypath_separator"] - def _parse_key(self, key): + def _parse_key(self, key: _K) -> _K: keys = keypath_util.parse_keys(key, self._keypath_separator) keys_count = len(keys) if keys_count == 0: @@ -48,18 +58,20 @@ def _parse_key(self, key): return keys @classmethod - def fromkeys(cls, sequence, value=None): + def fromkeys( # type: ignore[override] + cls, sequence: Iterable[_K], value: _V | None = None + ) -> Self: d = cls() for key in sequence: - d[key] = value + d[key] = value # type: ignore[assignment] return d - def get(self, key, default=None): + def get(self, key: _K, default: _V | None = None) -> _V | None: # type: ignore[override] return super().get(self._parse_key(key), default) - def pop(self, key, *args): - return super().pop(self._parse_key(key), *args) + def pop(self, key: _K, *args: Any) -> _V: # type: ignore[override] + return super().pop(self._parse_key(key), *args) # type: ignore[no-any-return] - def update(self, other): + def update(self, other: Mapping[Any, _V]) -> None: # type: ignore[override] keypath_util.check_keys(other, self._keypath_separator) super().update(other) diff --git a/benedict/dicts/keypath/keypath_util.py b/benedict/dicts/keypath/keypath_util.py index fcff75fd..310e35e9 100644 --- a/benedict/dicts/keypath/keypath_util.py +++ b/benedict/dicts/keypath/keypath_util.py @@ -1,19 +1,23 @@ +from __future__ import annotations + import re +from typing import Any, TypeAlias from benedict.core import traverse from benedict.utils import type_util KEY_INDEX_RE = r"(?:\[[\'\"]*(\-?[\d]+)[\'\"]*\]){1}$" +KeyType: TypeAlias = list[str | int] | tuple[str | int, ...] | int | str | None -def check_keys(d, separator): +def check_keys(d: Any, separator: str | None) -> None: """ Check if dict keys contain keypath separator. """ if not type_util.is_dict(d) or not separator: return - def check_key(parent, key, value): + def check_key(parent: Any, key: KeyType, value: Any) -> None: if key and type_util.is_string(key) and separator in key: raise ValueError( f"Key should not contain keypath separator {separator!r}, found: {key!r}." @@ -22,7 +26,7 @@ def check_key(parent, key, value): traverse(d, check_key) -def parse_keys(keypath, separator): +def parse_keys(keypath: KeyType, separator: str | None) -> list[str | int]: """ Parse keys from keylist or keypath using the given separator. """ @@ -34,13 +38,13 @@ def parse_keys(keypath, separator): return _split_keys_and_indexes(keypath, separator) -def _split_key_indexes(key): +def _split_key_indexes(key: str) -> list[str | int]: """ Splits key indexes: eg. 'item[0][1]' -> ['item', 0, 1]. """ if "[" in key and key.endswith("]"): - keys = [] + keys: list[str | int] = [] while True: matches = re.findall(KEY_INDEX_RE, key) if matches: @@ -55,7 +59,7 @@ def _split_key_indexes(key): return [key] -def _split_keys(keypath, separator): +def _split_keys(keypath: str, separator: str | None) -> list[str]: """ Splits keys using the given separator: eg. 'item.subitem[1]' -> ['item', 'subitem[1]']. @@ -65,14 +69,14 @@ def _split_keys(keypath, separator): return [keypath] -def _split_keys_and_indexes(keypath, separator): +def _split_keys_and_indexes(keypath: Any, separator: str | None) -> list[Any]: """ Splits keys and indexes using the given separator: eg. 'item[0].subitem[1]' -> ['item', 0, 'subitem', 1]. """ if type_util.is_string(keypath): keys1 = _split_keys(keypath, separator) - keys2 = [] + keys2: list[Any] = [] for key in keys1: keys2 += _split_key_indexes(key) return keys2 diff --git a/benedict/dicts/parse/parse_dict.py b/benedict/dicts/parse/parse_dict.py index 32d1f696..c4be21ee 100644 --- a/benedict/dicts/parse/parse_dict.py +++ b/benedict/dicts/parse/parse_dict.py @@ -1,18 +1,37 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping, Sequence +from datetime import date, datetime from decimal import Decimal +from typing import Any, cast + +from typing_extensions import TypeVar from benedict.dicts.base import BaseDict from benedict.dicts.parse import parse_util from benedict.utils import type_util +_K = TypeVar("_K", default=str) +_V = TypeVar("_V", default=Any) + +ParserFunc = Callable[..., Any] + -class ParseDict(BaseDict): - def __init__(self, *args, **kwargs): +class ParseDict(BaseDict[_K, _V]): + def __init__(self, *args: Any, **kwargs: Any) -> None: """ Constructs a new instance. """ super().__init__(*args, **kwargs) - def _get_value(self, key, default, choices, parser_func, parser_kwargs=None): + def _get_value( + self, + key: Any, + default: Any, + choices: Any, + parser_func: ParserFunc, + parserstrwargs: Any = None, + ) -> Any: """ Get value by key, or keypath core method. If choices and value is in choices return value otherwise default. @@ -25,7 +44,7 @@ def _get_value(self, key, default, choices, parser_func, parser_kwargs=None): return default # If not of the desired type, try to parse it using parser_func. - value = parser_func(value, **(parser_kwargs or {})) + value = parser_func(value, **(parserstrwargs or {})) # If value is None after parsing return default value. if value is None: return default @@ -41,8 +60,13 @@ def _get_value(self, key, default, choices, parser_func, parser_kwargs=None): return value def _get_values_list( - self, key, default, separator, parser_func, parser_kwargs=None - ): + self, + key: _K, + default: list[Any] | None, + separator: str | None, + parser_func: ParserFunc, + parserstrwargs: Any = None, + ) -> list[Any]: """ Get value by key or keypath trying to return it as list of bool values. If separator is specified and value is a string it will be splitted. @@ -50,52 +74,89 @@ def _get_values_list( if key not in self: return default or [] values_list = self.get_list(key, [], separator) - return [parser_func(value, **(parser_kwargs or {})) for value in values_list] + return [parser_func(value, **(parserstrwargs or {})) for value in values_list] - def get_bool(self, key, default=False): + def get_bool(self, key: _K, default: bool = False) -> bool: """ Get value by key or keypath trying to return it as bool. Values like `1`, `true`, `yes`, `on` will be returned as `True`. """ - return self._get_value(key, default, [True, False], parse_util.parse_bool) + return cast( + "bool", self._get_value(key, default, [True, False], parse_util.parse_bool) + ) - def get_bool_list(self, key, default=None, separator=","): + def get_bool_list( + self, key: _K, default: list[bool] | None = None, separator: str = "," + ) -> list[bool]: """ Get value by key or keypath trying to return it as list of bool values. If separator is specified and value is a string it will be splitted. """ return self._get_values_list(key, default, separator, parse_util.parse_bool) - def get_date(self, key, default=None, format=None, choices=None): + def get_date( + self, + key: _K, + default: date | None = None, + format: str | None = None, + choices: Sequence[date] | None = None, + ) -> date | None: """ Get value by key or keypath trying to return it as date. If format is not specified it will be autodetected. If choices and value is in choices return value otherwise default. """ - return self._get_value( - key, default, choices, parse_util.parse_date, {"format": format} + return cast( + "date | None", + self._get_value( + key, default, choices, parse_util.parse_date, {"format": format} + ), ) - def get_date_list(self, key, default=None, format=None, separator=","): + def get_date_list( + self, + key: _K, + default: Any = None, + format: str | None = None, + separator: str = ",", + ) -> list[date]: """ Get value by key or keypath trying to return it as list of date values. If separator is specified and value is a string it will be splitted. """ - return self._get_values_list( - key, default, separator, parse_util.parse_date, {"format": format} + return cast( + "list[date]", + self._get_values_list( + key, default, separator, parse_util.parse_date, {"format": format} + ), ) - def get_datetime(self, key, default=None, format=None, choices=None): + def get_datetime( + self, + key: _K, + default: datetime | None = None, + format: str | None = None, + choices: Sequence[datetime] | None = None, + ) -> datetime | None: """ Get value by key or keypath trying to return it as datetime. If format is not specified it will be autodetected. If choices and value is in choices return value otherwise default. """ - return self._get_value( - key, default, choices, parse_util.parse_datetime, {"format": format} + return cast( + "datetime | None", + self._get_value( + key, default, choices, parse_util.parse_datetime, {"format": format} + ), ) - def get_datetime_list(self, key, default=None, format=None, separator=","): + def get_datetime_list( + self, + key: _K, + default: list[datetime] | None = None, + format: str | None = None, + separator: str = ",", + ) -> list[datetime]: """ Get value by key or keypath trying to return it as list of datetime values. If separator is specified and value is a string it will be splitted. @@ -104,79 +165,134 @@ def get_datetime_list(self, key, default=None, format=None, separator=","): key, default, separator, parse_util.parse_datetime, {"format": format} ) - def get_decimal(self, key, default=Decimal("0.0"), choices=None): # noqa: B008 + def get_decimal( + self, + key: _V, + default: Decimal | None = Decimal("0.0"), + choices: Sequence[Decimal] | None = None, + ) -> Decimal: # noqa: B008 """ Get value by key or keypath trying to return it as Decimal. If choices and value is in choices return value otherwise default. """ - return self._get_value(key, default, choices, parse_util.parse_decimal) + return cast( + "Decimal", + self._get_value(key, default, choices, parse_util.parse_decimal), + ) - def get_decimal_list(self, key, default=None, separator=","): + def get_decimal_list( + self, key: _K, default: list[Decimal] | None = None, separator: str = "," + ) -> list[Decimal]: """ Get value by key or keypath trying to return it as list of Decimal values. If separator is specified and value is a string it will be splitted. """ - return self._get_values_list(key, default, separator, parse_util.parse_decimal) + return cast( + "list[Decimal]", + self._get_values_list(key, default, separator, parse_util.parse_decimal), + ) - def get_dict(self, key, default=None): + def get_dict( + self, key: _K, default: Mapping[str, Any] | None = None + ) -> dict[_K, Any]: """ Get value by key or keypath trying to return it as dict. If value is a json string it will be automatically decoded. """ - return self._get_value(key, default or {}, None, parse_util.parse_dict) + return cast( + "dict[_K, Any]", + self._get_value(key, default or {}, None, parse_util.parse_dict), + ) - def get_email(self, key, default="", choices=None, check_blacklist=True): + def get_email( + self, + key: _K, + default: str = "", + choices: Sequence[str] | None = None, + check_blacklist: bool = True, + ) -> str: """ Get email by key or keypath and return it. If value is blacklisted it will be automatically ignored. If check_blacklist is False, it will be not ignored even if blacklisted. """ - return self._get_value( - key, - default, - choices, - parse_util.parse_email, - {"check_blacklist": check_blacklist}, + return cast( + "str", + self._get_value( + key, + default, + choices, + parse_util.parse_email, + {"check_blacklist": check_blacklist}, + ), ) - def get_float(self, key, default=0.0, choices=None): + def get_float( + self, key: _K, default: float = 0.0, choices: Sequence[_V] | None = None + ) -> float: """ Get value by key or keypath trying to return it as float. If choices and value is in choices return value otherwise default. """ - return self._get_value(key, default, choices, parse_util.parse_float) + return cast( + "float", + self._get_value(key, default, choices, parse_util.parse_float), + ) - def get_float_list(self, key, default=None, separator=","): + def get_float_list( + self, key: _K, default: list[float] | None = None, separator: str = "," + ) -> list[float]: """ Get value by key or keypath trying to return it as list of float values. If separator is specified and value is a string it will be splitted. """ return self._get_values_list(key, default, separator, parse_util.parse_float) - def get_int(self, key, default=0, choices=None): + def get_int( + self, key: _K, default: int = 0, choices: Sequence[int] | None = None + ) -> int: """ Get value by key or keypath trying to return it as int. If choices and value is in choices return value otherwise default. """ - return self._get_value(key, default, choices, parse_util.parse_int) + return cast("int", self._get_value(key, default, choices, parse_util.parse_int)) - def get_int_list(self, key, default=None, separator=","): + def get_int_list( + self, key: _K, default: list[int] | None = None, separator: str = "," + ) -> list[int] | None: """ Get value by key or keypath trying to return it as list of int values. If separator is specified and value is a string it will be splitted. """ - return self._get_values_list(key, default, separator, parse_util.parse_int) + return cast( + "list[int]", + self._get_values_list(key, default, separator, parse_util.parse_int), + ) - def get_list(self, key, default=None, separator=","): + def get_list( + self, + key: _K, + default: Sequence[Any] | None = None, + separator: str | None = ",", + ) -> list[Any]: """ Get value by key or keypath trying to return it as list. If separator is specified and value is a string it will be splitted. """ - return self._get_value( - key, default or [], None, parse_util.parse_list, {"separator": separator} + return cast( + "list[Any]", + self._get_value( + key, + default or [], + None, + parse_util.parse_list, + {"separator": separator}, + ), ) - def get_list_item(self, key, index=0, default=None, separator=","): + def get_list_item( + self, key: _K, index: int = 0, default: Any = None, separator: str = "," + ) -> Any: """ Get list by key or keypath and return value at the specified index. If separator is specified and list value is a string it will be splitted. @@ -191,58 +307,82 @@ def get_list_item(self, key, index=0, default=None, separator=","): else: return default - def get_phonenumber(self, key, country_code=None, default=None): + def get_phonenumber( + self, + key: _K, + country_code: str | None = None, + default: dict[str, str] | None = None, + ) -> dict[str, str] | None: """ Get phonenumber by key or keypath and return a dict with different formats (e164, international, national). If country code is specified (alpha 2 code), it will be used to parse phone number correctly. """ - return self._get_value( - key, - default or {}, - None, - parse_util.parse_phonenumber, - {"country_code": country_code}, + return cast( + "dict[str, str]", + self._get_value( + key, + default or {}, + None, + parse_util.parse_phonenumber, + {"country_code": country_code}, + ), ) - def get_slug(self, key, default="", choices=None): + def get_slug( + self, key: _K, default: str = "", choices: Sequence[str] | None = None + ) -> str: """ Get value by key or keypath trying to return it as slug. If choices and value is in choices return value otherwise default. """ - return self._get_value(key, default, choices, parse_util.parse_slug) + return cast( + "str", self._get_value(key, default, choices, parse_util.parse_slug) + ) - def get_slug_list(self, key, default=None, separator=","): + def get_slug_list( + self, key: _K, default: list[str] | None = None, separator: str = "," + ) -> list[str]: """ Get value by key or keypath trying to return it as list of slug values. If separator is specified and value is a string it will be splitted. """ return self._get_values_list(key, default, separator, parse_util.parse_slug) - def get_str(self, key, default="", choices=None): + def get_str( + self, key: _K, default: str = "", choices: Sequence[str] | None = None + ) -> str: """ Get value by key or keypath trying to return it as string. Encoding issues will be automatically fixed. If choices and value is in choices return value otherwise default. """ - return self._get_value(key, default, choices, parse_util.parse_str) + return cast("str", self._get_value(key, default, choices, parse_util.parse_str)) - def get_str_list(self, key, default=None, separator=","): + def get_str_list( + self, key: _K, default: list[str] | None = None, separator: str = "," + ) -> list[str]: """ Get value by key or keypath trying to return it as list of str values. If separator is specified and value is a string it will be splitted. """ return self._get_values_list(key, default, separator, parse_util.parse_str) - def get_uuid(self, key, default="", choices=None): + def get_uuid( + self, key: _K, default: str = "", choices: Sequence[str] | None = None + ) -> str: """ Get value by key or keypath trying to return it as valid uuid. If choices and value is in choices return value otherwise default. """ - return self._get_value(key, default, choices, parse_util.parse_uuid) + return cast( + "str", self._get_value(key, default, choices, parse_util.parse_uuid) + ) - def get_uuid_list(self, key, default=None, separator=","): + def get_uuid_list( + self, key: _K, default: list[str] | None = None, separator: str = "," + ) -> list[str]: """ Get value by key or keypath trying to return it as list of valid uuid values. If separator is specified and value is a string it will be splitted. diff --git a/benedict/dicts/parse/parse_util.py b/benedict/dicts/parse/parse_util.py index dc68c5af..766a9100 100644 --- a/benedict/dicts/parse/parse_util.py +++ b/benedict/dicts/parse/parse_util.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import re -from datetime import datetime +from datetime import date, datetime from decimal import Decimal, DecimalException try: @@ -14,14 +16,116 @@ parse_installed = False +from collections.abc import Callable +from typing import Any, cast + from slugify import slugify +from typing_extensions import TypeIs, overload from benedict.extras import require_parse from benedict.serializers import JSONSerializer from benedict.utils import type_util - -def _parse_with(val, type_checker, parser, **kwargs): +CallableIsBool = Callable[[Any], TypeIs[bool]] +CallableIsDecimal = Callable[[Any], TypeIs[Decimal]] +CallableIsDict = Callable[[Any], TypeIs[dict[str, Any]]] +CallableIsListOrTuple = Callable[[Any], TypeIs[list[Any] | tuple[Any, ...]]] +CallableIsFloat = Callable[[Any], TypeIs[float]] +CallableIsInt = Callable[[Any], TypeIs[int]] +CallableIsStr = Callable[[Any], TypeIs[str]] + +ParseWithTypeChecker = ( + CallableIsBool + | CallableIsDecimal + | CallableIsDict + | CallableIsFloat + | CallableIsInt + | CallableIsListOrTuple + | CallableIsStr +) +ParseWithParser = ( + Callable[..., bool | None] + | Callable[..., Decimal | None] + | Callable[..., dict[str, Any] | None] + | Callable[..., float | None] + | Callable[..., int | None] + | Callable[..., list[Any] | tuple[Any, ...] | None] + | Callable[..., str | None] +) +ParseWithInputOutput = ( + bool | Decimal | float | dict[str, Any] | list[Any] | str | tuple[Any, ...] | None +) + + +@overload +def _parse_with( + val: bool | str, + type_checker: CallableIsBool | None, + parser: Callable[..., bool | None], + **kwargs: Any, +) -> bool | None: ... + + +@overload +def _parse_with( + val: Decimal | str, + type_checker: CallableIsDecimal | None, + parser: Callable[..., Decimal | None], + **kwargs: Any, +) -> Decimal | None: ... + + +@overload +def _parse_with( + val: str | dict[str, Any], + type_checker: CallableIsDict | None, + parser: Callable[..., dict[str, Any] | None], + **kwargs: Any, +) -> dict[str, Any] | None: ... + + +@overload +def _parse_with( + val: int | str, + type_checker: CallableIsInt | None, + parser: Callable[..., int | None], + **kwargs: Any, +) -> int | None: ... + + +@overload +def _parse_with( + val: float | str, + type_checker: CallableIsFloat | None, + parser: Callable[..., float | None], + **kwargs: Any, +) -> float | None: ... + + +@overload +def _parse_with( + val: list[Any] | tuple[Any, ...] | str, + type_checker: CallableIsListOrTuple | None, + parser: Callable[..., list[Any] | tuple[Any, ...] | None], + **kwargs: Any, +) -> list[Any] | tuple[Any, ...] | None: ... + + +@overload +def _parse_with( + val: str, + type_checker: CallableIsStr | None, + parser: Callable[..., str | None], + **kwargs: Any, +) -> str | None: ... + + +def _parse_with( # type: ignore[misc] + val: ParseWithInputOutput, + type_checker: ParseWithTypeChecker, + parser: ParseWithParser, + **kwargs: Any, +) -> ParseWithInputOutput: if val is None: return None if callable(type_checker) and type_checker(val): @@ -32,7 +136,7 @@ def _parse_with(val, type_checker, parser, **kwargs): return parser(s, **kwargs) -def _parse_bool(val): +def _parse_bool(val: str) -> bool | None: val = val.lower() if val in ["1", "true", "yes", "ok", "on"]: return True @@ -41,39 +145,39 @@ def _parse_bool(val): return None -def parse_bool(val): +def parse_bool(val: Any) -> bool | None: return _parse_with(val, type_util.is_bool, _parse_bool) -def parse_date(val, format=None): - val = parse_datetime(val, format) - if val: - return val.date() +def parse_date(val: str | datetime, format: str | None = None) -> date | None: + v = parse_datetime(val, format) + if v: + return v.date() return None -def _parse_datetime_with_format(val, format): +def _parse_datetime_with_format(val: str, format: str | None) -> datetime | None: try: - return datetime.strptime(val, format) + return datetime.strptime(val, format or "") except Exception: return None -def _parse_datetime_without_format(val): +def _parse_datetime_without_format(val: str) -> datetime | None: try: return date_parser.parse(val) except Exception: return _parse_datetime_from_timestamp(val) -def _parse_datetime_from_timestamp(val): +def _parse_datetime_from_timestamp(val: str | int | float) -> datetime | None: try: return datetime.fromtimestamp(float(val)) except Exception: return None -def parse_datetime(val, format=None): +def parse_datetime(val: str | datetime, format: str | None = None) -> datetime | None: require_parse(installed=parse_installed) if type_util.is_datetime(val): return val @@ -84,18 +188,18 @@ def parse_datetime(val, format=None): return _parse_datetime_without_format(s) -def _parse_decimal(val): +def _parse_decimal(val: str) -> Decimal | None: try: return Decimal(val) except (ValueError, DecimalException): return None -def parse_decimal(val): +def parse_decimal(val: str) -> Decimal | None: return _parse_with(val, type_util.is_decimal, _parse_decimal) -def _parse_dict(val): +def _parse_dict(val: str) -> dict[Any, Any] | None: serializer = JSONSerializer() try: d = serializer.decode(val) @@ -106,22 +210,22 @@ def _parse_dict(val): return None -def parse_dict(val): +def parse_dict(val: str | dict[str, Any]) -> dict[Any, Any] | None: return _parse_with(val, type_util.is_dict, _parse_dict) -def _parse_float(val): +def _parse_float(val: str) -> float | None: try: return float(val) except ValueError: return None -def parse_float(val): +def parse_float(val: str) -> float | None: return _parse_with(val, type_util.is_float, _parse_float) -def _parse_email(val, check_blacklist=True): +def _parse_email(val: str, check_blacklist: bool = True) -> str | None: val = val.lower() if check_blacklist: if not MailChecker.is_valid(val): @@ -132,23 +236,23 @@ def _parse_email(val, check_blacklist=True): return val -def parse_email(val, check_blacklist=True): +def parse_email(val: str, check_blacklist: bool = True) -> str | None: require_parse(installed=parse_installed) return _parse_with(val, None, _parse_email, check_blacklist=check_blacklist) -def _parse_int(val): +def _parse_int(val: str) -> int | None: try: return int(val) except ValueError: return None -def parse_int(val): +def parse_int(val: int | str) -> int | None: return _parse_with(val, type_util.is_integer, _parse_int) -def _parse_list(val, separator=None): +def _parse_list(val: str, separator: str | None = None) -> list[Any] | None: if ( val.startswith("{") and val.endswith("}") @@ -157,7 +261,7 @@ def _parse_list(val, separator=None): ): try: serializer = JSONSerializer() - ls = serializer.decode(val) + ls: list[Any] | None = serializer.decode(val) if type_util.is_list(ls): return ls return None @@ -169,12 +273,18 @@ def _parse_list(val, separator=None): return None -def parse_list(val, separator=None): - val = _parse_with(val, type_util.is_list_or_tuple, _parse_list, separator=separator) - return list(val) if type_util.is_list_or_tuple(val) else val +def parse_list( + val: str | tuple[Any, ...] | list[Any], separator: str | None = None +) -> list[Any] | None: + v = _parse_with(val, type_util.is_list_or_tuple, _parse_list, separator=separator) + if type_util.is_list_or_tuple(v): + return list(v) + return v # type: ignore[return-value] -def _parse_phonenumber(val, country_code=None): +def _parse_phonenumber( + val: str, country_code: str | None = None +) -> dict[str, str] | None: try: phone_obj = phonenumbers.parse(val, country_code) if phonenumbers.is_valid_number(phone_obj): @@ -192,7 +302,9 @@ def _parse_phonenumber(val, country_code=None): return None -def parse_phonenumber(val, country_code=None): +def parse_phonenumber( + val: str, country_code: str | None = None +) -> dict[str, str] | None: require_parse(installed=parse_installed) s = parse_str(val) if not s: @@ -206,16 +318,17 @@ def parse_phonenumber(val, country_code=None): return _parse_with(phone_raw, None, _parse_phonenumber, country_code=country_code) -def _parse_slug(val): - return slugify(val) +def _parse_slug(val: str) -> str: + result: str = slugify(val) + return result -def parse_slug(val): +def parse_slug(val: Any) -> str: s = parse_str(val) return _parse_slug(s) -def parse_str(val): +def parse_str(val: Any) -> str: require_parse(installed=parse_installed) if type_util.is_string(val): val = ftfy.fix_text(val) @@ -223,9 +336,9 @@ def parse_str(val): val = str(val) val = val.strip() val = " ".join(val.split()) - return val + return cast("str", val) -def parse_uuid(val): +def parse_uuid(val: str) -> str | None: s = parse_str(val) return s if type_util.is_uuid(s) else None diff --git a/benedict/exceptions.py b/benedict/exceptions.py index 823d574d..d8a16ed1 100644 --- a/benedict/exceptions.py +++ b/benedict/exceptions.py @@ -1,5 +1,5 @@ class ExtrasRequireModuleNotFoundError(ModuleNotFoundError): - def __init__(self, *, target): + def __init__(self, *, target: str) -> None: message = ( f"Extras require '[{target}]' module not found, " f"you can install it by running: " diff --git a/benedict/extras.py b/benedict/extras.py index 0a1122f5..df087aca 100644 --- a/benedict/extras.py +++ b/benedict/extras.py @@ -11,34 +11,34 @@ ] -def _require_optional_dependencies(*, target, installed): +def _require_optional_dependencies(*, target: str, installed: bool) -> None: if not installed: raise ExtrasRequireModuleNotFoundError(target=target) -def require_html(*, installed): +def require_html(*, installed: bool) -> None: _require_optional_dependencies(target="html", installed=installed) -def require_parse(*, installed): +def require_parse(*, installed: bool) -> None: _require_optional_dependencies(target="parse", installed=installed) -def require_s3(*, installed): +def require_s3(*, installed: bool) -> None: _require_optional_dependencies(target="s3", installed=installed) -def require_toml(*, installed): +def require_toml(*, installed: bool) -> None: _require_optional_dependencies(target="toml", installed=installed) -def require_xls(*, installed): +def require_xls(*, installed: bool) -> None: _require_optional_dependencies(target="xls", installed=installed) -def require_xml(*, installed): +def require_xml(*, installed: bool) -> None: _require_optional_dependencies(target="xml", installed=installed) -def require_yaml(*, installed): +def require_yaml(*, installed: bool) -> None: _require_optional_dependencies(target="yaml", installed=installed) diff --git a/benedict/py.typed b/benedict/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/benedict/serializers/__init__.py b/benedict/serializers/__init__.py index 6f2165fb..59d393f6 100644 --- a/benedict/serializers/__init__.py +++ b/benedict/serializers/__init__.py @@ -1,4 +1,9 @@ +from __future__ import annotations + import re +from typing import Any, Literal, TypedDict, cast + +from typing_extensions import overload from benedict.serializers.abstract import AbstractSerializer from benedict.serializers.base64 import Base64Serializer @@ -46,7 +51,7 @@ _XML_SERIALIZER = XMLSerializer() _YAML_SERIALIZER = YAMLSerializer() -_SERIALIZERS_LIST = [ +_SERIALIZERS_LIST: list[AbstractSerializer[Any, Any]] = [ _BASE64_SERIALIZER, _CLI_SERIALIZER, _CSV_SERIALIZER, @@ -62,17 +67,55 @@ _XML_SERIALIZER, ] -_SERIALIZERS_BY_EXTENSION = {} + +class SerializersByExtension(TypedDict, total=False): + b64: Base64Serializer + base64: Base64Serializer + cli: CLISerializer + csv: CSVSerializer + html: HTMLSerializer + ini: INISerializer + json: JSONSerializer + pickle: PickleSerializer + plist: PListSerializer + qs: QueryStringSerializer + querystring: QueryStringSerializer + toml: TOMLSerializer + xls: XLSSerializer + xlsx: XLSSerializer + xlsm: XLSSerializer + xml: XMLSerializer + yaml: YAMLSerializer + + +_SERIALIZERS_BY_EXTENSION: SerializersByExtension = {} +FormatExtension = Literal[ + "base64", + "b64", + "cli", + "csv", + "html", + "json", + "pickle", + "plist", + "qs", + "querystring", + "toml", + "xls", + "xlsx", + "xlsm", + "xml", +] for serializer in _SERIALIZERS_LIST: for extension in serializer.extensions(): - _SERIALIZERS_BY_EXTENSION[extension] = serializer + _SERIALIZERS_BY_EXTENSION[extension] = serializer # type: ignore[literal-required] _SERIALIZERS_EXTENSIONS = [ f".{extension}" for extension in _SERIALIZERS_BY_EXTENSION.keys() ] -def get_format_by_path(path): +def get_format_by_path(path: Any) -> str | None: path = str(path) path = path.lower() for extension in _SERIALIZERS_EXTENSIONS: @@ -81,8 +124,71 @@ def get_format_by_path(path): return None -def get_serializer_by_format(format): - format_key = (format or "").lower().strip() - format_key = re.sub(r"[\s\-\_]*", "", format_key) - serializer = _SERIALIZERS_BY_EXTENSION.get(format_key, None) +@overload +def get_serializer_by_format(format: Literal["base64", "b64"]) -> Base64Serializer: ... + + +@overload +def get_serializer_by_format(format: Literal["cli"]) -> CLISerializer: ... + + +@overload +def get_serializer_by_format(format: Literal["csv"]) -> CSVSerializer: ... + + +@overload +def get_serializer_by_format(format: Literal["html"]) -> HTMLSerializer: ... + + +@overload +def get_serializer_by_format(format: Literal["json"]) -> JSONSerializer: ... + + +@overload +def get_serializer_by_format(format: Literal["pickle"]) -> PickleSerializer: ... + + +@overload +def get_serializer_by_format( + format: Literal["qs", "querystring"], +) -> QueryStringSerializer: ... + + +@overload +def get_serializer_by_format(format: Literal["toml"]) -> TOMLSerializer: ... + + +@overload +def get_serializer_by_format( + format: Literal["xls", "xlsx", "xlsm"], +) -> XLSSerializer: ... + + +@overload +def get_serializer_by_format(format: Literal["xml"]) -> XMLSerializer: ... + + +@overload +def get_serializer_by_format(format: str) -> AbstractSerializer[Any, Any]: ... + + +def get_serializer_by_format( + format: FormatExtension | str, +) -> ( + Base64Serializer + | CLISerializer + | CSVSerializer + | HTMLSerializer + | JSONSerializer + | PickleSerializer + | PListSerializer + | QueryStringSerializer + | TOMLSerializer + | XLSSerializer + | XMLSerializer + | AbstractSerializer[Any, Any] +): + format_key = cast("FormatExtension", format.lower().strip()) + format_key = cast("FormatExtension", re.sub(r"[\s\-\_]*", "", format_key)) + serializer = _SERIALIZERS_BY_EXTENSION[format_key] return serializer diff --git a/benedict/serializers/abstract.py b/benedict/serializers/abstract.py index 67137b65..0202d1b1 100644 --- a/benedict/serializers/abstract.py +++ b/benedict/serializers/abstract.py @@ -1,17 +1,27 @@ -class AbstractSerializer: +from __future__ import annotations + +from typing import Any, Generic + +from typing_extensions import TypeVar + +_DT = TypeVar("_DT", default=str) # decode type +_ET = TypeVar("_ET", default=Any) # encode type + + +class AbstractSerializer(Generic[_DT, _ET]): """ This class describes an abstract serializer. """ - def __init__(self, extensions=None): + def __init__(self, extensions: list[str] | None = None) -> None: super().__init__() self._extensions = (extensions or []).copy() - def decode(self, s, **kwargs): + def decode(self, s: _DT, **kwargs: Any) -> _ET: raise NotImplementedError() - def encode(self, d, **kwargs): + def encode(self, d: _ET, **kwargs: Any) -> _DT: raise NotImplementedError() - def extensions(self): + def extensions(self) -> list[Any]: return self._extensions.copy() diff --git a/benedict/serializers/base64.py b/benedict/serializers/base64.py index bf872e83..41195869 100644 --- a/benedict/serializers/base64.py +++ b/benedict/serializers/base64.py @@ -1,16 +1,20 @@ +from __future__ import annotations + import base64 +from collections.abc import MutableMapping +from typing import Any, cast from urllib.parse import unquote from benedict.serializers.abstract import AbstractSerializer from benedict.utils import type_util -class Base64CoreSerializer(AbstractSerializer): +class Base64CoreSerializer(AbstractSerializer[str, str | bytes]): """ This class describes a base64 core serializer. """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "b64", @@ -18,7 +22,7 @@ def __init__(self): ], ) - def _fix_url_encoding_and_padding(self, s): + def _fix_url_encoding_and_padding(self, s: str) -> str: # fix urlencoded chars s = unquote(s) # fix padding @@ -27,47 +31,53 @@ def _fix_url_encoding_and_padding(self, s): s += "=" * (4 - m) return s - def decode(self, s, **kwargs): - value = self._fix_url_encoding_and_padding(s) + def decode(self, s: str, **kwargs: Any) -> str | bytes: + value: bytes | str = self._fix_url_encoding_and_padding(s) encoding = kwargs.pop("encoding", "utf-8") if encoding: - value = value.encode(encoding) + value = cast("str", value).encode(encoding) value = base64.b64decode(value) if encoding: return value.decode(encoding) return value - def encode(self, d, **kwargs): - value = d + def encode(self, d: str | bytes, **kwargs: Any) -> str: + value: str | bytes = d encoding = kwargs.pop("encoding", "utf-8") if encoding and type_util.is_string(value): value = value.encode(encoding) + if isinstance(value, str): + value = value.encode() value = base64.b64encode(value) if encoding: value = value.decode(encoding) + else: + value = value.decode() return value class Base64Serializer(Base64CoreSerializer): - def __init__(self): + def __init__(self) -> None: super().__init__() - def _pop_options(self, options): + def _pop_options( + self, options: MutableMapping[str, Any] + ) -> tuple[AbstractSerializer[Any, Any] | None, Any]: encoding = options.pop("encoding", "utf-8") - subformat = options.pop("subformat", None) + subformat = options.pop("subformat") from benedict.serializers import get_serializer_by_format serializer = get_serializer_by_format(subformat) return (serializer, encoding) - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> str | bytes: serializer, encoding = self._pop_options(kwargs) value = super().decode(s, encoding=encoding) if serializer: value = serializer.decode(value, **kwargs) return value - def encode(self, d, **kwargs): + def encode(self, d: str | bytes, **kwargs: Any) -> str: serializer, encoding = self._pop_options(kwargs) value = d if serializer: diff --git a/benedict/serializers/cli.py b/benedict/serializers/cli.py index ceab4b47..562257f0 100644 --- a/benedict/serializers/cli.py +++ b/benedict/serializers/cli.py @@ -1,12 +1,15 @@ +from __future__ import annotations + from argparse import ArgumentError, ArgumentParser from collections import Counter -from re import finditer +from re import Pattern, finditer +from typing import Any, NoReturn, cast from benedict.serializers.abstract import AbstractSerializer from benedict.utils import type_util -class CLISerializer(AbstractSerializer): +class CLISerializer(AbstractSerializer[str | None, dict[str, Any]]): """ This class describes a CLI serializer. """ @@ -33,13 +36,15 @@ class CLISerializer(AbstractSerializer): - Doesn't match: script.py, example, email@example.com """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=["cli"], ) @staticmethod - def parse_keys(regex, string): + def parse_keys(regex: str | Pattern[str], string: str | None) -> list[str]: + if not string: + return [] # For some reason findall didn't work results = [match.group(0) for match in finditer(regex, string)] return results @@ -47,11 +52,11 @@ def parse_keys(regex, string): """Helper method, returns a list of --keys based on the regex used""" @staticmethod - def _get_parser(options): + def _get_parser(options: dict[str, Any]) -> ArgumentParser: parser = ArgumentParser(**options) return parser - def decode(self, s=None, **kwargs): + def decode(self, s: str | None = None, **kwargs: Any) -> dict[str, Any]: parser = self._get_parser(options=kwargs) keys_with_values = set(self.parse_keys(self.regex_keys_with_values, s)) @@ -89,7 +94,7 @@ def decode(self, s=None, **kwargs): raise ValueError from error try: - args = parser.parse_args(s.split()) + args = parser.parse_args(cast("str", s).split()) except BaseException as error: raise ValueError from error @@ -103,5 +108,5 @@ def decode(self, s=None, **kwargs): return dict - def encode(self, d, **kwargs): + def encode(self, d: Any, **kwargs: Any) -> NoReturn: raise NotImplementedError diff --git a/benedict/serializers/csv.py b/benedict/serializers/csv.py index 952cad12..af59bd03 100644 --- a/benedict/serializers/csv.py +++ b/benedict/serializers/csv.py @@ -1,23 +1,26 @@ +from __future__ import annotations + import csv from io import StringIO +from typing import Any from benedict.serializers.abstract import AbstractSerializer from benedict.utils import type_util -class CSVSerializer(AbstractSerializer): +class CSVSerializer(AbstractSerializer[str, list[dict[str, Any]]]): """ This class describes a csv serializer. """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "csv", ], ) - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> list[dict[str, Any]]: # kwargs.setdefault('delimiter', ',') if kwargs.pop("quote", False): # TODO: add tests coverage @@ -39,7 +42,7 @@ def decode(self, s, **kwargs): ln += 1 return data - def encode(self, d, **kwargs): + def encode(self, d: list[dict[str, Any]], **kwargs: Any) -> str: ls = d # kwargs.setdefault('delimiter', ',') if kwargs.pop("quote", False): @@ -57,7 +60,7 @@ def encode(self, d, **kwargs): for item in ls: if type_util.is_dict(item): row = [item.get(key, "") for key in columns] - elif type_util.is_collection(item): + elif type_util.is_collection(item): # type: ignore[unreachable] # TODO: add tests coverage row = item else: diff --git a/benedict/serializers/html.py b/benedict/serializers/html.py index e190c815..3c5e7d73 100644 --- a/benedict/serializers/html.py +++ b/benedict/serializers/html.py @@ -1,3 +1,5 @@ +from __future__ import annotations + try: from bs4 import BeautifulSoup @@ -5,24 +7,26 @@ except ModuleNotFoundError: html_installed = False +from typing import Any, NoReturn + from benedict.extras import require_html from benedict.serializers.abstract import AbstractSerializer from benedict.serializers.xml import XMLSerializer -class HTMLSerializer(AbstractSerializer): +class HTMLSerializer(AbstractSerializer[str, dict[str, Any]]): """ This class describes a html serializer. """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "html", ], ) - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> dict[str, Any]: require_html(installed=html_installed) html_content = s soup = BeautifulSoup(html_content, "html.parser") @@ -31,5 +35,5 @@ def decode(self, s, **kwargs): data = xml_serializer.decode(xml_content) return data - def encode(self, d, **kwargs): + def encode(self, d: dict[str, Any], **kwargs: Any) -> NoReturn: raise NotImplementedError diff --git a/benedict/serializers/ini.py b/benedict/serializers/ini.py index e8de3028..ca6ec6c4 100644 --- a/benedict/serializers/ini.py +++ b/benedict/serializers/ini.py @@ -1,19 +1,23 @@ +from __future__ import annotations + +from collections.abc import Callable from configparser import DEFAULTSECT as default_section from configparser import RawConfigParser from io import StringIO from json.decoder import JSONDecodeError +from typing import Any from benedict.serializers.abstract import AbstractSerializer from benedict.serializers.json import JSONSerializer from benedict.utils import type_util -class INISerializer(AbstractSerializer): +class INISerializer(AbstractSerializer[str, dict[str, Any]]): """ This class describes an ini serializer. """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "ini", @@ -22,17 +26,24 @@ def __init__(self): self._json = JSONSerializer() @staticmethod - def _get_parser(options): + def _get_parser(options: dict[str, Any]) -> RawConfigParser: optionxform = options.pop("optionxform", lambda key: key) parser = RawConfigParser(**options) if optionxform and callable(optionxform): - parser.optionxform = optionxform + parser.optionxform = optionxform # type: ignore[method-assign] return parser @staticmethod - def _get_section_option_value(parser, section, option): + def _get_section_option_value( + parser: RawConfigParser, section: str, option: str + ) -> str | int | float | bool | None: value = None - funcs = [parser.getint, parser.getfloat, parser.getboolean, parser.get] + funcs: list[Callable[[str, str], Any]] = [ + parser.getint, + parser.getfloat, + parser.getboolean, + parser.get, + ] for func in funcs: try: value = func(section, option) @@ -41,10 +52,10 @@ def _get_section_option_value(parser, section, option): continue return value - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> dict[str, Any]: parser = self._get_parser(options=kwargs) parser.read_string(s) - data = {} + data: dict[str, Any] = {} for option, _ in parser.defaults().items(): data[option] = self._get_section_option_value( parser, default_section, option @@ -62,7 +73,7 @@ def decode(self, s, **kwargs): data[section][option] = value return data - def encode(self, d, **kwargs): + def encode(self, d: dict[str, Any], **kwargs: Any) -> str: parser = self._get_parser(options=kwargs) for key, value in d.items(): if not type_util.is_dict(value): diff --git a/benedict/serializers/json.py b/benedict/serializers/json.py index fbe92b12..db4304d1 100644 --- a/benedict/serializers/json.py +++ b/benedict/serializers/json.py @@ -1,38 +1,41 @@ +from __future__ import annotations + import json # fix benedict json dumps support - #57 #59 #61 from json import encoder +from typing import Any from benedict.serializers.abstract import AbstractSerializer from benedict.utils import type_util -class JSONSerializer(AbstractSerializer): +class JSONSerializer(AbstractSerializer[str, Any]): """ This class describes a json serializer. """ @staticmethod - def disable_c_make_encoder(): - encoder.c_make_encoder = None + def disable_c_make_encoder() -> None: + encoder.c_make_encoder = None # type: ignore[attr-defined] - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "json", ], ) - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> Any: data = json.loads(s, **kwargs) return data - def encode(self, d, **kwargs): + def encode(self, d: Any, **kwargs: Any) -> str: kwargs.setdefault("default", self._encode_default) data = json.dumps(d, **kwargs) return data - def _encode_default(self, obj): + def _encode_default(self, obj: Any) -> list[Any] | str: if type_util.is_set(obj): return list(obj) elif type_util.is_datetime(obj): diff --git a/benedict/serializers/pickle.py b/benedict/serializers/pickle.py index 20680139..8cab42ce 100644 --- a/benedict/serializers/pickle.py +++ b/benedict/serializers/pickle.py @@ -1,26 +1,32 @@ import base64 import pickle +from typing import Any + +from typing_extensions import override from benedict.serializers.abstract import AbstractSerializer -class PickleSerializer(AbstractSerializer): +class PickleSerializer(AbstractSerializer[str, Any]): """ This class describes a pickle serializer. """ - def __init__(self): + @override + def __init__(self) -> None: super().__init__( extensions=[ "pickle", ], ) - def decode(self, s, **kwargs): + @override + def decode(self, s: str, **kwargs: Any) -> Any: encoding = kwargs.pop("encoding", "utf-8") return pickle.loads(base64.b64decode(s.encode(encoding)), **kwargs) - def encode(self, d, **kwargs): + @override + def encode(self, d: Any, **kwargs: Any) -> str: encoding = kwargs.pop("encoding", "utf-8") kwargs.setdefault("protocol", 2) return base64.b64encode(pickle.dumps(d, **kwargs)).decode(encoding) diff --git a/benedict/serializers/plist.py b/benedict/serializers/plist.py index 10619b24..cfec5c4a 100644 --- a/benedict/serializers/plist.py +++ b/benedict/serializers/plist.py @@ -1,26 +1,27 @@ import plistlib +from typing import Any from benedict.serializers.abstract import AbstractSerializer -class PListSerializer(AbstractSerializer): +class PListSerializer(AbstractSerializer[Any, str]): """ This class describes a p list serializer. https://docs.python.org/3/library/plistlib.html """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "plist", ], ) - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> Any: kwargs.setdefault("fmt", plistlib.FMT_XML) encoding = kwargs.pop("encoding", "utf-8") return plistlib.loads(s.encode(encoding), **kwargs) - def encode(self, d, **kwargs): + def encode(self, d: Any, **kwargs: Any) -> str: encoding = kwargs.pop("encoding", "utf-8") return plistlib.dumps(d, **kwargs).decode(encoding) diff --git a/benedict/serializers/query_string.py b/benedict/serializers/query_string.py index 1816379b..c7272f50 100644 --- a/benedict/serializers/query_string.py +++ b/benedict/serializers/query_string.py @@ -1,15 +1,24 @@ +from __future__ import annotations + import re +from collections.abc import Mapping +from typing import Any from urllib.parse import parse_qs, urlencode +from typing_extensions import override + from benedict.serializers.abstract import AbstractSerializer -class QueryStringSerializer(AbstractSerializer): +class QueryStringSerializer( + AbstractSerializer[str, dict[str, str] | dict[str, list[str]]] +): """ This class describes a query-string serializer. """ - def __init__(self): + @override + def __init__(self) -> None: super().__init__( extensions=[ "qs", @@ -17,17 +26,22 @@ def __init__(self): ], ) - def decode(self, s, **kwargs): - flat = kwargs.pop("flat", True) + @override + def decode( # type: ignore[override] + self, s: str, flat: bool = True + ) -> dict[str, str] | dict[str, list[str]]: qs_re = r"(?:([\w\-\%\+\.\|]+\=[\w\-\%\+\.\|]*)+(?:[\&]{1})?)+" qs_pattern = re.compile(qs_re) if qs_pattern.match(s): data = parse_qs(s) if flat: - data = {key: value[0] for key, value in data.items()} + return {key: value[0] for key, value in data.items()} return data raise ValueError(f"Invalid query string: {s}") - def encode(self, d, **kwargs): + @override + def encode( + self, d: Mapping[str, str] | Mapping[str, list[str]], **kwargs: Any + ) -> str: data = urlencode(d, **kwargs) return data diff --git a/benedict/serializers/toml.py b/benedict/serializers/toml.py index 2622374d..b7346234 100644 --- a/benedict/serializers/toml.py +++ b/benedict/serializers/toml.py @@ -13,23 +13,25 @@ except ImportError: tomllib_available = False +from typing import Any + from benedict.extras import require_toml from benedict.serializers.abstract import AbstractSerializer -class TOMLSerializer(AbstractSerializer): +class TOMLSerializer(AbstractSerializer[str, Any]): """ This class describes a toml serializer. """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "toml", ], ) - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> Any: if tomllib_available: data = tomllib.loads(s, **kwargs) else: @@ -37,7 +39,7 @@ def decode(self, s, **kwargs): data = toml.loads(s, **kwargs) return data - def encode(self, d, **kwargs): + def encode(self, d: Any, **kwargs: Any) -> str: require_toml(installed=toml_installed) data = toml.dumps(dict(d), **kwargs) return data diff --git a/benedict/serializers/xls.py b/benedict/serializers/xls.py index bb529996..15fe1618 100644 --- a/benedict/serializers/xls.py +++ b/benedict/serializers/xls.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import fsutil try: @@ -8,18 +10,21 @@ except ModuleNotFoundError: xls_installed = False +from collections.abc import Sequence +from typing import Any, NoReturn + from slugify import slugify from benedict.extras import require_xls from benedict.serializers.abstract import AbstractSerializer -class XLSSerializer(AbstractSerializer): +class XLSSerializer(AbstractSerializer[str, list[dict[str, Any]]]): """ This class describes a xls serializer. """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "xls", @@ -28,7 +33,7 @@ def __init__(self): ], ) - def _get_sheet_index_and_name_from_options(self, **kwargs): + def _get_sheet_index_and_name_from_options(self, **kwargs: Any) -> tuple[int, str]: sheet_index_or_name = kwargs.pop("sheet", 0) sheet_index = 0 sheet_name = "" @@ -38,7 +43,9 @@ def _get_sheet_index_and_name_from_options(self, **kwargs): sheet_name = sheet_index_or_name return (sheet_index, sheet_name) - def _get_sheet_index_by_name(self, sheet_name, sheet_names): + def _get_sheet_index_by_name( + self, sheet_name: str, sheet_names: Sequence[str] + ) -> int: sheet_names = [slugify(name) for name in sheet_names] try: sheet_index = sheet_names.index(slugify(sheet_name)) @@ -48,15 +55,15 @@ def _get_sheet_index_by_name(self, sheet_name, sheet_names): f"Invalid sheet name {sheet_name!r}, sheet not found." ) from error - def _get_sheet_columns_indexes(self, columns_count): + def _get_sheet_columns_indexes(self, columns_count: int) -> list[int]: return list(range(columns_count)) - def _decode_legacy(self, s, **kwargs): + def _decode_legacy(self, s: str, **kwargs: Any) -> list[dict[str, Any]]: options = {} options["filename"] = s options["logfile"] = kwargs.pop("logfile", None) - options["verbosity"] = kwargs.pop("verbosity", 0) or 0 - options["use_mmap"] = kwargs.pop("use_mmap", False) or False + options["verbosity"] = kwargs.pop("verbosity", 0) + options["use_mmap"] = kwargs.pop("use_mmap", False) options["file_contents"] = kwargs.pop("file_contents", None) # load the worksheet @@ -84,7 +91,7 @@ def _decode_legacy(self, s, **kwargs): else: # otherwise use columns indexes as column names # for row in sheet.iter_rows(min_row=1, max_row=1): - columns = self._get_sheet_columns_indexes(sheet_columns_range) + columns = list(sheet_columns_range) # standardize column names, eg. "Date Created" -> "date_created" if columns_standardized: @@ -104,8 +111,8 @@ def _decode_legacy(self, s, **kwargs): # print(items) return items - def _decode(self, s, **kwargs): - options = {} + def _decode(self, s: str, **kwargs: Any) -> list[dict[str, Any]]: + options: dict[str, Any] = {} options["filename"] = s options["read_only"] = True options["data_only"] = kwargs.pop("data_only", False) @@ -155,14 +162,15 @@ def _decode(self, s, **kwargs): # print(items) return items - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> list[dict[str, Any]]: require_xls(installed=xls_installed) extension = fsutil.get_file_extension(s) if extension in ["xlsx", "xlsm"]: return self._decode(s, **kwargs) elif extension in ["xls", "xlt"]: return self._decode_legacy(s, **kwargs) + raise ValueError(extension) - def encode(self, d, **kwargs): + def encode(self, d: list[dict[str, Any]], **kwargs: Any) -> NoReturn: # require_xls(installed=xls_installed) raise NotImplementedError diff --git a/benedict/serializers/xml.py b/benedict/serializers/xml.py index 9adcff37..54dc83f0 100644 --- a/benedict/serializers/xml.py +++ b/benedict/serializers/xml.py @@ -1,3 +1,5 @@ +from __future__ import annotations + try: import xmltodict @@ -6,29 +8,31 @@ xml_installed = False +from typing import Any, cast + from benedict.extras import require_xml from benedict.serializers.abstract import AbstractSerializer -class XMLSerializer(AbstractSerializer): +class XMLSerializer(AbstractSerializer[str, dict[str, Any]]): """ This class describes a xml serializer. """ - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "xml", ], ) - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> dict[str, Any]: require_xml(installed=xml_installed) kwargs.setdefault("dict_constructor", dict) data = xmltodict.parse(s, **kwargs) return data - def encode(self, d, **kwargs): + def encode(self, d: dict[str, Any], **kwargs: Any) -> str: require_xml(installed=xml_installed) data = xmltodict.unparse(d, **kwargs) - return data + return cast("str", data) diff --git a/benedict/serializers/yaml.py b/benedict/serializers/yaml.py index c3c3e2e5..d1e1cb73 100644 --- a/benedict/serializers/yaml.py +++ b/benedict/serializers/yaml.py @@ -10,23 +10,25 @@ yaml_installed = False +from typing import Any, cast + from benedict.extras import require_yaml from benedict.serializers.abstract import AbstractSerializer from benedict.serializers.json import JSONSerializer -class YAMLSerializer(AbstractSerializer): +class YAMLSerializer(AbstractSerializer[str, Any]): """ This class describes an yaml serializer. """ @staticmethod - def represent_dict_for_class(cls): + def represent_dict_for_class(cls: Any) -> None: if yaml_installed: # fix benedict yaml representer - #43 - SafeDumper.yaml_representers[cls] = SafeRepresenter.represent_dict + SafeDumper.yaml_representers[cls] = SafeRepresenter.represent_dict # type: ignore[assignment] - def __init__(self): + def __init__(self) -> None: super().__init__( extensions=[ "yaml", @@ -35,13 +37,13 @@ def __init__(self): ) self._json_serializer = JSONSerializer() - def decode(self, s, **kwargs): + def decode(self, s: str, **kwargs: Any) -> Any: require_yaml(installed=yaml_installed) data = yaml.safe_load(s, **kwargs) return data - def encode(self, d, **kwargs): + def encode(self, d: Any, **kwargs: Any) -> str: require_yaml(installed=yaml_installed) d = self._json_serializer.decode(self._json_serializer.encode(d)) data = yaml.dump(d, **kwargs) - return data + return cast("str", data) diff --git a/benedict/utils/type_util.py b/benedict/utils/type_util.py index 476d66e5..5d76af73 100644 --- a/benedict/utils/type_util.py +++ b/benedict/utils/type_util.py @@ -1,7 +1,13 @@ +from __future__ import annotations + import pathlib import re +from collections.abc import Callable, Collection, Sequence from datetime import datetime from decimal import Decimal +from typing import Any + +from typing_extensions import TypeIs regex = re.compile("").__class__ uuid_re = re.compile( @@ -10,86 +16,94 @@ ) -def is_bool(val): +def is_bool(val: Any) -> TypeIs[bool]: return isinstance(val, bool) -def is_collection(val): +def is_collection(val: Any) -> TypeIs[Collection[Any]]: return isinstance(val, (dict, list, set, tuple)) -def is_datetime(val): +def is_datetime(val: Any) -> TypeIs[datetime]: return isinstance(val, datetime) -def is_decimal(val): +def is_decimal(val: Any) -> TypeIs[Decimal]: return isinstance(val, Decimal) -def is_dict(val): +def is_dict(val: Any) -> TypeIs[dict[Any, Any]]: return isinstance(val, dict) -def is_dict_or_list(val): +def is_dict_or_list(val: Any) -> TypeIs[dict[Any, Any] | list[Any]]: return isinstance(val, (dict, list)) -def is_dict_or_list_or_tuple(val): +def is_dict_or_list_or_tuple( + val: Any, +) -> TypeIs[dict[Any, Any] | list[Any] | tuple[Any]]: return isinstance(val, (dict, list, tuple)) -def is_float(val): +def is_float(val: Any) -> TypeIs[float]: return isinstance(val, float) -def is_function(val): +def is_function(val: Any) -> TypeIs[Callable[..., Any]]: return callable(val) -def is_integer(val): +def is_integer(val: Any) -> TypeIs[int]: return isinstance(val, int) -def is_json_serializable(val): +def is_json_serializable( + val: Any, +) -> TypeIs[bool | dict[Any, Any] | float | int | list[Any] | str | tuple[Any] | None]: json_types = (type(None), bool, dict, float, int, list, str, tuple) return isinstance(val, json_types) -def is_list(val): +def is_list(val: Any) -> TypeIs[list[Any]]: return isinstance(val, list) -def is_list_or_tuple(val): +def is_sequence(val: Any) -> TypeIs[Sequence[Any]]: + return isinstance(val, Sequence) + + +def is_list_or_tuple(val: Any) -> TypeIs[list[Any] | tuple[Any]]: return isinstance(val, (list, tuple)) -def is_none(val): +def is_none(val: Any) -> TypeIs[None]: return val is None -def is_not_none(val): +def is_not_none(val: Any) -> bool: return val is not None -def is_path(val): +def is_path(val: Any) -> TypeIs[pathlib.Path]: return isinstance(val, pathlib.Path) -def is_regex(val): +def is_regex(val: Any) -> TypeIs[re.Pattern[Any]]: return isinstance(val, regex) -def is_set(val): +def is_set(val: Any) -> TypeIs[set[Any]]: return isinstance(val, set) -def is_string(val): +def is_string(val: Any) -> TypeIs[str]: return isinstance(val, str) -def is_tuple(val): +def is_tuple(val: Any) -> TypeIs[tuple[Any]]: return isinstance(val, tuple) -def is_uuid(val): - return is_string(val) and uuid_re.match(val) +def is_uuid(val: Any) -> bool: + return bool(is_string(val) and uuid_re.match(val)) diff --git a/pyproject.toml b/pyproject.toml index 6500b08e..027cc9ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,11 +86,14 @@ classifiers = [ "Topic :: System :: Filesystems", "Topic :: Text Processing :: Markup :: XML", "Topic :: Utilities", + "Typing :: Typed", ] dependencies = [ "python-fsutil >= 0.9.3, < 1.0.0", "python-slugify >= 7.0.0, < 9.0.0", "requests >= 2.26.0, < 3.0.0", + "typing_extensions >= 4.13.2, < 4.14.0", + "useful-types >= 0.2.1, < 0.3.0" ] dynamic = ["version"] maintainers = [ @@ -165,6 +168,29 @@ exclude = ''' )/ ''' +[tool.mypy] +cache_dir = ".mypy_cache" +check_untyped_defs = true +# disable import-related error codes: +# - import-untyped: third-party libraries like xlrd, MailChecker lack type stubs +# - import-not-found: tomllib is Python 3.11+ but we support 3.10+ +disable_error_code = "import-untyped,import-not-found" +disallow_any_generics = true +disallow_subclassing_any = true +explicit_package_bases = true +incremental = true +platform = "linux" +python_version = "3.10" +show_column_numbers = true +show_error_codes = true +strict = true +strict_optional = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true +warn_unused_ignores = true + [tool.ruff] line-length = 88 diff --git a/requirements-test.txt b/requirements-test.txt index 711aed76..d15832ad 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,5 +1,17 @@ coverage == 7.10.* +mypy == 1.16.* orjson == 3.11.* pre-commit == 4.3.* python-decouple == 3.8 tox == 4.30.* + +# type stubs for mypy +boto3-stubs[essential,lambda]==1.37.20 +decouple-types==1.0.2 +types-PyYAML==6.0.12.20250402 +types-beautifulsoup4==4.12.0.20250204 +types-html5lib==1.1.11.20241018 +types-openpyxl==3.1.5.20250306 +types-python-dateutil==2.9.0.20241206 +types-toml==0.10.8.20240310 +types-xmltodict==0.14.0.20241009 diff --git a/requirements.txt b/requirements.txt index d92ed95f..d6c33947 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,5 +10,7 @@ python-slugify == 8.0.4 pyyaml == 6.0.2 requests == 2.32.5 toml == 0.10.2 +typing_extensions >= 4.14.1 +useful-types == 0.2.1 xlrd == 2.0.2 xmltodict == 0.15.1 diff --git a/tests/core/test_clean.py b/tests/core/test_clean.py index e2dd6faf..822d7402 100644 --- a/tests/core/test_clean.py +++ b/tests/core/test_clean.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict.core import clean as _clean @@ -8,7 +11,7 @@ class clean_test_case(unittest.TestCase): This class describes a clean test case. """ - def test_clean(self): + def test_clean(self) -> None: i = { "a": {}, "b": {"x": 1}, @@ -61,9 +64,9 @@ def test_clean(self): } self.assertEqual(o, r) - def test_clean_nested_dicts(self): + def test_clean_nested_dicts(self) -> None: # https://github.com/fabiocaccamo/python-benedict/issues/383 - d = { + d: dict[str, Any] = { "a": { "b": { "c": {}, @@ -71,7 +74,7 @@ def test_clean_nested_dicts(self): }, } _clean(d, collections=True) - r = {} + r: dict[str, Any] = {} self.assertEqual(d, r) d = { diff --git a/tests/core/test_clone.py b/tests/core/test_clone.py index 0fca2d29..d8e4e007 100644 --- a/tests/core/test_clone.py +++ b/tests/core/test_clone.py @@ -8,7 +8,7 @@ class clone_test_case(unittest.TestCase): This class describes a clone test case. """ - def test_clone(self): + def test_clone(self) -> None: i = { "a": { "b": { @@ -24,7 +24,7 @@ def test_clone(self): self.assertEqual(i["a"]["b"]["c"], 1) self.assertEqual(o["a"]["b"]["c"], 2) - def test_clone_empty(self): + def test_clone_empty(self) -> None: i = { "a": { "b": { diff --git a/tests/core/test_dump.py b/tests/core/test_dump.py index 9662745c..bc2bae8b 100644 --- a/tests/core/test_dump.py +++ b/tests/core/test_dump.py @@ -1,5 +1,8 @@ +from __future__ import annotations + import datetime as dt import unittest +from typing import Any from benedict.core import dump as _dump @@ -10,10 +13,10 @@ class dump_test_case(unittest.TestCase): """ @staticmethod - def _rstrip_lines(s): + def _rstrip_lines(s: str) -> str: return "\n".join([line.rstrip() for line in s.splitlines()]) - def test_dump(self): + def test_dump(self) -> None: d = { "a": { "b": { @@ -31,7 +34,7 @@ def test_dump(self): o = _dump(d) self.assertEqual(self._rstrip_lines(o), r) - def test_dump_with_datetime(self): + def test_dump_with_datetime(self) -> None: d = { "datetime": dt.datetime(2019, 6, 11), } @@ -41,7 +44,7 @@ def test_dump_with_datetime(self): o = _dump(d) self.assertEqual(self._rstrip_lines(o), r) - def test_dump_with_set(self): + def test_dump_with_set(self) -> None: d = { "set": {0, 1, 2, 3, 4, 5}, } @@ -58,8 +61,8 @@ def test_dump_with_set(self): o = _dump(d) self.assertEqual(self._rstrip_lines(o), r) - def test_dump_with_unsortable_keys(self): - d = { + def test_dump_with_unsortable_keys(self) -> None: + d: dict[Any, Any] = { None: None, 0: 0, 1: 1, diff --git a/tests/core/test_filter.py b/tests/core/test_filter.py index c6430d29..add79598 100644 --- a/tests/core/test_filter.py +++ b/tests/core/test_filter.py @@ -8,7 +8,7 @@ class filter_test_case(unittest.TestCase): This class describes a filter test case. """ - def test_filter(self): + def test_filter(self) -> None: i = { "a": 1, "b": 2, @@ -18,7 +18,7 @@ def test_filter(self): "g": 7, } with self.assertRaises(ValueError): - _filter(i, True) + _filter(i, True) # type: ignore[arg-type] o = _filter(i, lambda key, val: isinstance(val, int)) r = { "a": 1, diff --git a/tests/core/test_find.py b/tests/core/test_find.py index ccddcda2..5f1f7cbe 100644 --- a/tests/core/test_find.py +++ b/tests/core/test_find.py @@ -8,7 +8,7 @@ class find_test_case(unittest.TestCase): This class describes a find test case. """ - def test_find_with_single_result(self): + def test_find_with_single_result(self) -> None: i = { "a": 1, "b": 2, @@ -18,7 +18,7 @@ def test_find_with_single_result(self): o = _find(i, ["x", "y", "b", "z"], 5) self.assertEqual(o, 2) - def test_find_with_multiple_results(self): + def test_find_with_multiple_results(self) -> None: i = { "a": 1, "b": 2, @@ -28,7 +28,7 @@ def test_find_with_multiple_results(self): o = _find(i, ["a", "x", "b", "y"]) self.assertEqual(o, 1) - def test_find_with_no_result(self): + def test_find_with_no_result(self) -> None: i = { "a": 1, "b": 2, @@ -38,7 +38,7 @@ def test_find_with_no_result(self): o = _find(i, ["x", "y", "z"]) self.assertEqual(o, None) - def test_find_with_no_result_and_default(self): + def test_find_with_no_result_and_default(self) -> None: i = { "a": 1, "b": 2, diff --git a/tests/core/test_flatten.py b/tests/core/test_flatten.py index 07c6ce32..2c063d1f 100644 --- a/tests/core/test_flatten.py +++ b/tests/core/test_flatten.py @@ -8,7 +8,7 @@ class flatten_test_case(unittest.TestCase): This class describes a flatten test case. """ - def test_flatten(self): + def test_flatten(self) -> None: i = { "a": 1, "b": 2, @@ -32,7 +32,7 @@ def test_flatten(self): } self.assertEqual(o, r) - def test_flatten_with_custom_separator(self): + def test_flatten_with_custom_separator(self) -> None: i = { "a": 1, "b": 2, @@ -56,7 +56,7 @@ def test_flatten_with_custom_separator(self): } self.assertEqual(o, r) - def test_flatten_with_key_conflict(self): + def test_flatten_with_key_conflict(self) -> None: i = { "a": 1, "b": 2, diff --git a/tests/core/test_groupby.py b/tests/core/test_groupby.py index ac00c2a8..1aa6d219 100644 --- a/tests/core/test_groupby.py +++ b/tests/core/test_groupby.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict.core import clone as _clone from benedict.core import groupby as _groupby @@ -9,8 +12,8 @@ class groupby_test_case(unittest.TestCase): This class describes a groupby test case. """ - def test_groupby(self): - ls = [ + def test_groupby(self) -> None: + ls: list[dict[str, Any]] = [ {"id": 1, "name": "John"}, {"id": 2, "name": "Frank"}, {"id": 3, "name": "Tony"}, @@ -51,13 +54,13 @@ def test_groupby(self): self.assertEqual(len(d[3]), 3) self.assertEqual(len(d[4]), 2) - def test_groupby_with_wrong_input(self): - ls = {"id": 1, "name": "John"} + def test_groupby_with_wrong_input(self) -> None: + ls: list[list[dict[str, Any]]] | dict[str, Any] = {"id": 1, "name": "John"} with self.assertRaises(ValueError): - _ = _groupby(ls, "id") + _ = _groupby(ls, "id") # type: ignore[arg-type] ls = [ [{"id": 1, "name": "John"}], [{"id": 2, "name": "Frank"}], ] with self.assertRaises(ValueError): - _ = _groupby(ls, "id") + _ = _groupby(ls, "id") # type: ignore[arg-type] diff --git a/tests/core/test_invert.py b/tests/core/test_invert.py index bc9302cd..4c1becd4 100644 --- a/tests/core/test_invert.py +++ b/tests/core/test_invert.py @@ -8,7 +8,7 @@ class invert_test_case(unittest.TestCase): This class describes an invert test case. """ - def test_invert_with_unique_values(self): + def test_invert_with_unique_values(self) -> None: i = { "a": 1, "b": 2, @@ -26,7 +26,7 @@ def test_invert_with_unique_values(self): } self.assertEqual(o, r) - def test_invert_with_flat_unique_values(self): + def test_invert_with_flat_unique_values(self) -> None: i = { "a": 1, "b": 2, @@ -44,7 +44,7 @@ def test_invert_with_flat_unique_values(self): } self.assertEqual(o, r) - def test_invert_with_multiple_values(self): + def test_invert_with_multiple_values(self) -> None: i = { "a": 1, "b": 2, @@ -58,7 +58,7 @@ def test_invert_with_multiple_values(self): self.assertTrue("b" and "e" in o[2]) self.assertTrue("c" and "f" in o[3]) - def test_invert_with_list_values(self): + def test_invert_with_list_values(self) -> None: i = { "a": [ "x", @@ -95,7 +95,7 @@ def test_invert_with_list_values(self): self.assertTrue("e" in ii["b"]) self.assertEqual(len(ii["b"]), 3) - def test_invert_with_tuple_values(self): + def test_invert_with_tuple_values(self) -> None: i = { "a": ( "x", diff --git a/tests/core/test_items_sorted.py b/tests/core/test_items_sorted.py index 5baa1ae1..253c9715 100644 --- a/tests/core/test_items_sorted.py +++ b/tests/core/test_items_sorted.py @@ -9,7 +9,7 @@ class items_sorted_test_case(unittest.TestCase): This class describes an items sorted test case. """ - def test_items_sorted_by_keys(self): + def test_items_sorted_by_keys(self) -> None: i = { "y": 3, "a": 6, @@ -17,7 +17,7 @@ def test_items_sorted_by_keys(self): "z": 4, "x": 1, } - o = _items_sorted_by_keys(i) + o = _items_sorted_by_keys(i) # type: ignore[misc] r = [ ("a", 6), ("f", 9), @@ -27,7 +27,7 @@ def test_items_sorted_by_keys(self): ] self.assertEqual(o, r) - def test_items_sorted_by_keys_reverse(self): + def test_items_sorted_by_keys_reverse(self) -> None: i = { "y": 3, "a": 6, @@ -35,7 +35,7 @@ def test_items_sorted_by_keys_reverse(self): "z": 4, "x": 1, } - o = _items_sorted_by_keys(i, reverse=True) + o = _items_sorted_by_keys(i, reverse=True) # type: ignore[misc] r = [ ("z", 4), ("y", 3), @@ -45,7 +45,7 @@ def test_items_sorted_by_keys_reverse(self): ] self.assertEqual(o, r) - def test_items_sorted_by_values(self): + def test_items_sorted_by_values(self) -> None: i = { "a": 3, "b": 6, @@ -53,7 +53,7 @@ def test_items_sorted_by_values(self): "e": 4, "d": 1, } - o = _items_sorted_by_values(i) + o = _items_sorted_by_values(i) # type: ignore[misc] r = [ ("d", 1), ("a", 3), @@ -63,7 +63,7 @@ def test_items_sorted_by_values(self): ] self.assertEqual(o, r) - def test_items_sorted_by_values_reverse(self): + def test_items_sorted_by_values_reverse(self) -> None: i = { "a": 3, "b": 6, @@ -71,7 +71,7 @@ def test_items_sorted_by_values_reverse(self): "e": 4, "d": 1, } - o = _items_sorted_by_values(i, reverse=True) + o = _items_sorted_by_values(i, reverse=True) # type: ignore[misc] r = [ ("c", 9), ("b", 6), diff --git a/tests/core/test_keylists.py b/tests/core/test_keylists.py index 09870f8e..2240c097 100644 --- a/tests/core/test_keylists.py +++ b/tests/core/test_keylists.py @@ -8,7 +8,7 @@ class keylists_test_case(unittest.TestCase): This class describes a keylists test case. """ - def test_keylists(self): + def test_keylists(self) -> None: i = { "a": 1, "b": { @@ -36,7 +36,7 @@ def test_keylists(self): for k in r: self.assertTrue(k in o) - def test_keylists_with_non_string_keys(self): + def test_keylists_with_non_string_keys(self) -> None: i = { True: { True: 1, @@ -60,7 +60,7 @@ def test_keylists_with_non_string_keys(self): for k in r: self.assertTrue(k in o) - def test_keylists_with_lists_and_indexes_included(self): + def test_keylists_with_lists_and_indexes_included(self) -> None: i = { "a": 1, "b": { @@ -127,7 +127,7 @@ def test_keylists_with_lists_and_indexes_included(self): ] self.assertEqual(o, r) - def test_keylists_with_nested_lists_and_indexes_included(self): + def test_keylists_with_nested_lists_and_indexes_included(self) -> None: i = { "a": { "b": [ @@ -161,7 +161,7 @@ def test_keylists_with_nested_lists_and_indexes_included(self): ] self.assertEqual(o, r) - def test_keylists_with_lists_and_indexes_not_included(self): + def test_keylists_with_lists_and_indexes_not_included(self) -> None: i = { "a": 1, "b": { diff --git a/tests/core/test_keypaths.py b/tests/core/test_keypaths.py index 968d5063..5d4faa6f 100644 --- a/tests/core/test_keypaths.py +++ b/tests/core/test_keypaths.py @@ -8,7 +8,7 @@ class keypaths_test_case(unittest.TestCase): This class describes a keypaths test case. """ - def test_keypaths(self): + def test_keypaths(self) -> None: i = { "a": 1, "b": { @@ -35,7 +35,7 @@ def test_keypaths(self): ] self.assertEqual(o, r) - def test_keypaths_unsorted(self): + def test_keypaths_unsorted(self) -> None: i = { "b": { "c": { @@ -62,7 +62,7 @@ def test_keypaths_unsorted(self): ] self.assertEqual(o, r) - def test_keypaths_with_custom_separator(self): + def test_keypaths_with_custom_separator(self) -> None: i = { "a": 1, "b": { @@ -89,7 +89,7 @@ def test_keypaths_with_custom_separator(self): ] self.assertEqual(o, r) - def test_keypaths_with_invalid_separator(self): + def test_keypaths_with_invalid_separator(self) -> None: i = { "a": 1, "b": { @@ -104,9 +104,9 @@ def test_keypaths_with_invalid_separator(self): }, } with self.assertRaises(ValueError): - _ = _keypaths(i, separator=True) + _ = _keypaths(i, separator=True) # type: ignore[arg-type] - def test_keypaths_without_separator(self): + def test_keypaths_without_separator(self) -> None: i = { "a": 1, "b": { @@ -135,7 +135,7 @@ def test_keypaths_without_separator(self): ] self.assertEqual(o, r) - def test_keypaths_with_non_string_keys(self): + def test_keypaths_with_non_string_keys(self) -> None: i = { True: { True: 1, @@ -158,7 +158,7 @@ def test_keypaths_with_non_string_keys(self): ] self.assertEqual(o, r) - def test_keypaths_with_lists_and_indexes_included(self): + def test_keypaths_with_lists_and_indexes_included(self) -> None: i = { "a": 1, "b": { @@ -224,7 +224,7 @@ def test_keypaths_with_lists_and_indexes_included(self): ] self.assertEqual(o, r) - def test_keypaths_with_lists_and_indexes_not_included(self): + def test_keypaths_with_lists_and_indexes_not_included(self) -> None: i = { "a": 1, "b": { @@ -269,7 +269,7 @@ def test_keypaths_with_lists_and_indexes_not_included(self): ] self.assertEqual(o, r) - def test_keypaths_with_nested_lists_and_indexes_included(self): + def test_keypaths_with_nested_lists_and_indexes_included(self) -> None: i = { "a": { "b": [ diff --git a/tests/core/test_match.py b/tests/core/test_match.py index 81243ea6..4147c05c 100644 --- a/tests/core/test_match.py +++ b/tests/core/test_match.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import re import unittest @@ -10,7 +12,7 @@ class match_test_case(unittest.TestCase): """ @staticmethod - def _get_dict(): + def _get_dict() -> dict[str, str]: return { "DOC_0001.pdf": "DOC_0001.pdf", "IMG_0001.jpg": "IMG_0001.jpg", @@ -29,7 +31,7 @@ def _get_dict(): "IMG_0005.raw": "IMG_0005.raw", } - def test_match_with_string_pattern(self): + def test_match_with_string_pattern(self) -> None: d = self._get_dict() values = _match(d, "IMG_*.jpg") values.sort() @@ -42,7 +44,7 @@ def test_match_with_string_pattern(self): ] self.assertEqual(values, expected_values) - def test_match_with_regex_pattern(self): + def test_match_with_regex_pattern(self) -> None: d = self._get_dict() values = _match(d, re.compile(r"^DOC\_")) values.sort() @@ -55,7 +57,7 @@ def test_match_with_regex_pattern(self): ] self.assertEqual(values, expected_values) - def test_match_with_invalid_pattern(self): + def test_match_with_invalid_pattern(self) -> None: d = self._get_dict() with self.assertRaises(ValueError): - _ = _match(d, 100) + _ = _match(d, 100) # type: ignore[arg-type] diff --git a/tests/core/test_merge.py b/tests/core/test_merge.py index f2d000ac..0936c94f 100644 --- a/tests/core/test_merge.py +++ b/tests/core/test_merge.py @@ -8,7 +8,7 @@ class merge_test_case(unittest.TestCase): This class describes a merge test case. """ - def test_merge_with_flatten_dict(self): + def test_merge_with_flatten_dict(self) -> None: d = { "a": 1, "b": 1, @@ -25,7 +25,7 @@ def test_merge_with_flatten_dict(self): } self.assertEqual(d, r) - def test_merge_with_lists(self): + def test_merge_with_lists(self) -> None: d = { "a": [0, 1, 2], "b": [5, 6, 7], @@ -46,7 +46,7 @@ def test_merge_with_lists(self): } self.assertEqual(d, r) - def test_merge_with_lists_and_concat(self): + def test_merge_with_lists_and_concat(self) -> None: d = { "a": [0, 1, 2], "b": [5, 6, 7], @@ -67,7 +67,7 @@ def test_merge_with_lists_and_concat(self): } self.assertEqual(d, r) - def test_merge_with_multiple_dicts(self): + def test_merge_with_multiple_dicts(self) -> None: d = { "a": 1, "b": 1, @@ -96,7 +96,7 @@ def test_merge_with_multiple_dicts(self): } self.assertEqual(d, r) - def test_merge_with_nested_dict(self): + def test_merge_with_nested_dict(self) -> None: d = { "a": 1, "b": { @@ -160,7 +160,7 @@ def test_merge_with_nested_dict(self): } self.assertEqual(d, r) - def test_merge_without_overwrite(self): + def test_merge_without_overwrite(self) -> None: d = { "a": 1, "b": { diff --git a/tests/core/test_move.py b/tests/core/test_move.py index 0ebef9df..3a7d25bf 100644 --- a/tests/core/test_move.py +++ b/tests/core/test_move.py @@ -8,7 +8,7 @@ class move_test_case(unittest.TestCase): This class describes a move test case. """ - def test_move(self): + def test_move(self) -> None: d = { "a": { "x": 1, @@ -40,7 +40,7 @@ def test_move(self): } self.assertEqual(d, r) - def test_move_with_same_key(self): + def test_move_with_same_key(self) -> None: d = { "a": 1, "b": 2, diff --git a/tests/core/test_nest.py b/tests/core/test_nest.py index 1ccbd4ef..af5b57af 100644 --- a/tests/core/test_nest.py +++ b/tests/core/test_nest.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict.core import clone as _clone from benedict.core import nest as _nest @@ -9,8 +12,8 @@ class nest_test_case(unittest.TestCase): This class describes a nest test case. """ - def test_nest(self): - ls = [ + def test_nest(self) -> None: + ls: list[dict[str, Any]] = [ {"id": 1, "parent_id": None, "name": "John"}, {"id": 2, "parent_id": 1, "name": "Frank"}, {"id": 3, "parent_id": 2, "name": "Tony"}, @@ -85,8 +88,8 @@ def test_nest(self): self.assertEqual(ls, ls_clone) self.assertEqual(n, r) - def test_nest_with_wrong_keys(self): - ls = [ + def test_nest_with_wrong_keys(self) -> None: + ls: list[dict[str, Any]] = [ {"id": 1, "parent_id": None, "name": "John"}, {"id": 2, "parent_id": 1, "name": "Frank"}, {"id": 3, "parent_id": 2, "name": "Tony"}, @@ -104,13 +107,13 @@ def test_nest_with_wrong_keys(self): with self.assertRaises(ValueError): _ = _nest(ls, "id", "parent_id", "parent_id") - def test_nest_with_wrong_input(self): + def test_nest_with_wrong_input(self) -> None: ls = {"id": 1, "parent_id": None, "name": "John"} with self.assertRaises(ValueError): - _ = _nest(ls, "id", "parent_id", "children") - ls = [ + _ = _nest(ls, "id", "parent_id", "children") # type: ignore[arg-type] + ls2 = [ [{"id": 1, "parent_id": None, "name": "John"}], [{"id": 2, "parent_id": 1, "name": "Frank"}], ] with self.assertRaises(ValueError): - _ = _nest(ls, "id", "parent_id", "children") + _ = _nest(ls2, "id", "parent_id", "children") # type: ignore[arg-type] diff --git a/tests/core/test_remove.py b/tests/core/test_remove.py index 9a163008..c3a0df12 100644 --- a/tests/core/test_remove.py +++ b/tests/core/test_remove.py @@ -8,7 +8,7 @@ class remove_test_case(unittest.TestCase): This class describes a remove test case. """ - def test_remove_with_single_key(self): + def test_remove_with_single_key(self) -> None: d = { "a": 1, "b": 2, @@ -21,7 +21,7 @@ def test_remove_with_single_key(self): } self.assertEqual(d, r) - def test_remove_with_multiple_keys_as_args(self): + def test_remove_with_multiple_keys_as_args(self) -> None: d = { "a": 1, "b": 2, @@ -36,7 +36,7 @@ def test_remove_with_multiple_keys_as_args(self): } self.assertEqual(d, r) - def test_remove_with_multiple_keys_as_list(self): + def test_remove_with_multiple_keys_as_list(self) -> None: d = { "a": 1, "b": 2, diff --git a/tests/core/test_rename.py b/tests/core/test_rename.py index 1ba38adf..a87d0bd8 100644 --- a/tests/core/test_rename.py +++ b/tests/core/test_rename.py @@ -8,7 +8,7 @@ class rename_test_case(unittest.TestCase): This class describes a rename test case. """ - def test_rename(self): + def test_rename(self) -> None: d = { "a": { "x": 1, @@ -40,7 +40,7 @@ def test_rename(self): } self.assertEqual(d, r) - def test_rename_with_same_key(self): + def test_rename_with_same_key(self) -> None: d = { "a": 1, "b": 2, @@ -52,7 +52,7 @@ def test_rename_with_same_key(self): } self.assertEqual(d, r) - def test_rename_to_existing_name(self): + def test_rename_to_existing_name(self) -> None: d = { "a": 1, "b": 2, diff --git a/tests/core/test_search.py b/tests/core/test_search.py index 55feefe0..e46ce326 100644 --- a/tests/core/test_search.py +++ b/tests/core/test_search.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict.core import search as _search @@ -8,8 +11,8 @@ class search_test_case(unittest.TestCase): This class describes a search test case. """ - def test_search_string(self): - d = { + def test_search_string(self) -> None: + d: dict[str, Any] = { "a": "Hello world", "b": "Hello world!", "c": { @@ -130,8 +133,8 @@ def test_search_string(self): in results ) - def test_search_int(self): - d = { + def test_search_int(self) -> None: + d: dict[str | int, Any] = { "u": 5, "v": { "x": { diff --git a/tests/core/test_standardize.py b/tests/core/test_standardize.py index d2a6fd79..5d169533 100644 --- a/tests/core/test_standardize.py +++ b/tests/core/test_standardize.py @@ -8,7 +8,7 @@ class standardize_test_case(unittest.TestCase): This class describes a standardize test case. """ - def test_standardize(self): + def test_standardize(self) -> None: d = { "CamelCase": 1, "CamelCamelCase": 1, diff --git a/tests/core/test_subset.py b/tests/core/test_subset.py index 3b599cec..9db004e1 100644 --- a/tests/core/test_subset.py +++ b/tests/core/test_subset.py @@ -8,7 +8,7 @@ class subset_test_case(unittest.TestCase): This class describes a subset test case. """ - def test_subset_with_keys_as_args(self): + def test_subset_with_keys_as_args(self) -> None: i = { "a": 1, "b": 2, @@ -26,7 +26,7 @@ def test_subset_with_keys_as_args(self): self.assertFalse(i is o) self.assertEqual(o, r) - def test_subset_with_keys_as_list(self): + def test_subset_with_keys_as_list(self) -> None: i = { "a": 1, "b": 2, diff --git a/tests/core/test_swap.py b/tests/core/test_swap.py index 3040bf22..f3ead53f 100644 --- a/tests/core/test_swap.py +++ b/tests/core/test_swap.py @@ -8,7 +8,7 @@ class swap_test_case(unittest.TestCase): This class describes a swap test case. """ - def test_swap(self): + def test_swap(self) -> None: d = { "a": 1, "b": 2, @@ -22,7 +22,7 @@ def test_swap(self): } self.assertEqual(d, r) - def test_swap_with_same_key(self): + def test_swap_with_same_key(self) -> None: d = { "a": 1, "b": 2, @@ -34,7 +34,7 @@ def test_swap_with_same_key(self): } self.assertEqual(d, r) - def test_swap_with_invalid_key(self): + def test_swap_with_invalid_key(self) -> None: d = { "a": 1, "b": 2, diff --git a/tests/core/test_traverse.py b/tests/core/test_traverse.py index 66473499..979ca372 100644 --- a/tests/core/test_traverse.py +++ b/tests/core/test_traverse.py @@ -1,4 +1,5 @@ import unittest +from typing import Any from benedict.core import clone as _clone from benedict.core import traverse as _traverse @@ -9,7 +10,7 @@ class traverse_test_case(unittest.TestCase): This class describes a traverse test case. """ - def test_traverse(self): + def test_traverse(self) -> None: i = { "a": { "x": 2, @@ -35,9 +36,9 @@ def test_traverse(self): } o = _clone(i) with self.assertRaises(ValueError): - _traverse(o, True) + _traverse(o, True) # type: ignore[arg-type] - def f(parent, key, value): + def f(parent: Any, key: Any, value: Any) -> None: if not isinstance(value, dict): parent[key] = value + 1 diff --git a/tests/core/test_unflatten.py b/tests/core/test_unflatten.py index 745bf13f..72585137 100644 --- a/tests/core/test_unflatten.py +++ b/tests/core/test_unflatten.py @@ -8,7 +8,7 @@ class unflatten_test_case(unittest.TestCase): This class describes an unflatten test case. """ - def test_unflatten(self): + def test_unflatten(self) -> None: d = { "a": 1, "b_c": 2, @@ -26,7 +26,7 @@ def test_unflatten(self): } self.assertEqual(u, r) - def test_unflatten_with_custom_separator(self): + def test_unflatten_with_custom_separator(self) -> None: d = { "a": 1, "b|c": 2, @@ -46,7 +46,7 @@ def test_unflatten_with_custom_separator(self): } self.assertEqual(u, r) - def test_unflatten_with_nested_dict(self): + def test_unflatten_with_nested_dict(self) -> None: d = { "a": 1, "b_c": { diff --git a/tests/core/test_unique.py b/tests/core/test_unique.py index 0587bbf0..570b70e8 100644 --- a/tests/core/test_unique.py +++ b/tests/core/test_unique.py @@ -8,7 +8,7 @@ class unique_test_case(unittest.TestCase): This class describes an unique test case. """ - def test_unique(self): + def test_unique(self) -> None: d = { "a": { "x": 1, diff --git a/tests/dicts/base/test_base_dict.py b/tests/dicts/base/test_base_dict.py index 1fdc64eb..0852df23 100644 --- a/tests/dicts/base/test_base_dict.py +++ b/tests/dicts/base/test_base_dict.py @@ -1,6 +1,9 @@ +from __future__ import annotations + import copy import unittest from collections.abc import Iterable +from typing import Any from benedict.dicts.base import BaseDict @@ -10,7 +13,7 @@ class base_dict_test_case(unittest.TestCase): This class describes a BaseDict test case. """ - def test__bool__(self): + def test__bool__(self) -> None: b = BaseDict() self.assertFalse(b) self.assertFalse(bool(b)) @@ -21,7 +24,7 @@ def test__bool__(self): self.assertTrue(bool(b)) self.assertEqual(b, b.dict()) - def test__bool__with_pointer(self): + def test__bool__with_pointer(self) -> None: d = {"a": 1} b = BaseDict(d) self.assertTrue(b) @@ -32,14 +35,14 @@ def test__bool__with_pointer(self): self.assertFalse(bool(b)) self.assertEqual(b, b.dict()) - def test__contains__(self): + def test__contains__(self) -> None: b = BaseDict() b["a"] = 1 self.assertTrue("a" in b) self.assertFalse("b" in b) self.assertEqual(b, b.dict()) - def test__contains__with_pointer(self): + def test__contains__with_pointer(self) -> None: d = {"a": 1} b = BaseDict(d) self.assertTrue("a" in b) @@ -48,7 +51,7 @@ def test__contains__with_pointer(self): del d["a"] self.assertFalse("a" in b) - def test__deepcopy__(self): + def test__deepcopy__(self) -> None: b1 = BaseDict() b1["a"] = {} b1["a"]["b"] = {} @@ -58,8 +61,8 @@ def test__deepcopy__(self): self.assertEqual(type(b1), type(b2)) self.assertFalse(b1 is b2) - def test__deepcopy__with_pointer(self): - d = {} + def test__deepcopy__with_pointer(self) -> None: + d: dict[str, Any] = {} d["a"] = {} d["a"]["b"] = {} d["a"]["b"]["c"] = True @@ -69,14 +72,14 @@ def test__deepcopy__with_pointer(self): self.assertEqual(type(b1), type(b2)) self.assertFalse(b1 is b2) - def test__delitem__(self): + def test__delitem__(self) -> None: b = BaseDict() with self.assertRaises(KeyError): del b["a"] self.assertEqual(b, b.dict()) - def test__delitem__with_pointer(self): - d = { + def test__delitem__with_pointer(self) -> None: + d: dict[str, Any] = { "a": 1, } b = BaseDict(d) @@ -87,9 +90,9 @@ def test__delitem__with_pointer(self): del b["a"] self.assertEqual(b, b.dict()) - def test__equal__(self): + def test__equal__(self) -> None: b = BaseDict() - o1 = {} + o1: dict[str, Any] = {} o2 = { "a": 2, } @@ -97,7 +100,7 @@ def test__equal__(self): self.assertFalse(b == o2) self.assertEqual(b, b.dict()) - def test__equal__with_pointer(self): + def test__equal__with_pointer(self) -> None: d = { "a": 1, } @@ -112,13 +115,13 @@ def test__equal__with_pointer(self): self.assertFalse(b == o2) self.assertEqual(b, b.dict()) - def test__getitem__(self): + def test__getitem__(self) -> None: b = BaseDict() with self.assertRaises(KeyError): b["a"] self.assertEqual(b, b.dict()) - def test__getitem__with_pointer(self): + def test__getitem__with_pointer(self) -> None: d = { "a": 1, } @@ -126,13 +129,13 @@ def test__getitem__with_pointer(self): self.assertEqual(b["a"], 1) self.assertEqual(b, b.dict()) - def test__iter__(self): + def test__iter__(self) -> None: b = BaseDict() i = iter(b) self.assertTrue(isinstance(i, Iterable)) self.assertEqual(b, b.dict()) - def test__iter__with_pointer(self): + def test__iter__with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -142,14 +145,14 @@ def test__iter__with_pointer(self): self.assertTrue(isinstance(i, Iterable)) self.assertEqual(b, b.dict()) - def test__len__(self): + def test__len__(self) -> None: b = BaseDict() self.assertEqual(len(b), 0) b["a"] = 1 self.assertEqual(len(b), 1) self.assertEqual(b, b.dict()) - def test__len__with_pointer(self): + def test__len__with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -163,13 +166,13 @@ def test__len__with_pointer(self): self.assertEqual(len(d), 4) self.assertEqual(b, b.dict()) - def test__repr__(self): - d = {} + def test__repr__(self) -> None: + d: dict[str, Any] = {} b = BaseDict() self.assertEqual(repr(d), repr(b)) self.assertEqual(b, b.dict()) - def test__repr__with_pointer(self): + def test__repr__with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -179,13 +182,13 @@ def test__repr__with_pointer(self): self.assertEqual(repr(d), repr(b)) self.assertEqual(b, b.dict()) - def test__setitem__(self): + def test__setitem__(self) -> None: b = BaseDict() b["a"] = 1 self.assertEqual(b["a"], 1) self.assertEqual(b, b.dict()) - def test__setitem__with_pointer(self): + def test__setitem__with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -197,7 +200,7 @@ def test__setitem__with_pointer(self): self.assertEqual(d["a"], 2) self.assertEqual(b, b.dict()) - def test__str__(self): + def test__str__(self) -> None: d = { "a": 1, "b": 2, @@ -213,7 +216,7 @@ def test__str__(self): self.assertEqual(str(d), str(b)) self.assertEqual(b, b.dict()) - def test__str__with_pointer(self): + def test__str__with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -223,7 +226,7 @@ def test__str__with_pointer(self): self.assertEqual(str(d), str(b)) self.assertEqual(b, b.dict()) - def test_clear(self): + def test_clear(self) -> None: d = { "a": 1, "b": 2, @@ -238,7 +241,7 @@ def test_clear(self): self.assertTrue(b == {}) self.assertTrue(d != {}) - def test_clear_with_pointer(self): + def test_clear_with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -251,7 +254,7 @@ def test_clear_with_pointer(self): self.assertTrue(b == d) self.assertEqual(b, b.dict()) - def test_copy(self): + def test_copy(self) -> None: b = BaseDict() b["a"] = 1 b["b"] = 2 @@ -264,7 +267,7 @@ def test_copy(self): # self.assertTrue(type(b) == type(c)) self.assertEqual(b, b.dict()) - def test_copy_with_pointer(self): + def test_copy_with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -280,7 +283,7 @@ def test_copy_with_pointer(self): # self.assertTrue(type(b) == type(c)) self.assertEqual(b, b.dict()) - def test_dict(self): + def test_dict(self) -> None: d = { "a": 1, "b": 2, @@ -291,7 +294,7 @@ def test_dict(self): self.assertTrue(b.dict() is d) self.assertEqual(b, b.dict()) - def test_dict_pointer(self): + def test_dict_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -310,7 +313,7 @@ def test_dict_pointer(self): self.assertEqual(d, b) self.assertEqual(b, b.dict()) - def test_get(self): + def test_get(self) -> None: b = BaseDict() b["a"] = 1 self.assertEqual(b.get("a"), 1) @@ -318,7 +321,7 @@ def test_get(self): self.assertEqual(b.get("b", 2), 2) self.assertEqual(b, b.dict()) - def test_get_with_pointer(self): + def test_get_with_pointer(self) -> None: d = { "a": 1, } @@ -330,7 +333,7 @@ def test_get_with_pointer(self): self.assertEqual(b.get("b", 2), 2) self.assertEqual(b, b.dict()) - def test_items(self): + def test_items(self) -> None: b = BaseDict() b["a"] = 1 b["b"] = 2 @@ -340,7 +343,7 @@ def test_items(self): self.assertTrue(i, [("a", 1), ("b", 2), ("c", 3)]) self.assertEqual(b, b.dict()) - def test_items_with_pointer(self): + def test_items_with_pointer(self) -> None: d = {"a": 1, "b": 2, "c": 3} b = BaseDict(d) i = list(b.items()) @@ -348,7 +351,7 @@ def test_items_with_pointer(self): self.assertTrue(i, [("a", 1), ("b", 2), ("c", 3)]) self.assertEqual(b, b.dict()) - def test_keys(self): + def test_keys(self) -> None: b = BaseDict() b["a"] = 1 b["b"] = 2 @@ -358,7 +361,7 @@ def test_keys(self): self.assertTrue(k, ["a", "b", "c"]) self.assertEqual(b, b.dict()) - def test_keys_with_pointer(self): + def test_keys_with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -370,7 +373,7 @@ def test_keys_with_pointer(self): self.assertTrue(k, ["a", "b", "c"]) self.assertEqual(b, b.dict()) - def test_pop(self): + def test_pop(self) -> None: b = BaseDict() b["a"] = 1 b["b"] = 2 @@ -383,7 +386,7 @@ def test_pop(self): self.assertEqual(v, 5) self.assertEqual(b, b.dict()) - def test_pop_with_pointer(self): + def test_pop_with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -406,7 +409,7 @@ def test_pop_with_pointer(self): self.assertTrue(b == d) self.assertEqual(b, b.dict()) - def test_setdefault(self): + def test_setdefault(self) -> None: b = BaseDict() b["a"] = 1 b["b"] = 2 @@ -417,7 +420,7 @@ def test_setdefault(self): self.assertEqual(v, 4) self.assertEqual(b, b.dict()) - def test_setdefault_with_pointer(self): + def test_setdefault_with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -441,7 +444,7 @@ def test_setdefault_with_pointer(self): self.assertTrue(b == d) self.assertEqual(b, b.dict()) - def test_update(self): + def test_update(self) -> None: b = BaseDict() b["a"] = 1 b["b"] = 2 @@ -455,7 +458,7 @@ def test_update(self): self.assertEqual(b, {"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}) self.assertEqual(b, b.dict()) - def test_update_with_pointer(self): + def test_update_with_pointer(self) -> None: d = { "a": 1, "b": 2, @@ -482,7 +485,7 @@ def test_update_with_pointer(self): self.assertTrue(b == d) self.assertEqual(b, b.dict()) - def test_values(self): + def test_values(self) -> None: b = BaseDict() b["a"] = 1 b["b"] = 2 @@ -492,7 +495,7 @@ def test_values(self): self.assertTrue(v, [1, 2, 3]) self.assertEqual(b, b.dict()) - def test_values_with_pointer(self): + def test_values_with_pointer(self) -> None: d = { "a": 1, "b": 2, diff --git a/tests/dicts/io/test_io_dict.py b/tests/dicts/io/test_io_dict.py index eca98583..491d4fd7 100644 --- a/tests/dicts/io/test_io_dict.py +++ b/tests/dicts/io/test_io_dict.py @@ -11,28 +11,28 @@ class io_dict_test_case(unittest.TestCase): """ @classmethod - def tearDownClass(cls): + def tearDownClass(cls) -> None: dir_path = cls.output_path(filepath="") shutil.rmtree(dir_path, ignore_errors=True) @staticmethod - def input_path(filepath): + def input_path(filepath: str) -> str: dir_path = os.path.dirname(os.path.realpath(__file__)) return os.path.join(dir_path, f"input/{filepath}") @staticmethod - def input_url(filepath): + def input_url(filepath: str) -> str: return f"https://raw.githubusercontent.com/fabiocaccamo/python-benedict/main/tests/dicts/io/input/{filepath}" @staticmethod - def output_path(filepath): + def output_path(filepath: str) -> str: dir_path = os.path.dirname(os.path.realpath(__file__)) return os.path.join(dir_path, f"output/{filepath}") - def assertFileExists(self, filepath): + def assertFileExists(self, filepath: str) -> None: self.assertTrue(os.path.isfile(filepath)) - def test_init_with_key_value_list(self): + def test_init_with_key_value_list(self) -> None: d = IODict(a="1", b="2", c="3") self.assertEqual( d, @@ -43,6 +43,6 @@ def test_init_with_key_value_list(self): }, ) - def test_init_with_invalid_data(self): + def test_init_with_invalid_data(self) -> None: with self.assertRaises(ValueError): _ = IODict("invalid json data") diff --git a/tests/dicts/io/test_io_dict_base64.py b/tests/dicts/io/test_io_dict_base64.py index 3e55c344..f9401eef 100644 --- a/tests/dicts/io/test_io_dict_base64.py +++ b/tests/dicts/io/test_io_dict_base64.py @@ -8,7 +8,7 @@ class io_dict_base64_test_case(io_dict_test_case): This class describes an IODict / base64 test case. """ - def test_from_base64_with_valid_data(self): + def test_from_base64_with_valid_data(self) -> None: j = "eyJhIjogMSwgImIiOiAyLCAiYyI6IDN9" # j = '{"a": 1, "b": 2, "c": 3}' # static method @@ -24,7 +24,7 @@ def test_from_base64_with_valid_data(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, {"a": 1, "b": 2, "c": 3}) - def test_from_base64_with_valid_data_without_padding(self): + def test_from_base64_with_valid_data_without_padding(self) -> None: j = "eyJhIjogMSwgImIiOiAyLCAiYyI6IDMsICJkIjogNH0" # eyJhIjogMSwgImIiOiAyLCAiYyI6IDMsICJkIjogNH0= # j = '{"a": 1, "b": 2, "c": 3, "d": 4}' @@ -37,7 +37,7 @@ def test_from_base64_with_valid_data_without_padding(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, {"a": 1, "b": 2, "c": 3, "d": 4}) - def test_from_base64_with_invalid_data(self): + def test_from_base64_with_invalid_data(self) -> None: j = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -46,7 +46,7 @@ def test_from_base64_with_invalid_data(self): with self.assertRaises(ValueError): IODict(j, format="base64") - def test_from_base64_with_valid_file_valid_content(self): + def test_from_base64_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.base64") # static method d = IODict.from_base64(filepath) @@ -58,7 +58,7 @@ def test_from_base64_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_base64_with_valid_file_valid_content_invalid_format(self): + def test_from_base64_with_valid_file_valid_content_invalid_format(self) -> None: filepath = self.input_path("valid-content.json") with self.assertRaises(ValueError): IODict.from_base64(filepath) @@ -75,7 +75,7 @@ def test_from_base64_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_base64(filepath) - def test_from_base64_with_valid_file_invalid_content(self): + def test_from_base64_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.base64") # static method with self.assertRaises(ValueError): @@ -84,7 +84,7 @@ def test_from_base64_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="base64") - def test_from_base64_with_invalid_file(self): + def test_from_base64_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.base64") # static method with self.assertRaises(ValueError): @@ -93,7 +93,7 @@ def test_from_base64_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath, format="base64") - def test_from_base64_with_valid_url_valid_content(self): + def test_from_base64_with_valid_url_valid_content(self) -> None: url = self.input_url("valid-content.base64") # static method d = IODict.from_base64(url) @@ -105,7 +105,7 @@ def test_from_base64_with_valid_url_valid_content(self): d = IODict(url) self.assertTrue(isinstance(d, dict)) - def test_from_base64_with_valid_url_invalid_content(self): + def test_from_base64_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -114,7 +114,7 @@ def test_from_base64_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="base64") - def test_from_base64_with_invalid_url(self): + def test_from_base64_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -123,12 +123,12 @@ def test_from_base64_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="base64") - def test_to_base64(self): + def test_to_base64(self) -> None: d = IODict({"a": 1, "b": 2, "c": 3}) s = d.to_base64(sort_keys=True) self.assertEqual(s, "eyJhIjogMSwgImIiOiAyLCAiYyI6IDN9") - def test_to_base64_file(self): + def test_to_base64_file(self) -> None: d = IODict({"a": 1, "b": 2, "c": 3}) filepath = self.output_path("test_to_base64_file.base64") d.to_base64(filepath=filepath, sort_keys=True) diff --git a/tests/dicts/io/test_io_dict_cli.py b/tests/dicts/io/test_io_dict_cli.py index bb26d11a..ec614eee 100644 --- a/tests/dicts/io/test_io_dict_cli.py +++ b/tests/dicts/io/test_io_dict_cli.py @@ -11,7 +11,7 @@ class io_dict_cli_test_case(io_dict_test_case): This class describes an IODict / cli test case. """ - def test_from_cli_with_valid_data(self): + def test_from_cli_with_valid_data(self) -> None: s = """--url "https://github.com" --usernames another handle --languages Python --languages JavaScript -v --count --count --count""" # static method r = { @@ -30,7 +30,7 @@ def test_from_cli_with_valid_data(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, r) - def test_from_cli_with_invalid_arguments(self): + def test_from_cli_with_invalid_arguments(self) -> None: s = """--help -h""" # static method with self.assertRaises(ValueError): @@ -39,7 +39,7 @@ def test_from_cli_with_invalid_arguments(self): with self.assertRaises(ValueError): IODict(s, format="cli") - def test_from_cli_with_invalid_data(self): + def test_from_cli_with_invalid_data(self) -> None: with ( patch( "sys.stdout", @@ -58,7 +58,7 @@ def test_from_cli_with_invalid_data(self): with self.assertRaises(ValueError): IODict(s, format="cli") - def test_to_cli(self): + def test_to_cli(self) -> None: d = IODict( { "values": [ diff --git a/tests/dicts/io/test_io_dict_csv.py b/tests/dicts/io/test_io_dict_csv.py index 3da5aa67..e29346d9 100644 --- a/tests/dicts/io/test_io_dict_csv.py +++ b/tests/dicts/io/test_io_dict_csv.py @@ -8,7 +8,7 @@ class io_dict_csv_test_case(io_dict_test_case): This class describes an IODict / csv test case. """ - def test_from_csv_with_valid_data(self): + def test_from_csv_with_valid_data(self) -> None: s = """id,name,age,height,weight 1,Alice,20,62,120.6 2,Freddie,21,74,190.6 @@ -65,7 +65,7 @@ def test_from_csv_with_valid_data(self): # with self.assertRaises(ValueError): # IODict(s, format='csv') - def test_from_csv_with_valid_file_valid_content(self): + def test_from_csv_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.csv") # static method d = IODict.from_csv(filepath) @@ -100,7 +100,7 @@ def test_from_csv_with_valid_file_valid_content(self): # with self.assertRaises(ValueError): # IODict(filepath, format='csv') - def test_from_csv_with_invalid_file(self): + def test_from_csv_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.csv") # static method with self.assertRaises(ValueError): @@ -128,7 +128,7 @@ def test_from_csv_with_invalid_file(self): # with self.assertRaises(ValueError): # IODict(url, format='csv') - def test_from_csv_with_invalid_url(self): + def test_from_csv_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -137,7 +137,7 @@ def test_from_csv_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="csv") - def test_to_csv(self): + def test_to_csv(self) -> None: d = IODict( { "values": [ @@ -181,7 +181,7 @@ def test_to_csv(self): """ self.assertEqual(s, r) - def test_to_csv_with_custom_columns(self): + def test_to_csv_with_custom_columns(self) -> None: d = IODict( { "values": [ @@ -228,7 +228,7 @@ def test_to_csv_with_custom_columns(self): """ self.assertEqual(s, r) - def test_to_csv_with_custom_delimiter_and_quotes(self): + def test_to_csv_with_custom_delimiter_and_quotes(self) -> None: d = IODict( { "values": [ @@ -274,7 +274,7 @@ def test_to_csv_with_custom_delimiter_and_quotes(self): """ self.assertEqual(s, r) - def test_to_csv_with_custom_key_valid(self): + def test_to_csv_with_custom_key_valid(self) -> None: d = IODict( { "results": [ @@ -318,7 +318,7 @@ def test_to_csv_with_custom_key_valid(self): """ self.assertEqual(s, r) - def test_to_csv_with_custom_key_invalid(self): + def test_to_csv_with_custom_key_invalid(self) -> None: d = IODict( { "values": [ @@ -358,7 +358,7 @@ def test_to_csv_with_custom_key_invalid(self): "invalid_values", columns=["id", "name", "age", "height", "weight"] ) - def test_to_csv_file(self): + def test_to_csv_file(self) -> None: d = IODict( { "values": [ diff --git a/tests/dicts/io/test_io_dict_html.py b/tests/dicts/io/test_io_dict_html.py index 73909b07..d88235a7 100644 --- a/tests/dicts/io/test_io_dict_html.py +++ b/tests/dicts/io/test_io_dict_html.py @@ -11,7 +11,7 @@ class io_dict_html_test_case(io_dict_test_case): This class describes an IODict / html test case. """ - def test_from_html_with_valid_file_valid_content(self): + def test_from_html_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.html") expected_title = ( "Fabio Caccamo - Python/Django full-stack developer - Torino, Italy" @@ -30,7 +30,9 @@ def test_from_html_with_valid_file_valid_content(self): self.assertEqual(d["html"]["head"]["title"], expected_title) @patch("benedict.serializers.html.html_installed", False) - def test_from_html_with_valid_file_valid_content_but_xls_extra_not_installed(self): + def test_from_html_with_valid_file_valid_content_but_xls_extra_not_installed( + self, + ) -> None: filepath = self.input_path("valid-content.html") # static method with self.assertRaises(ExtrasRequireModuleNotFoundError): @@ -42,7 +44,7 @@ def test_from_html_with_valid_file_valid_content_but_xls_extra_not_installed(sel with self.assertRaises(ExtrasRequireModuleNotFoundError): _ = IODict(filepath) - def test_from_html_with_valid_url_valid_content(self): + def test_from_html_with_valid_url_valid_content(self) -> None: expected_title = ( "Fabio Caccamo - Python/Django full-stack developer - Torino, Italy" ) @@ -60,7 +62,7 @@ def test_from_html_with_valid_url_valid_content(self): with self.assertRaises(ValueError): _ = IODict(url) - def test_from_html_with_invalid_file(self): + def test_from_html_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.html") # static method with self.assertRaises(ValueError): @@ -72,7 +74,7 @@ def test_from_html_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath) - def test_from_html_with_valid_url_invalid_content(self): + def test_from_html_with_valid_url_invalid_content(self) -> None: url = "https://raw.githubusercontent.com/fabiocaccamo/python-benedict/main/README.md" # static method with self.assertRaises(ValueError): @@ -84,7 +86,7 @@ def test_from_html_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url) - def test_from_html_with_invalid_url(self): + def test_from_html_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -96,7 +98,7 @@ def test_from_html_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url) - def test_to_html(self): + def test_to_html(self) -> None: d = IODict( { "html": { diff --git a/tests/dicts/io/test_io_dict_ini.py b/tests/dicts/io/test_io_dict_ini.py index b71aefaf..d146f709 100644 --- a/tests/dicts/io/test_io_dict_ini.py +++ b/tests/dicts/io/test_io_dict_ini.py @@ -8,7 +8,7 @@ class io_dict_ini_test_case(io_dict_test_case): This class describes an IODict / ini test case. """ - def test_from_ini_with_valid_data(self): + def test_from_ini_with_valid_data(self) -> None: s = """ [DEFAULT] ServerAliveInterval = 45 @@ -53,7 +53,7 @@ def test_from_ini_with_valid_data(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, r) - def test_from_ini_with_valid_data_and_optionxform_custom(self): + def test_from_ini_with_valid_data_and_optionxform_custom(self) -> None: s = """ [DEFAULT] ServerAliveInterval = 45 @@ -90,7 +90,7 @@ def test_from_ini_with_valid_data_and_optionxform_custom(self): }, } - def optionxform(key): + def optionxform(key: str) -> str: return key.upper() d = IODict.from_ini(s, optionxform=optionxform) @@ -101,7 +101,7 @@ def optionxform(key): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, r) - def test_from_ini_with_valid_data_and_optionxform_none(self): + def test_from_ini_with_valid_data_and_optionxform_none(self) -> None: s = """ [DEFAULT] ServerAliveInterval = 45 @@ -146,7 +146,7 @@ def test_from_ini_with_valid_data_and_optionxform_none(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, r) - def test_from_ini_with_invalid_data(self): + def test_from_ini_with_invalid_data(self) -> None: s = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -155,7 +155,7 @@ def test_from_ini_with_invalid_data(self): with self.assertRaises(ValueError): IODict(s, format="ini") - def test_from_ini_with_valid_file_valid_content(self): + def test_from_ini_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.ini") # static method d = IODict.from_ini(filepath) @@ -167,7 +167,7 @@ def test_from_ini_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_ini_with_valid_file_valid_content_invalid_format(self): + def test_from_ini_with_valid_file_valid_content_invalid_format(self) -> None: filepath = self.input_path("valid-content.base64") with self.assertRaises(ValueError): IODict.from_ini(filepath) @@ -190,7 +190,7 @@ def test_from_ini_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_ini(filepath) - def test_from_ini_with_valid_file_invalid_content(self): + def test_from_ini_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.ini") # static method with self.assertRaises(ValueError): @@ -199,7 +199,7 @@ def test_from_ini_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="ini") - def test_from_ini_with_invalid_file(self): + def test_from_ini_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.ini") # static method with self.assertRaises(ValueError): @@ -220,7 +220,7 @@ def test_from_ini_with_invalid_file(self): # d = IODict(url) # self.assertTrue(isinstance(d, dict)) - def test_from_ini_with_valid_url_invalid_content(self): + def test_from_ini_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -229,7 +229,7 @@ def test_from_ini_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="ini") - def test_from_ini_with_invalid_url(self): + def test_from_ini_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -238,7 +238,7 @@ def test_from_ini_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="ini") - def test_to_ini(self): + def test_to_ini(self) -> None: d = IODict( { "serveraliveinterval": 45, @@ -264,7 +264,7 @@ def test_to_ini(self): s = d.to_ini() self.assertEqual(d, IODict.from_ini(s)) - def test_to_ini_file(self): + def test_to_ini_file(self) -> None: d = IODict( { "serveraliveinterval": 45, diff --git a/tests/dicts/io/test_io_dict_json.py b/tests/dicts/io/test_io_dict_json.py index 4c064505..046dce42 100644 --- a/tests/dicts/io/test_io_dict_json.py +++ b/tests/dicts/io/test_io_dict_json.py @@ -8,7 +8,7 @@ class io_dict_json_test_case(io_dict_test_case): This class describes an IODict / json test case. """ - def test_from_json_with_valid_data(self): + def test_from_json_with_valid_data(self) -> None: j = '{"a": 1, "b": 2, "c": 3}' # static method d = IODict.from_json(j) @@ -19,7 +19,7 @@ def test_from_json_with_valid_data(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, {"a": 1, "b": 2, "c": 3}) - def test_from_json_with_valid_data_empty(self): + def test_from_json_with_valid_data_empty(self) -> None: j = "{}" # static method d = IODict.from_json(j) @@ -30,7 +30,7 @@ def test_from_json_with_valid_data_empty(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, {}) - def test_from_json_with_valid_data_list(self): + def test_from_json_with_valid_data_list(self) -> None: j = "[0,1,2,3,4,5]" # static method d = IODict.from_json(j) @@ -62,7 +62,7 @@ def test_from_json_with_valid_data_list(self): # self.assertTrue(isinstance(d, dict)) # self.assertEqual(d, { 'a': 1, 'b': 2, 'c': 3, }) - def test_from_json_with_invalid_data(self): + def test_from_json_with_invalid_data(self) -> None: j = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -71,7 +71,7 @@ def test_from_json_with_invalid_data(self): with self.assertRaises(ValueError): IODict(j, format="json") - def test_from_json_with_valid_file_valid_content(self): + def test_from_json_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.json") # static method d = IODict.from_json(filepath) @@ -83,7 +83,9 @@ def test_from_json_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_json_with_valid_file_valid_content_but_unexpected_extension(self): + def test_from_json_with_valid_file_valid_content_but_unexpected_extension( + self, + ) -> None: filepath = self.input_path("valid-content.json.txt") # static method d = IODict.from_json(filepath) @@ -95,7 +97,7 @@ def test_from_json_with_valid_file_valid_content_but_unexpected_extension(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_json_with_valid_file_valid_content_invalid_format(self): + def test_from_json_with_valid_file_valid_content_invalid_format(self) -> None: filepath = self.input_path("valid-content.base64") with self.assertRaises(ValueError): IODict.from_json(filepath) @@ -112,7 +114,7 @@ def test_from_json_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_json(filepath) - def test_from_json_with_valid_file_invalid_content(self): + def test_from_json_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.json") # static method with self.assertRaises(ValueError): @@ -121,7 +123,7 @@ def test_from_json_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="json") - def test_from_json_with_invalid_file(self): + def test_from_json_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.json") # static method with self.assertRaises(ValueError): @@ -130,7 +132,7 @@ def test_from_json_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath, format="json") - def test_from_json_with_valid_url_valid_content(self): + def test_from_json_with_valid_url_valid_content(self) -> None: url = self.input_url("valid-content.json") # static method d = IODict.from_json(url) @@ -142,7 +144,7 @@ def test_from_json_with_valid_url_valid_content(self): d = IODict(url) self.assertTrue(isinstance(d, dict)) - def test_from_json_with_valid_url_invalid_content(self): + def test_from_json_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -151,7 +153,7 @@ def test_from_json_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="json") - def test_from_json_with_invalid_url(self): + def test_from_json_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -160,12 +162,12 @@ def test_from_json_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="json") - def test_to_json(self): + def test_to_json(self) -> None: d = IODict({"x": 7, "y": 8, "z": 9, "a": 1, "b": 2, "c": 3}) s = d.to_json(sort_keys=True) self.assertEqual(s, '{"a": 1, "b": 2, "c": 3, "x": 7, "y": 8, "z": 9}') - def test_to_json_file(self): + def test_to_json_file(self) -> None: d = IODict({"x": 7, "y": 8, "z": 9, "a": 1, "b": 2, "c": 3}) filepath = self.output_path("test_to_json_file.json") d.to_json(filepath=filepath, sort_keys=True) diff --git a/tests/dicts/io/test_io_dict_pickle.py b/tests/dicts/io/test_io_dict_pickle.py index 744a1123..c903c5aa 100644 --- a/tests/dicts/io/test_io_dict_pickle.py +++ b/tests/dicts/io/test_io_dict_pickle.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import datetime as dt from benedict.dicts.io import IODict @@ -11,16 +13,16 @@ class io_dict_pickle_test_case(io_dict_test_case): """ @staticmethod - def _get_pickle_decoded(): + def _get_pickle_decoded() -> dict[str, dt.datetime]: return { "date": dt.datetime(year=1985, month=4, day=3), } @staticmethod - def _get_pickle_encoded(): + def _get_pickle_encoded() -> str: return "gAJ9cQBYBAAAAGRhdGVxAWNkYXRldGltZQpkYXRldGltZQpxAmNfY29kZWNzCmVuY29kZQpxA1gLAAAAB8OBBAMAAAAAAABxBFgGAAAAbGF0aW4xcQWGcQZScQeFcQhScQlzLg==" - def test_from_pickle_with_valid_data(self): + def test_from_pickle_with_valid_data(self) -> None: j = self._get_pickle_encoded() r = self._get_pickle_decoded() # static method @@ -32,7 +34,7 @@ def test_from_pickle_with_valid_data(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, r) - def test_from_pickle_with_invalid_data(self): + def test_from_pickle_with_invalid_data(self) -> None: j = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -41,7 +43,7 @@ def test_from_pickle_with_invalid_data(self): with self.assertRaises(ValueError): IODict(j, format="pickle") - def test_from_pickle_with_valid_file_valid_content(self): + def test_from_pickle_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.pickle") # static method d = IODict.from_pickle(filepath) @@ -53,7 +55,7 @@ def test_from_pickle_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_pickle_with_valid_file_valid_content_invalid_format(self): + def test_from_pickle_with_valid_file_valid_content_invalid_format(self) -> None: filepath = self.input_path("valid-content.json") with self.assertRaises(ValueError): IODict.from_pickle(filepath) @@ -70,7 +72,7 @@ def test_from_pickle_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_pickle(filepath) - def test_from_pickle_with_valid_file_invalid_content(self): + def test_from_pickle_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.pickle") # static method with self.assertRaises(ValueError): @@ -79,7 +81,7 @@ def test_from_pickle_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="pickle") - def test_from_pickle_with_invalid_file(self): + def test_from_pickle_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.pickle") # static method with self.assertRaises(ValueError): @@ -88,7 +90,7 @@ def test_from_pickle_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath, format="pickle") - def test_from_pickle_with_valid_url_valid_content(self): + def test_from_pickle_with_valid_url_valid_content(self) -> None: url = self.input_url("valid-content.pickle") # static method d = IODict.from_pickle(url) @@ -100,7 +102,7 @@ def test_from_pickle_with_valid_url_valid_content(self): d = IODict(url) self.assertTrue(isinstance(d, dict)) - def test_from_pickle_with_valid_url_invalid_content(self): + def test_from_pickle_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -109,7 +111,7 @@ def test_from_pickle_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="pickle") - def test_from_pickle_with_invalid_url(self): + def test_from_pickle_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -118,12 +120,12 @@ def test_from_pickle_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="pickle") - def test_to_pickle(self): + def test_to_pickle(self) -> None: d = IODict(self._get_pickle_decoded()) s = d.to_pickle() self.assertEqual(IODict.from_pickle(s), self._get_pickle_decoded()) - def test_to_pickle_file(self): + def test_to_pickle_file(self) -> None: d = IODict({"date": self._get_pickle_decoded()}) filepath = self.output_path("test_to_pickle_file.pickle") d.to_pickle(filepath=filepath) diff --git a/tests/dicts/io/test_io_dict_plist.py b/tests/dicts/io/test_io_dict_plist.py index bb08902b..bf23a07e 100644 --- a/tests/dicts/io/test_io_dict_plist.py +++ b/tests/dicts/io/test_io_dict_plist.py @@ -1,4 +1,5 @@ import datetime as dt +from typing import Any from benedict.dicts.io import IODict @@ -10,7 +11,7 @@ class io_dict_plist_test_case(io_dict_test_case): This class describes an IODict / plist test case. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self._dict = { "aString": "Doodah", @@ -101,7 +102,7 @@ def __init__(self, *args, **kwargs): """ - def test_from_plist_with_valid_data(self): + def test_from_plist_with_valid_data(self) -> None: j = self._plist # static method d = IODict.from_plist(j) @@ -113,7 +114,7 @@ def test_from_plist_with_valid_data(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, self._dict) - def test_from_plist_with_invalid_data(self): + def test_from_plist_with_invalid_data(self) -> None: j = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -122,7 +123,7 @@ def test_from_plist_with_invalid_data(self): with self.assertRaises(ValueError): IODict(j, format="plist") - def test_from_plist_with_valid_file_valid_content(self): + def test_from_plist_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.plist") # static method d = IODict.from_plist(filepath) @@ -134,7 +135,7 @@ def test_from_plist_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_plist_with_valid_file_valid_content_invalid_format(self): + def test_from_plist_with_valid_file_valid_content_invalid_format(self) -> None: filepath = self.input_path("valid-content.base64") with self.assertRaises(ValueError): IODict.from_plist(filepath) @@ -160,7 +161,7 @@ def test_from_plist_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_plist(filepath) - def test_from_plist_with_valid_file_invalid_content(self): + def test_from_plist_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.plist") # static method with self.assertRaises(ValueError): @@ -169,7 +170,7 @@ def test_from_plist_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="plist") - def test_from_plist_with_invalid_file(self): + def test_from_plist_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.plist") # static method with self.assertRaises(ValueError): @@ -178,7 +179,7 @@ def test_from_plist_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath, format="plist") - def test_from_plist_with_valid_url_valid_content(self): + def test_from_plist_with_valid_url_valid_content(self) -> None: url = self.input_url("valid-content.plist") # static method d = IODict.from_plist(url) @@ -190,7 +191,7 @@ def test_from_plist_with_valid_url_valid_content(self): d = IODict(url) self.assertTrue(isinstance(d, dict)) - def test_from_plist_with_valid_url_invalid_content(self): + def test_from_plist_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -199,7 +200,7 @@ def test_from_plist_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="plist") - def test_from_plist_with_invalid_url(self): + def test_from_plist_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -208,7 +209,7 @@ def test_from_plist_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="plist") - def test_to_plist(self): + def test_to_plist(self) -> None: # example data taken from: # https://docs.python.org/3/library/plistlib.html#examples d = IODict(self._dict) @@ -216,7 +217,7 @@ def test_to_plist(self): # print(s) self.assertEqual(d, IODict.from_plist(s)) - def test_to_plist_file(self): + def test_to_plist_file(self) -> None: d = IODict(self._dict) filepath = self.output_path("test_to_plist_file.plist") d.to_plist(filepath=filepath) diff --git a/tests/dicts/io/test_io_dict_query_string.py b/tests/dicts/io/test_io_dict_query_string.py index aab873cd..702a54c8 100644 --- a/tests/dicts/io/test_io_dict_query_string.py +++ b/tests/dicts/io/test_io_dict_query_string.py @@ -8,7 +8,7 @@ class io_dict_query_string_test_case(io_dict_test_case): This class describes an IODict / query-string test case. """ - def test_from_query_string_with_valid_data(self): + def test_from_query_string_with_valid_data(self) -> None: s = "ok=1&test=2&page=3&lib=python%20benedict&author=Fabio+Caccamo&author=Fabio%20Caccamo" r = { "ok": "1", @@ -26,7 +26,7 @@ def test_from_query_string_with_valid_data(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, r) - def test_from_query_string_with_invalid_data(self): + def test_from_query_string_with_invalid_data(self) -> None: s = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -35,7 +35,7 @@ def test_from_query_string_with_invalid_data(self): with self.assertRaises(ValueError): IODict(s, format="query_string") - def test_from_query_string_with_valid_file_valid_content(self): + def test_from_query_string_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.qs") # static method d = IODict.from_query_string(filepath) @@ -47,7 +47,9 @@ def test_from_query_string_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_query_string_with_valid_file_valid_content_invalid_format(self): + def test_from_query_string_with_valid_file_valid_content_invalid_format( + self, + ) -> None: filepath = self.input_path("valid-content.base64") with self.assertRaises(ValueError): IODict.from_query_string(filepath) @@ -64,7 +66,7 @@ def test_from_query_string_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_query_string(filepath) - def test_from_query_string_with_valid_file_invalid_content(self): + def test_from_query_string_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.qs") # static method with self.assertRaises(ValueError): @@ -73,7 +75,7 @@ def test_from_query_string_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="query_string") - def test_from_query_string_with_invalid_file(self): + def test_from_query_string_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.qs") # static method with self.assertRaises(ValueError): @@ -82,7 +84,7 @@ def test_from_query_string_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath, format="query_string") - def test_from_query_string_with_valid_url_valid_content(self): + def test_from_query_string_with_valid_url_valid_content(self) -> None: url = self.input_url("valid-content.qs") # static method d = IODict.from_query_string(url) @@ -94,7 +96,7 @@ def test_from_query_string_with_valid_url_valid_content(self): d = IODict(url) self.assertTrue(isinstance(d, dict)) - def test_from_query_string_with_valid_url_invalid_content(self): + def test_from_query_string_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -103,7 +105,7 @@ def test_from_query_string_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="query_string") - def test_from_query_string_with_invalid_url(self): + def test_from_query_string_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -112,7 +114,7 @@ def test_from_query_string_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="query_string") - def test_to_query_string(self): + def test_to_query_string(self) -> None: d = IODict( { "ok": "1", @@ -125,7 +127,7 @@ def test_to_query_string(self): s = d.to_query_string() self.assertEqual(d, IODict.from_query_string(s)) - def test_to_query_string_file(self): + def test_to_query_string_file(self) -> None: d = IODict( { "ok": "1", diff --git a/tests/dicts/io/test_io_dict_toml.py b/tests/dicts/io/test_io_dict_toml.py index 74cb8c7b..f7232bcd 100644 --- a/tests/dicts/io/test_io_dict_toml.py +++ b/tests/dicts/io/test_io_dict_toml.py @@ -13,7 +13,7 @@ class io_dict_toml_test_case(io_dict_test_case): This class describes an IODict / toml test case. """ - def test_from_toml_with_valid_data(self): + def test_from_toml_with_valid_data(self) -> None: j = """ a = 1 @@ -53,7 +53,7 @@ def test_from_toml_with_valid_data(self): "standard tomlib is available, exception will not be raised", ) @patch("benedict.serializers.toml.toml_installed", False) - def test_from_toml_with_valid_data_but_toml_extra_not_installed(self): + def test_from_toml_with_valid_data_but_toml_extra_not_installed(self) -> None: j = """ a = 1 @@ -68,7 +68,7 @@ def test_from_toml_with_valid_data_but_toml_extra_not_installed(self): with self.assertRaises(ExtrasRequireModuleNotFoundError): _ = IODict(j, format="toml") - def test_from_toml_with_invalid_data(self): + def test_from_toml_with_invalid_data(self) -> None: j = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -77,7 +77,7 @@ def test_from_toml_with_invalid_data(self): with self.assertRaises(ValueError): IODict(j, format="toml") - def test_from_toml_with_valid_file_valid_content(self): + def test_from_toml_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.toml") # static method d = IODict.from_toml(filepath) @@ -89,7 +89,7 @@ def test_from_toml_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_toml_with_valid_file_valid_content_invalid_format(self): + def test_from_toml_with_valid_file_valid_content_invalid_format(self) -> None: # filepath = self.input_path('valid-content.base64') # with self.assertRaises(ValueError): # d = IODict.from_toml(filepath) @@ -106,7 +106,7 @@ def test_from_toml_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_toml(filepath) - def test_from_toml_with_valid_file_invalid_content(self): + def test_from_toml_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.toml") # static method with self.assertRaises(ValueError): @@ -115,7 +115,7 @@ def test_from_toml_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="toml") - def test_from_toml_with_invalid_file(self): + def test_from_toml_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.toml") # static method with self.assertRaises(ValueError): @@ -124,7 +124,7 @@ def test_from_toml_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath, format="toml") - def test_from_toml_with_valid_url_valid_content(self): + def test_from_toml_with_valid_url_valid_content(self) -> None: url = self.input_url("valid-content.toml") # static method d = IODict.from_toml(url) @@ -136,7 +136,7 @@ def test_from_toml_with_valid_url_valid_content(self): d = IODict(url) self.assertTrue(isinstance(d, dict)) - def test_from_toml_with_valid_url_invalid_content(self): + def test_from_toml_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -145,7 +145,7 @@ def test_from_toml_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="toml") - def test_from_toml_with_invalid_url(self): + def test_from_toml_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -154,7 +154,7 @@ def test_from_toml_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="toml") - def test_to_toml(self): + def test_to_toml(self) -> None: d = IODict( { "x": 7, @@ -168,7 +168,7 @@ def test_to_toml(self): s = d.to_toml() self.assertEqual(d, IODict.from_toml(s)) - def test_to_toml_file(self): + def test_to_toml_file(self) -> None: d = IODict( { "x": 7, @@ -185,7 +185,7 @@ def test_to_toml_file(self): self.assertEqual(d, IODict.from_toml(filepath)) @patch("benedict.serializers.toml.toml_installed", False) - def test_to_toml_with_extra_not_installed(self): + def test_to_toml_with_extra_not_installed(self) -> None: d = IODict( { "a": 1, diff --git a/tests/dicts/io/test_io_dict_xls.py b/tests/dicts/io/test_io_dict_xls.py index 8561120d..780adaa9 100644 --- a/tests/dicts/io/test_io_dict_xls.py +++ b/tests/dicts/io/test_io_dict_xls.py @@ -1,3 +1,4 @@ +from typing import Any from unittest.mock import patch from decouple import config @@ -13,7 +14,7 @@ class io_dict_xls_test_case(io_dict_test_case): This class describes an IODict / xls test case. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self._extensions = [ "xlsx", @@ -21,7 +22,7 @@ def __init__(self, *args, **kwargs): "xls", ] - def test_from_xls_with_valid_file_valid_content(self): + def test_from_xls_with_valid_file_valid_content(self) -> None: expected_dict = { "values": [ { @@ -72,7 +73,9 @@ def test_from_xls_with_valid_file_valid_content(self): self.assertEqual(d, expected_dict) @patch("benedict.serializers.xls.xls_installed", False) - def test_from_xls_with_valid_file_valid_content_but_xls_extra_not_installed(self): + def test_from_xls_with_valid_file_valid_content_but_xls_extra_not_installed( + self, + ) -> None: for extension in self._extensions: with self.subTest( msg=f"test_from_xls_({extension})_with_valid_file_valid_content_but_xls_extra_not_installed" @@ -88,7 +91,7 @@ def test_from_xls_with_valid_file_valid_content_but_xls_extra_not_installed(self with self.assertRaises(ExtrasRequireModuleNotFoundError): _ = IODict(filepath) - def test_from_xls_with_valid_url_valid_content(self): + def test_from_xls_with_valid_url_valid_content(self) -> None: expected_dict = { "values": [ { @@ -139,7 +142,7 @@ def test_from_xls_with_valid_url_valid_content(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, expected_dict) - def test_from_xls_with_valid_s3_url_valid_content(self): + def test_from_xls_with_valid_s3_url_valid_content(self) -> None: aws_access_key_id = config("AWS_ACCESS_KEY_ID", default=None) aws_secret_access_key = config("AWS_SECRET_ACCESS_KEY", default=None) if not all([aws_access_key_id, aws_secret_access_key]): @@ -200,7 +203,7 @@ def test_from_xls_with_valid_s3_url_valid_content(self): def test_from_xls_with_valid_file_valid_content_custom_sheet_by_index_and_columns( self, - ): + ) -> None: expected_dict = { "values": [ { @@ -232,7 +235,7 @@ def test_from_xls_with_valid_file_valid_content_custom_sheet_by_index_and_column self.assertTrue(isinstance(d, dict)) self.assertEqual(d, expected_dict) - def test_from_xls_with_invalid_file(self): + def test_from_xls_with_invalid_file(self) -> None: for extension in self._extensions: with self.subTest( msg=f"test_from_xls_({extension})_with_valid_file_valid_content" @@ -248,7 +251,7 @@ def test_from_xls_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath) - def test_from_xls_with_valid_url_invalid_content(self): + def test_from_xls_with_valid_url_invalid_content(self) -> None: for extension in self._extensions: with self.subTest( msg=f"test_from_xls_({extension})_with_valid_url_invalid_content" @@ -264,7 +267,7 @@ def test_from_xls_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url) - def test_from_xls_with_invalid_url(self): + def test_from_xls_with_invalid_url(self) -> None: for extension in self._extensions: with self.subTest(msg=f"test_from_xls_({extension})_with_invalid_url"): url = "https://github.com/fabiocaccamo/python-benedict-invalid" @@ -278,7 +281,7 @@ def test_from_xls_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url) - def test_to_xls(self): + def test_to_xls(self) -> None: d = IODict( { "values": [ diff --git a/tests/dicts/io/test_io_dict_xml.py b/tests/dicts/io/test_io_dict_xml.py index 6fce0295..14b69399 100644 --- a/tests/dicts/io/test_io_dict_xml.py +++ b/tests/dicts/io/test_io_dict_xml.py @@ -11,7 +11,7 @@ class io_dict_xml_test_case(io_dict_test_case): This class describes an IODict / xml test case. """ - def test_from_xml_with_valid_data(self): + def test_from_xml_with_valid_data(self) -> None: j = """ @@ -44,7 +44,7 @@ def test_from_xml_with_valid_data(self): ) @patch("benedict.serializers.xml.xml_installed", False) - def test_from_xml_with_extra_not_installed(self): + def test_from_xml_with_extra_not_installed(self) -> None: j = """ @@ -62,7 +62,7 @@ def test_from_xml_with_extra_not_installed(self): with self.assertRaises(ExtrasRequireModuleNotFoundError): _ = IODict(j, format="xml") - def test_from_xml_with_invalid_data(self): + def test_from_xml_with_invalid_data(self) -> None: j = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -71,7 +71,7 @@ def test_from_xml_with_invalid_data(self): with self.assertRaises(ValueError): IODict(j, format="xml") - def test_from_xml_with_valid_file_valid_content(self): + def test_from_xml_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.xml") # static method d = IODict.from_xml(filepath) @@ -83,7 +83,7 @@ def test_from_xml_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_xml_with_valid_file_valid_content_invalid_format(self): + def test_from_xml_with_valid_file_valid_content_invalid_format(self) -> None: filepath = self.input_path("valid-content.base64") with self.assertRaises(ValueError): IODict.from_xml(filepath) @@ -100,7 +100,7 @@ def test_from_xml_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_xml(filepath) - def test_from_xml_with_valid_file_invalid_content(self): + def test_from_xml_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.xml") # static method with self.assertRaises(ValueError): @@ -109,7 +109,7 @@ def test_from_xml_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="xml") - def test_from_xml_with_invalid_file(self): + def test_from_xml_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.xml") # static method with self.assertRaises(ValueError): @@ -118,7 +118,7 @@ def test_from_xml_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath, format="xml") - def test_from_xml_with_valid_url_valid_content(self): + def test_from_xml_with_valid_url_valid_content(self) -> None: url = self.input_url("valid-content.xml") # static method d = IODict.from_xml(url) @@ -130,7 +130,7 @@ def test_from_xml_with_valid_url_valid_content(self): d = IODict(url) self.assertTrue(isinstance(d, dict)) - def test_from_xml_with_valid_url_invalid_content(self): + def test_from_xml_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -139,7 +139,7 @@ def test_from_xml_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="xml") - def test_from_xml_with_invalid_url(self): + def test_from_xml_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -148,7 +148,7 @@ def test_from_xml_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="xml") - def test_to_xml(self): + def test_to_xml(self) -> None: d = IODict( { "root": { @@ -164,7 +164,7 @@ def test_to_xml(self): s = d.to_xml() self.assertEqual(d, IODict.from_xml(s)) - def test_to_xml_file(self): + def test_to_xml_file(self) -> None: d = IODict( { "root": { @@ -183,7 +183,7 @@ def test_to_xml_file(self): self.assertEqual(d, IODict.from_xml(filepath)) @patch("benedict.serializers.xml.xml_installed", False) - def test_to_xml_with_extra_not_installed(self): + def test_to_xml_with_extra_not_installed(self) -> None: d = IODict( { "root": { diff --git a/tests/dicts/io/test_io_dict_yaml.py b/tests/dicts/io/test_io_dict_yaml.py index 6a987cea..fd687833 100644 --- a/tests/dicts/io/test_io_dict_yaml.py +++ b/tests/dicts/io/test_io_dict_yaml.py @@ -11,7 +11,7 @@ class io_dict_yaml_test_case(io_dict_test_case): This class describes an IODict / yaml test case. """ - def test_from_yaml_with_valid_data(self): + def test_from_yaml_with_valid_data(self) -> None: j = """ a: 1 b: @@ -46,7 +46,7 @@ def test_from_yaml_with_valid_data(self): ) @patch("benedict.serializers.yaml.yaml_installed", False) - def test_from_yaml_with_extra_not_installed(self): + def test_from_yaml_with_extra_not_installed(self) -> None: j = """ a: 1 b: @@ -60,7 +60,7 @@ def test_from_yaml_with_extra_not_installed(self): with self.assertRaises(ExtrasRequireModuleNotFoundError): _ = IODict(j, format="yaml") - def test_from_yaml_with_invalid_data(self): + def test_from_yaml_with_invalid_data(self) -> None: j = "Lorem ipsum est in ea occaecat nisi officia." # static method with self.assertRaises(ValueError): @@ -69,7 +69,7 @@ def test_from_yaml_with_invalid_data(self): with self.assertRaises(ValueError): IODict(j, format="yaml") - def test_from_yaml_with_valid_file_valid_content(self): + def test_from_yaml_with_valid_file_valid_content(self) -> None: filepath = self.input_path("valid-content.yml") # static method d = IODict.from_yaml(filepath) @@ -81,7 +81,7 @@ def test_from_yaml_with_valid_file_valid_content(self): d = IODict(filepath) self.assertTrue(isinstance(d, dict)) - def test_from_yaml_with_valid_file_valid_content_invalid_format(self): + def test_from_yaml_with_valid_file_valid_content_invalid_format(self) -> None: filepath = self.input_path("valid-content.base64") with self.assertRaises(ValueError): IODict.from_yaml(filepath) @@ -98,7 +98,7 @@ def test_from_yaml_with_valid_file_valid_content_invalid_format(self): with self.assertRaises(ValueError): IODict.from_yaml(filepath) - def test_from_yaml_with_valid_file_invalid_content(self): + def test_from_yaml_with_valid_file_invalid_content(self) -> None: filepath = self.input_path("invalid-content.yml") # static method with self.assertRaises(ValueError): @@ -107,7 +107,7 @@ def test_from_yaml_with_valid_file_invalid_content(self): with self.assertRaises(ValueError): IODict(filepath, format="yaml") - def test_from_yaml_with_invalid_file(self): + def test_from_yaml_with_invalid_file(self) -> None: filepath = self.input_path("invalid-file.yml") # static method with self.assertRaises(ValueError): @@ -116,7 +116,7 @@ def test_from_yaml_with_invalid_file(self): with self.assertRaises(ValueError): IODict(filepath, format="yaml") - def test_from_yaml_with_valid_url_valid_content(self): + def test_from_yaml_with_valid_url_valid_content(self) -> None: url = self.input_url("valid-content.yml") # static method d = IODict.from_yaml(url) @@ -128,7 +128,7 @@ def test_from_yaml_with_valid_url_valid_content(self): d = IODict(url) self.assertTrue(isinstance(d, dict)) - def test_from_yaml_with_valid_url_invalid_content(self): + def test_from_yaml_with_valid_url_invalid_content(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict" # static method with self.assertRaises(ValueError): @@ -137,7 +137,7 @@ def test_from_yaml_with_valid_url_invalid_content(self): with self.assertRaises(ValueError): IODict(url, format="yaml") - def test_from_yaml_with_invalid_url(self): + def test_from_yaml_with_invalid_url(self) -> None: url = "https://github.com/fabiocaccamo/python-benedict-invalid" # static method with self.assertRaises(ValueError): @@ -146,7 +146,7 @@ def test_from_yaml_with_invalid_url(self): with self.assertRaises(ValueError): IODict(url, format="yaml") - def test_to_yaml(self): + def test_to_yaml(self) -> None: d = IODict( { "x": 7, @@ -160,7 +160,7 @@ def test_to_yaml(self): s = d.to_yaml() self.assertEqual(d, IODict.from_yaml(s)) - def test_to_yaml_file(self): + def test_to_yaml_file(self) -> None: d = IODict( { "x": 7, @@ -177,7 +177,7 @@ def test_to_yaml_file(self): self.assertEqual(d, IODict.from_yaml(filepath)) @patch("benedict.serializers.yaml.yaml_installed", False) - def test_to_yaml_with_extra_not_installed(self): + def test_to_yaml_with_extra_not_installed(self) -> None: d = IODict( { "a": 1, diff --git a/tests/dicts/io/test_io_util.py b/tests/dicts/io/test_io_util.py index 56a68ca4..61cb1b1f 100644 --- a/tests/dicts/io/test_io_util.py +++ b/tests/dicts/io/test_io_util.py @@ -1,6 +1,6 @@ import tempfile import unittest -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, Mock, patch import fsutil from decouple import config @@ -15,88 +15,88 @@ class io_util_test_case(unittest.TestCase): This class describes an i/o utility test case. """ - def test_decode(self): + def test_decode(self) -> None: # TODO pass - def test_autodetect_format_by_data(self): + def test_autodetect_format_by_data(self) -> None: s = '{"a": 1, "b": 2, "c": 3}' self.assertEqual(io_util.autodetect_format(s), None) - def test_autodetect_format_by_path(self): + def test_autodetect_format_by_path(self) -> None: s = "path-to/data.xml" self.assertEqual(io_util.autodetect_format(s), "xml") - def test_autodetect_format_by_path_with_unsupported_format(self): + def test_autodetect_format_by_path_with_unsupported_format(self) -> None: s = "path-to/data.jpg" self.assertEqual(io_util.autodetect_format(s), None) - def test_autodetect_format_by_url(self): + def test_autodetect_format_by_url(self) -> None: s = "https://github.com/fabiocaccamo/python-benedict.xml" self.assertEqual(io_util.autodetect_format(s), "xml") - def test_autodetect_format_by_url_with_unsupported_format(self): + def test_autodetect_format_by_url_with_unsupported_format(self) -> None: s = "https://github.com/fabiocaccamo/python-benedict.jpg" self.assertEqual(io_util.autodetect_format(s), None) - def test_decode_with_invalid_format(self): - with self.assertRaises(ValueError): + def test_decode_with_invalid_format(self) -> None: + with self.assertRaises(KeyError): io_util.decode("", format="xxx") - def test_encode(self): + def test_encode(self) -> None: # TODO pass - def test_encode_with_invalid_format(self): - with self.assertRaises(ValueError): + def test_encode_with_invalid_format(self) -> None: + with self.assertRaises(KeyError): io_util.encode({}, format="xxx") - def test_is_data(self): + def test_is_data(self) -> None: # TODO pass - def test_is_filepath(self): + def test_is_filepath(self) -> None: path = "my-folder/my-file.json" self.assertTrue(io_util.is_filepath(path)) - def test_is_s3(self): + def test_is_s3(self) -> None: path = "s3://my-folder/my-file.json" self.assertTrue(io_util.is_s3(path)) - def test_is_s3_with_txt_document(self): + def test_is_s3_with_txt_document(self) -> None: path = "s3://my-folder/my-file.txt" self.assertTrue(io_util.is_s3(path)) - def test_is_url(self): + def test_is_url(self) -> None: path = "https://my-site.com/my-folder/my-file.json" self.assertTrue(io_util.is_url(path)) - def test_read_content(self): + def test_read_content(self) -> None: # TODO pass - def test_read_file(self): + def test_read_file(self) -> None: # TODO pass - def test_read_file_from_s3(self): + def test_read_file_from_s3(self) -> None: # TODO: pass - def test_read_url(self): + def test_read_url(self) -> None: # TODO pass - def test_write_file(self): + def test_write_file(self) -> None: # TODO pass - def test_write_file_to_s3(self): + def test_write_file_to_s3(self) -> None: # TODO: # io_util.write_file_to_s3("s3://test-bucket/my-file.txt", "ok", anon=True) pass - def test_parse_s3_url_valid(self): + def test_parse_s3_url_valid(self) -> None: url = "s3://my-bucket/path/to/key" result = io_util.parse_s3_url(url) expected_result = { @@ -106,7 +106,7 @@ def test_parse_s3_url_valid(self): } self.assertEqual(result, expected_result) - def test_parse_s3_url_with_query(self): + def test_parse_s3_url_with_query(self) -> None: url = "s3://my-bucket/path/to/key?versionId=123" result = io_util.parse_s3_url(url) expected_result = { @@ -116,7 +116,7 @@ def test_parse_s3_url_with_query(self): } self.assertEqual(result, expected_result) - def test_parse_s3_url_with_multiple_query_parameters(self): + def test_parse_s3_url_with_multiple_query_parameters(self) -> None: url = "s3://my-bucket/path/to/key?versionId=123&foo=bar" result = io_util.parse_s3_url(url) expected_result = { @@ -126,7 +126,7 @@ def test_parse_s3_url_with_multiple_query_parameters(self): } self.assertEqual(result, expected_result) - def test_parse_s3_url_with_special_characters(self): + def test_parse_s3_url_with_special_characters(self) -> None: url = "s3://my-bucket/path/to/key with spaces?foo=bar&baz=qux" result = io_util.parse_s3_url(url) expected_result = { @@ -137,7 +137,7 @@ def test_parse_s3_url_with_special_characters(self): self.assertEqual(result, expected_result) @patch("benedict.dicts.io.io_util.s3_installed", False) - def test_read_content_from_s3_with_s3_extra_not_installed(self): + def test_read_content_from_s3_with_s3_extra_not_installed(self) -> None: s3_options = { "aws_access_key_id": "", "aws_secret_access_key": "", @@ -149,7 +149,9 @@ def test_read_content_from_s3_with_s3_extra_not_installed(self): @patch("benedict.dicts.io.io_util.s3_installed", True) @patch("benedict.dicts.io.io_util.boto3.client") @patch("benedict.dicts.io.io_util.read_content_from_file") - def test_read_content_from_s3(self, mock_read_content_from_file, mock_boto3_client): + def test_read_content_from_s3( + self, mock_read_content_from_file: Mock, mock_boto3_client: Mock + ) -> None: aws_access_key_id = config("AWS_ACCESS_KEY_ID", default=None) aws_secret_access_key = config("AWS_SECRET_ACCESS_KEY", default=None) s3_options = { @@ -180,8 +182,8 @@ def test_read_content_from_s3(self, mock_read_content_from_file, mock_boto3_clie @patch("benedict.dicts.io.io_util.fsutil.remove_file") @patch("benedict.dicts.io.io_util.fsutil.write_file") def test_write_content_to_s3( - self, mock_write_file, mock_remove_file, mock_boto3_client - ): + self, mock_write_file: Mock, mock_remove_file: Mock, mock_boto3_client: Mock + ) -> None: aws_access_key_id = config("AWS_ACCESS_KEY_ID", default=None) aws_secret_access_key = config("AWS_SECRET_ACCESS_KEY", default=None) s3_options = { @@ -210,7 +212,7 @@ def test_write_content_to_s3( mock_s3.close.assert_called() mock_remove_file.assert_called_with(temporary_filepath) - def test_write_and_read_content_s3(self): + def test_write_and_read_content_s3(self) -> None: aws_access_key_id = config("AWS_ACCESS_KEY_ID", default=None) aws_secret_access_key = config("AWS_SECRET_ACCESS_KEY", default=None) if not all([aws_access_key_id, aws_secret_access_key]): @@ -225,7 +227,7 @@ def test_write_and_read_content_s3(self): content = io_util.read_content_from_s3(filepath, s3_options=s3_options) self.assertEqual(content, "ok") - def test_write_and_read_content_s3_with_s3_url_autodetection(self): + def test_write_and_read_content_s3_with_s3_url_autodetection(self) -> None: aws_access_key_id = config("AWS_ACCESS_KEY_ID", default=None) aws_secret_access_key = config("AWS_SECRET_ACCESS_KEY", default=None) if not all([aws_access_key_id, aws_secret_access_key]): @@ -241,7 +243,7 @@ def test_write_and_read_content_s3_with_s3_url_autodetection(self): self.assertEqual(content, "ok") @patch("benedict.dicts.io.io_util.s3_installed", False) - def test_write_and_read_content_s3_with_s3_extra_not_installed(self): + def test_write_and_read_content_s3_with_s3_extra_not_installed(self) -> None: aws_access_key_id = config("AWS_ACCESS_KEY_ID", default=None) aws_secret_access_key = config("AWS_SECRET_ACCESS_KEY", default=None) if not all([aws_access_key_id, aws_secret_access_key]): diff --git a/tests/dicts/io/test_pickle.py b/tests/dicts/io/test_pickle.py index 63689f04..5decf8bb 100644 --- a/tests/dicts/io/test_pickle.py +++ b/tests/dicts/io/test_pickle.py @@ -9,7 +9,7 @@ class pickle_test_case(unittest.TestCase): This class describes a pickle test case. """ - def test_pickle(self): + def test_pickle(self) -> None: d = { "a": {}, "b": {"x": 1}, diff --git a/tests/dicts/keylist/test_keylist_dict.py b/tests/dicts/keylist/test_keylist_dict.py index e708b1fc..c8152071 100644 --- a/tests/dicts/keylist/test_keylist_dict.py +++ b/tests/dicts/keylist/test_keylist_dict.py @@ -8,14 +8,14 @@ class keylist_dict_test_case(unittest.TestCase): This class describes a KeylistDict test case. """ - def test_contains(self): + def test_contains(self) -> None: d = KeylistDict() d["a"] = {} d["a"]["b"] = True self.assertTrue(["a", "b"] in d) self.assertFalse(["a", "b", "c"] in d) - def test_delitem(self): + def test_delitem(self) -> None: d = KeylistDict() d["a"] = {} d["a"]["b"] = {} @@ -25,21 +25,21 @@ def test_delitem(self): self.assertFalse(["a", "b", "c"] in d) self.assertTrue(["a", "b"] in d) - def test_get(self): + def test_get(self) -> None: d = KeylistDict() d["a"] = {} d["a"]["b"] = True self.assertEqual(d.get(["a", "b"]), True) self.assertEqual(d.get(["a", "b"]), True) - def test_getitem(self): + def test_getitem(self) -> None: d = KeylistDict() d["a"] = {} d["a"]["b"] = True self.assertEqual(d[["a", "b"]], True) self.assertEqual(d[["a", "b"]], True) - def test_pop(self): + def test_pop(self) -> None: d = KeylistDict() d["a"] = {} d["a"]["b"] = {} @@ -49,14 +49,14 @@ def test_pop(self): self.assertFalse(["a", "b", "c"] in d) self.assertTrue(["a", "b"] in d) - def test_set(self): + def test_set(self) -> None: # TODO pass - def test_setdefault(self): + def test_setdefault(self) -> None: # TODO pass - def test_setitem(self): + def test_setitem(self) -> None: # TODO pass diff --git a/tests/dicts/keylist/test_keylist_util.py b/tests/dicts/keylist/test_keylist_util.py index 6c1cde37..93a0343b 100644 --- a/tests/dicts/keylist/test_keylist_util.py +++ b/tests/dicts/keylist/test_keylist_util.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict.dicts.keylist import keylist_util @@ -8,7 +11,7 @@ class keylist_util_test_case(unittest.TestCase): This class describes a keylist utility test case. """ - def test_get_item_with_valid_keys(self): + def test_get_item_with_valid_keys(self) -> None: d = { "a": { "b": { @@ -47,8 +50,8 @@ def test_get_item_with_valid_keys(self): ), ) - def test_get_item_with_empty_dict(self): - d = {} + def test_get_item_with_empty_dict(self) -> None: + d: dict[str, Any] = {} item = keylist_util.get_item(d, ["a"]) self.assertEqual( @@ -80,8 +83,8 @@ def test_get_item_with_empty_dict(self): ), ) - def test_get_item_with_empty_keys(self): - d = {} + def test_get_item_with_empty_keys(self) -> None: + d: dict[str, Any] = {} item = keylist_util.get_item(d, []) self.assertEqual( @@ -93,8 +96,8 @@ def test_get_item_with_empty_keys(self): ), ) - def test_set_item_with_indexes(self): - d = {} + def test_set_item_with_indexes(self) -> None: + d: dict[str, Any] = {} keylist_util.set_item(d, "a", None) self.assertEqual(d, {"a": None}) diff --git a/tests/dicts/keypath/test_keypath_dict.py b/tests/dicts/keypath/test_keypath_dict.py index 9f314fd8..639cd1eb 100644 --- a/tests/dicts/keypath/test_keypath_dict.py +++ b/tests/dicts/keypath/test_keypath_dict.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict.dicts.keypath import KeypathDict @@ -8,7 +11,7 @@ class keypath_dict_test_case(unittest.TestCase): This class describes a KeypathDict test case. """ - def test_init_with_custom_separator(self): + def test_init_with_custom_separator(self) -> None: d = { "a.b": { "c.d": 1, @@ -25,7 +28,7 @@ def test_init_with_custom_separator(self): self.assertEqual(b.get("g.h/i.j"), 3) self.assertEqual(b.get("g.h/k.l"), 4) - def test_init_without_keypath_separator(self): + def test_init_without_keypath_separator(self) -> None: d = { "a": { "b": 1, @@ -42,7 +45,7 @@ def test_init_without_keypath_separator(self): self.assertEqual(b.get("d.e"), None) self.assertEqual(b.get("d.f"), None) - def test_init_with_dict_with_separator_in_keys(self): + def test_init_with_dict_with_separator_in_keys(self) -> None: d = { "a.b.c": 1, "c.d.e": 2, @@ -50,7 +53,7 @@ def test_init_with_dict_with_separator_in_keys(self): with self.assertRaises(ValueError): KeypathDict(d) - def test_update_with_dict_with_separator_in_keys(self): + def test_update_with_dict_with_separator_in_keys(self) -> None: d1 = { "a": 1, "b": 2, @@ -65,7 +68,7 @@ def test_update_with_dict_with_separator_in_keys(self): with self.assertRaises(ValueError): b.update(d2) - def test_update_with_dict_without_separator_in_keys(self): + def test_update_with_dict_without_separator_in_keys(self) -> None: d1 = { "a": 1, "b": 2, @@ -85,7 +88,7 @@ def test_update_with_dict_without_separator_in_keys(self): self.assertEqual(b.get("a.y"), 5) self.assertEqual(b.get("a.z"), 6) - def test_fromkeys(self): + def test_fromkeys(self) -> None: k = [ "a", "a.b", @@ -112,7 +115,7 @@ def test_fromkeys(self): } self.assertEqual(b, r) - def test_fromkeys_with_value(self): + def test_fromkeys_with_value(self) -> None: k = [ "a", "a.b", @@ -139,7 +142,7 @@ def test_fromkeys_with_value(self): } self.assertEqual(b, r) - def test_get_with_1_valid_key(self): + def test_get_with_1_valid_key(self) -> None: d = { "a": 1, 1: 1, @@ -148,14 +151,14 @@ def test_get_with_1_valid_key(self): self.assertEqual(b.get("a", 2), 1) self.assertEqual(b.get(1, 2), 1) - def test_get_with_1_invalid_key(self): + def test_get_with_1_invalid_key(self) -> None: d = { "a": 1, } b = KeypathDict(d) self.assertEqual(b.get("b", 2), 2) - def test_get_with_1_not_str_key(self): + def test_get_with_1_not_str_key(self) -> None: d = { None: None, False: False, @@ -166,14 +169,14 @@ def test_get_with_1_not_str_key(self): self.assertEqual(b.get(True, True), True) self.assertEqual(b.get(0, 1), 0) - def test_getitem_with_1_valid_key(self): + def test_getitem_with_1_valid_key(self) -> None: d = { "a": 1, } b = KeypathDict(d) self.assertEqual(b["a"], 1) - def test_getitem_with_1_invalid_key(self): + def test_getitem_with_1_invalid_key(self) -> None: d = { "a": 1, } @@ -181,7 +184,7 @@ def test_getitem_with_1_invalid_key(self): with self.assertRaises(KeyError): _ = b["b"] - def test_getitem_with_1_not_str_key(self): + def test_getitem_with_1_not_str_key(self) -> None: d = { None: None, False: False, @@ -195,7 +198,7 @@ def test_getitem_with_1_not_str_key(self): self.assertEqual(b[0], 0) - def test_get_with_2_valid_keys(self): + def test_get_with_2_valid_keys(self) -> None: d = { "a": { "b": 1, @@ -204,7 +207,7 @@ def test_get_with_2_valid_keys(self): b = KeypathDict(d) self.assertEqual(b.get("a.b", 2), 1) - def test_get_with_2_invalid_keys(self): + def test_get_with_2_invalid_keys(self) -> None: d = { "a": { "b": 1, @@ -213,7 +216,7 @@ def test_get_with_2_invalid_keys(self): b = KeypathDict(d) self.assertEqual(b.get("b.a", 2), 2) - def test_getitem_with_2_valid_keys(self): + def test_getitem_with_2_valid_keys(self) -> None: d = { "a": { "b": 1, @@ -222,7 +225,7 @@ def test_getitem_with_2_valid_keys(self): b = KeypathDict(d) self.assertEqual(b["a.b"], 1) - def test_getitem_with_2_invalid_keys(self): + def test_getitem_with_2_invalid_keys(self) -> None: d = { "a": { "b": 1, @@ -232,7 +235,7 @@ def test_getitem_with_2_invalid_keys(self): with self.assertRaises(KeyError): _ = b["b.a"] - def test_get_with_3_valid_keys(self): + def test_get_with_3_valid_keys(self) -> None: d = { "a": { "b": { @@ -243,7 +246,7 @@ def test_get_with_3_valid_keys(self): b = KeypathDict(d) self.assertEqual(b.get("a.b.c", 2), 1) - def test_get_with_3_invalid_keys(self): + def test_get_with_3_invalid_keys(self) -> None: d = { "a": { "b": { @@ -254,14 +257,14 @@ def test_get_with_3_invalid_keys(self): b = KeypathDict(d) self.assertEqual(b.get("c.b.a", 2), 2) - def test_get_with_empty_keys_list(self): + def test_get_with_empty_keys_list(self) -> None: d = { "a": 1, } b = KeypathDict(d) self.assertEqual(b.get([]), None) - def test_get_with_keys_list(self): + def test_get_with_keys_list(self) -> None: d = { "a": { "b": { @@ -286,7 +289,7 @@ def test_get_with_keys_list(self): self.assertEqual(b.get(["a", "b", "d"]), 2) self.assertEqual(b.get(["a", "b", "e"]), None) - def test_get_with_keys_list_and_no_keypath_separator(self): + def test_get_with_keys_list_and_no_keypath_separator(self) -> None: d = { "a": { "b": { @@ -301,7 +304,7 @@ def test_get_with_keys_list_and_no_keypath_separator(self): self.assertEqual(b.get(["a", "b", "d"]), 2) self.assertEqual(b.get(["a", "b", "e"]), None) - def test_getitem_with_3_valid_keys(self): + def test_getitem_with_3_valid_keys(self) -> None: d = { "a": { "b": { @@ -312,7 +315,7 @@ def test_getitem_with_3_valid_keys(self): b = KeypathDict(d) self.assertEqual(b["a.b.c"], 1) - def test_getitem_with_3_invalid_keys(self): + def test_getitem_with_3_invalid_keys(self) -> None: d = { "a": { "b": { @@ -324,7 +327,7 @@ def test_getitem_with_3_invalid_keys(self): with self.assertRaises(KeyError): _ = b["c.b.a"] - def test_get_item_with_keys_list(self): + def test_get_item_with_keys_list(self) -> None: d = { "a": { "b": { @@ -340,7 +343,7 @@ def test_get_item_with_keys_list(self): with self.assertRaises(KeyError): _ = b[["a", "b", "e"]] - def test_get_item_with_keys_list_and_no_keypath_separator(self): + def test_get_item_with_keys_list_and_no_keypath_separator(self) -> None: d = { "a": { "b": { @@ -358,8 +361,8 @@ def test_get_item_with_keys_list_and_no_keypath_separator(self): with self.assertRaises(KeyError): _ = b[["a", "b", "e"]] - def test_has_with_1_key(self): - d = { + def test_has_with_1_key(self) -> None: + d: dict[str, Any] = { "a": 0, "b": None, "c": {}, @@ -370,8 +373,8 @@ def test_has_with_1_key(self): self.assertTrue("c" in b) self.assertFalse("d" in b) - def test_has_with_2_keys(self): - d = { + def test_has_with_2_keys(self) -> None: + d: dict[str, Any] = { "a": { "a": 0, "b": None, @@ -386,8 +389,8 @@ def test_has_with_2_keys(self): self.assertFalse("b" in b) self.assertFalse("b.a" in b) - def test_has_with_3_keys(self): - d = { + def test_has_with_3_keys(self) -> None: + d: dict[str, Any] = { "a": { "b": { "c": 0, @@ -417,7 +420,7 @@ def test_has_with_3_keys(self): self.assertTrue(3 in b["a.b.x"]) self.assertFalse(4 in b["a.b.x"]) - def test_has_with_keys_list(self): + def test_has_with_keys_list(self) -> None: d = { "a": { "b": { @@ -438,7 +441,7 @@ def test_has_with_keys_list(self): self.assertTrue(["a", "b", "x", 2] in b) self.assertFalse(["a", "b", "x", 3] in b) - def test_has_with_keys_list_and_no_keypath_separator(self): + def test_has_with_keys_list_and_no_keypath_separator(self) -> None: d = { "a": { "b": { @@ -453,7 +456,7 @@ def test_has_with_keys_list_and_no_keypath_separator(self): self.assertTrue(["a", "b", "d"] in b) self.assertFalse(["a", "b", "e"] in b) - def test_keypath_separator_getter_setter(self): + def test_keypath_separator_getter_setter(self) -> None: d = KeypathDict({}, keypath_separator=None) self.assertEqual(d.keypath_separator, None) d["a.b.c"] = 1 @@ -472,11 +475,11 @@ def test_keypath_separator_getter_setter(self): } self.assertEqual(d, r) - def test_set_override_existing_item(self): - d = {} + def test_set_override_existing_item(self) -> None: + d: dict[str, Any] = {} b = KeypathDict(d) b.set("a.b.c", 1) - r = {"a": {"b": {"c": 1}}} + r: dict[str, Any] = {"a": {"b": {"c": 1}}} b.set("a.b.c", 2) r = {"a": {"b": {"c": 2}}} self.assertEqual(b, r) @@ -484,11 +487,11 @@ def test_set_override_existing_item(self): r = {"a": {"b": {"c": {"d": 3}}}} self.assertEqual(b, r) - def test_setitem_override_existing_item(self): - d = {} + def test_setitem_override_existing_item(self) -> None: + d: dict[str, Any] = {} b = KeypathDict(d) b["a.b.c"] = 1 - r = {"a": {"b": {"c": 1}}} + r: dict[str, Any] = {"a": {"b": {"c": 1}}} b["a.b.c"] = 2 r = {"a": {"b": {"c": 2}}} self.assertEqual(b, r) @@ -496,8 +499,8 @@ def test_setitem_override_existing_item(self): r = {"a": {"b": {"c": {"d": 3}}}} self.assertEqual(b, r) - def test_setitem_with_keys_list(self): - d = { + def test_setitem_with_keys_list(self) -> None: + d: dict[str, Any] = { "a": { "b": { "c": 1, @@ -515,8 +518,8 @@ def test_setitem_with_keys_list(self): b[["a", "b", "e"]] = 5 self.assertEqual(b["a.b.e"], 5) - def test_setitem_with_keys_list_and_no_keypath_separator(self): - d = { + def test_setitem_with_keys_list_and_no_keypath_separator(self) -> None: + d: dict[str, Any] = { "a": { "b": { "c": 1, @@ -540,8 +543,8 @@ def test_setitem_with_keys_list_and_no_keypath_separator(self): _ = b["a.b.e"] self.assertEqual(b[["a", "b", "e"]], 5) - def test_setitem_with_dict_value_with_separator_in_keys(self): - d = { + def test_setitem_with_dict_value_with_separator_in_keys(self) -> None: + d: dict[str, Any] = { "a": { "b": { "c": 1, @@ -559,7 +562,7 @@ def test_setitem_with_dict_value_with_separator_in_keys(self): with self.assertRaises(ValueError): b["a.b.e"] = v - def test_delitem_with_1_valid_key(self): + def test_delitem_with_1_valid_key(self) -> None: d = { "a": 1, } @@ -569,7 +572,7 @@ def test_delitem_with_1_valid_key(self): del b["a"] self.assertEqual(b.get("a"), None) - def test_delitem_with_1_invalid_key(self): + def test_delitem_with_1_invalid_key(self) -> None: d = { "a": 1, } @@ -578,7 +581,7 @@ def test_delitem_with_1_invalid_key(self): del b["b"] self.assertEqual(b.get("b"), None) - def test_delitem_with_2_valid_keys(self): + def test_delitem_with_2_valid_keys(self) -> None: d = { "a": { "b": 1, @@ -596,7 +599,7 @@ def test_delitem_with_2_valid_keys(self): del b["a"] self.assertEqual(b.get("a"), None) - def test_delitem_with_2_invalid_keys(self): + def test_delitem_with_2_invalid_keys(self) -> None: d = { "a": { "b": 1, @@ -607,7 +610,7 @@ def test_delitem_with_2_invalid_keys(self): del b["a.c"] self.assertEqual(b.get("a"), {"b": 1}) - def test_delitem_with_3_valid_keys(self): + def test_delitem_with_3_valid_keys(self) -> None: d = { "a": { "b": { @@ -633,7 +636,7 @@ def test_delitem_with_3_valid_keys(self): del b["a.b"] self.assertEqual(b.get("a.b"), None) - def test_delitem_with_3_invalid_keys(self): + def test_delitem_with_3_invalid_keys(self) -> None: d = { "a": { "b": { @@ -647,7 +650,7 @@ def test_delitem_with_3_invalid_keys(self): del b["a.b.c.d"] self.assertEqual(b.get("a.b.c"), 1) - def test_delitem_with_keys_list(self): + def test_delitem_with_keys_list(self) -> None: d = { "a": { "b": { @@ -662,7 +665,7 @@ def test_delitem_with_keys_list(self): del b[["a", "b", "c"]] self.assertEqual(b.get("a.b.c", 3), 3) - def test_delitem_with_keys_list_and_no_keypath_separator(self): + def test_delitem_with_keys_list_and_no_keypath_separator(self) -> None: d = { "a": { "b": { @@ -677,7 +680,7 @@ def test_delitem_with_keys_list_and_no_keypath_separator(self): del b[["a", "b", "c"]] self.assertEqual(b.get("a.b.c", 3), 3) - def test_pop_default(self): + def test_pop_default(self) -> None: d = { "a": 1, } @@ -689,7 +692,7 @@ def test_pop_default(self): val = b.pop("c", 3) self.assertEqual(val, 3) - def test_pop_with_1_valid_key(self): + def test_pop_with_1_valid_key(self) -> None: d = { "a": 1, } @@ -697,7 +700,7 @@ def test_pop_with_1_valid_key(self): val = b.pop("a") self.assertEqual(val, 1) - def test_pop_with_1_invalid_key(self): + def test_pop_with_1_invalid_key(self) -> None: d = { "a": 1, } @@ -709,7 +712,7 @@ def test_pop_with_1_invalid_key(self): val = b.pop("b", None) self.assertEqual(val, None) - def test_pop_with_2_valid_keys(self): + def test_pop_with_2_valid_keys(self) -> None: d = { "a": { "b": 1, @@ -727,7 +730,7 @@ def test_pop_with_2_valid_keys(self): _ = b.pop("a") self.assertEqual(val, {}) - def test_pop_with_2_invalid_keys(self): + def test_pop_with_2_invalid_keys(self) -> None: d = { "a": { "b": 1, @@ -746,7 +749,7 @@ def test_pop_with_2_invalid_keys(self): with self.assertRaises(KeyError): _ = b.pop("x.y") - def test_pop_with_keys_list(self): + def test_pop_with_keys_list(self) -> None: d = { "a": { "b": 1, @@ -768,7 +771,7 @@ def test_pop_with_keys_list(self): val = b.pop(["a", "b"]) self.assertEqual(val, 1) - def test_pop_with_keys_list_and_no_keypath_separator(self): + def test_pop_with_keys_list_and_no_keypath_separator(self) -> None: d = { "a": { "b": 1, @@ -790,7 +793,7 @@ def test_pop_with_keys_list_and_no_keypath_separator(self): val = b.pop(["a", "b"]) self.assertEqual(val, 1) - def test_setdefault_with_1_key(self): + def test_setdefault_with_1_key(self) -> None: d = { "a": None, "b": 0, @@ -806,7 +809,7 @@ def test_setdefault_with_1_key(self): self.assertEqual(b["c"], 1) self.assertEqual(b["d"], 2) - def test_setdefault_with_2_keys(self): + def test_setdefault_with_2_keys(self) -> None: d = { "x": { "a": None, @@ -824,7 +827,7 @@ def test_setdefault_with_2_keys(self): self.assertEqual(b["x.c"], 1) self.assertEqual(b["x.d"], 2) - def test_setdefault_with_3_keys(self): + def test_setdefault_with_3_keys(self) -> None: d = { "y": { "z": { diff --git a/tests/dicts/keypath/test_keypath_dict_list_indexes.py b/tests/dicts/keypath/test_keypath_dict_list_indexes.py index ffaa4eee..282bea00 100644 --- a/tests/dicts/keypath/test_keypath_dict_list_indexes.py +++ b/tests/dicts/keypath/test_keypath_dict_list_indexes.py @@ -8,7 +8,7 @@ class keypath_dict_list_indexes_test_case(unittest.TestCase): This class describes a KeypathDict list indexes test case. """ - def test_contains_with_flat_list(self): + def test_contains_with_flat_list(self) -> None: d = { "a": [1, 2, 3], } @@ -18,7 +18,7 @@ def test_contains_with_flat_list(self): self.assertTrue("a[2]" in b) self.assertTrue("a[-1]" in b) - def test_contains_with_wrong_index(self): + def test_contains_with_wrong_index(self) -> None: d = { "a": [1, 2, 3], } @@ -26,7 +26,7 @@ def test_contains_with_wrong_index(self): self.assertFalse("a[3]" in b) self.assertFalse("a[]" in b) - def test_contains_with_nested_list(self): + def test_contains_with_nested_list(self) -> None: d = { "a": { "b": [ @@ -55,7 +55,7 @@ def test_contains_with_nested_list(self): self.assertTrue("a.b[1]" in b) self.assertFalse("a.b[2]" in b) - def test_delitem_with_flat_list(self): + def test_delitem_with_flat_list(self) -> None: d = { "a": [1, 2, 3], } @@ -65,7 +65,7 @@ def test_delitem_with_flat_list(self): del b["a[0]"] self.assertEqual(b, {"a": [2]}) - def test_delitem_with_wrong_index(self): + def test_delitem_with_wrong_index(self) -> None: d = { "a": [1, 2, 3], } @@ -73,7 +73,7 @@ def test_delitem_with_wrong_index(self): with self.assertRaises(KeyError): del b["a[5]"] - def test_delitem_with_nested_list(self): + def test_delitem_with_nested_list(self) -> None: d = { "a": { "b": [ @@ -105,7 +105,7 @@ def test_delitem_with_nested_list(self): }, ) - def test_getitem_with_flat_list(self): + def test_getitem_with_flat_list(self) -> None: d = { "a": [1, 2, 3], } @@ -119,7 +119,7 @@ def test_getitem_with_flat_list(self): self.assertEqual(b["a[-2]"], 2) self.assertEqual(b["a[-3]"], 1) - def test_getitem_with_wrong_index(self): + def test_getitem_with_wrong_index(self) -> None: d = { "a": [1, 2, 3], } @@ -127,7 +127,7 @@ def test_getitem_with_wrong_index(self): with self.assertRaises(KeyError): self.assertEqual(b["a[5]"], 1) - def test_getitem_with_nested_list(self): + def test_getitem_with_nested_list(self) -> None: d = { "a": { "b": [ @@ -181,7 +181,7 @@ def test_getitem_with_nested_list(self): self.assertEqual(b["a.b[2].d[2]"], 9) self.assertEqual(b["a.b[2].d[3][0]"], 0) - def test_getitem_github_issue_feature_request(self): + def test_getitem_github_issue_feature_request(self) -> None: d = { "products": [ { @@ -199,7 +199,7 @@ def test_getitem_github_issue_feature_request(self): b = KeypathDict(d) self.assertEqual(b['products[""0""].categories[1].name'], "OK 2") - def test_get_with_flat_list(self): + def test_get_with_flat_list(self) -> None: d = { "a": [1, 2, 3], } @@ -213,14 +213,14 @@ def test_get_with_flat_list(self): self.assertEqual(b.get("a[-2]"), 2) self.assertEqual(b.get("a[-3]"), 1) - def test_get_with_wrong_index(self): + def test_get_with_wrong_index(self) -> None: d = { "a": [1, 2, 3], } b = KeypathDict(d) self.assertEqual(b.get("a[3]", 1), 1) - def test_get_with_nested_list(self): + def test_get_with_nested_list(self) -> None: d = { "a": { "b": [ @@ -262,7 +262,7 @@ def test_get_with_nested_list(self): self.assertEqual(b.get("a.b[2].d[2]"), 9) self.assertEqual(b.get("a.b[2].d[3][0]"), 0) - def test_list_indexes_with_quotes(self): + def test_list_indexes_with_quotes(self) -> None: d = { "a": [1, 2, 3], } @@ -276,7 +276,7 @@ def test_list_indexes_with_quotes(self): self.assertEqual(b.get('a[""]'), None) self.assertEqual(b.get("a['']"), None) - def test_pop_with_flat_list(self): + def test_pop_with_flat_list(self) -> None: d = { "a": [1, 2, 3], } @@ -286,7 +286,7 @@ def test_pop_with_flat_list(self): self.assertEqual(b.pop("a[0]"), 1) self.assertEqual(b.pop("a"), [2]) - def test_pop_with_flat_list_and_default(self): + def test_pop_with_flat_list_and_default(self) -> None: d = { "a": [1, 2, 3], } @@ -296,7 +296,7 @@ def test_pop_with_flat_list_and_default(self): b.pop("a[3]") self.assertEqual(b.pop("a[3]", 4), 4) - def test_pop_with_wrong_index(self): + def test_pop_with_wrong_index(self) -> None: d = { "a": [1, 2, 3], } @@ -304,14 +304,14 @@ def test_pop_with_wrong_index(self): with self.assertRaises(KeyError): b.pop("a[5]") - def test_pop_with_wrong_index_and_default(self): + def test_pop_with_wrong_index_and_default(self) -> None: d = { "a": [1, 2, 3], } b = KeypathDict(d) self.assertEqual(b.pop("a[5]", 6), 6) - def test_pop_with_nested_list(self): + def test_pop_with_nested_list(self) -> None: d = { "a": { "b": [ diff --git a/tests/dicts/keypath/test_keypath_util.py b/tests/dicts/keypath/test_keypath_util.py index cd6c2654..72958d2a 100644 --- a/tests/dicts/keypath/test_keypath_util.py +++ b/tests/dicts/keypath/test_keypath_util.py @@ -8,7 +8,7 @@ class keypath_util_test_case(unittest.TestCase): This class describes a keypath utility test case. """ - def test_split_key_indexes_with_valid_indexes(self): + def test_split_key_indexes_with_valid_indexes(self) -> None: f = keypath_util._split_key_indexes self.assertEqual(f("item[0]"), ["item", 0]) self.assertEqual(f("item[1]"), ["item", 1]) @@ -24,7 +24,7 @@ def test_split_key_indexes_with_valid_indexes(self): self.assertEqual(f('item["0"]["-1"]'), ["item", 0, -1]) self.assertEqual(f('item["0"]["-1"]'), ["item", 0, -1]) - def test_split_key_indexes_with_invalid_indexes(self): + def test_split_key_indexes_with_invalid_indexes(self) -> None: f = keypath_util._split_key_indexes self.assertEqual(f("item[]"), ["item[]"]) self.assertEqual(f("item[*]"), ["item[*]"]) diff --git a/tests/dicts/parse/test_parse_dict.py b/tests/dicts/parse/test_parse_dict.py index 3157bb33..ec59858e 100644 --- a/tests/dicts/parse/test_parse_dict.py +++ b/tests/dicts/parse/test_parse_dict.py @@ -12,7 +12,7 @@ class parse_dict_test_case(unittest.TestCase): This class describes a ParseDict test case. """ - def test_get_bool_default(self): + def test_get_bool_default(self) -> None: d = { "n": None, } @@ -22,7 +22,7 @@ def test_get_bool_default(self): self.assertTrue(b.get_bool("d1", True)) self.assertFalse(b.get_bool("d2", False)) - def test_get_bool_with_bool_values(self): + def test_get_bool_with_bool_values(self) -> None: d = { "b1": True, "b2": False, @@ -31,7 +31,7 @@ def test_get_bool_with_bool_values(self): self.assertTrue(b.get_bool("b1")) self.assertFalse(b.get_bool("b2")) - def test_get_bool_with_int_values(self): + def test_get_bool_with_int_values(self) -> None: d = { "i0": 0, "i1": 1, @@ -43,7 +43,7 @@ def test_get_bool_with_int_values(self): self.assertTrue(b.get_bool("i2", True)) self.assertFalse(b.get_bool("i2", False)) - def test_get_bool_with_str_values(self): + def test_get_bool_with_str_values(self) -> None: d = { "t1": "1", "t2": "YES", @@ -60,7 +60,7 @@ def test_get_bool_with_str_values(self): self.assertFalse(b.get_bool("f2")) self.assertFalse(b.get_bool("f3")) - def test_get_bool_list(self): + def test_get_bool_list(self) -> None: d = { "a": "1,YES,True,0,NO,False,XXX", "b": "1;YES;True;0;NO;False;XXX", @@ -88,7 +88,7 @@ def test_get_bool_list(self): ) self.assertEqual(b.get_bool_list("d", default=[False]), [False]) - def test_get_date_default(self): + def test_get_date_default(self) -> None: today = datetime.now().date() d = { "a": None, @@ -97,7 +97,7 @@ def test_get_date_default(self): self.assertEqual(b.get_date("a", today), today) self.assertEqual(b.get_date("b", today), today) - def test_get_date_with_date_value(self): + def test_get_date_with_date_value(self) -> None: today = datetime.now().date() d = { "a": today, @@ -105,7 +105,7 @@ def test_get_date_with_date_value(self): b = ParseDict(d) self.assertEqual(b.get_date("a"), today) - def test_get_date_list(self): + def test_get_date_list(self) -> None: d = { "a": ["2019-05-01", "2018-12-31", "Hello World"], "b": "2019-05-01,2018-12-31", @@ -120,7 +120,7 @@ def test_get_date_list(self): [datetime(2019, 5, 1).date(), datetime(2018, 12, 31).date()], ) - def test_get_datetime_default(self): + def test_get_datetime_default(self) -> None: now = datetime.now() d = { "a": None, @@ -129,7 +129,7 @@ def test_get_datetime_default(self): self.assertEqual(b.get_datetime("a", now), now) self.assertEqual(b.get_datetime("b", now), now) - def test_get_datetime_with_datetime_value(self): + def test_get_datetime_with_datetime_value(self) -> None: now = datetime.now() d = { "a": now, @@ -137,7 +137,7 @@ def test_get_datetime_with_datetime_value(self): b = ParseDict(d) self.assertEqual(b.get_datetime("a"), now) - def test_get_datetime_with_timestamp_int(self): + def test_get_datetime_with_timestamp_int(self) -> None: now = datetime.now() ts = datetime.timestamp(now) d = { @@ -146,7 +146,7 @@ def test_get_datetime_with_timestamp_int(self): b = ParseDict(d) self.assertEqual(b.get_datetime("a"), datetime.fromtimestamp(ts)) - def test_get_datetime_with_timestamp_string(self): + def test_get_datetime_with_timestamp_string(self) -> None: now = datetime.now() ts = datetime.timestamp(now) d = { @@ -155,7 +155,7 @@ def test_get_datetime_with_timestamp_string(self): b = ParseDict(d) self.assertEqual(b.get_datetime("a"), datetime.fromtimestamp(ts)) - def test_get_datetime_with_valid_format(self): + def test_get_datetime_with_valid_format(self) -> None: d = { "a": "2019-05-01", } @@ -163,7 +163,7 @@ def test_get_datetime_with_valid_format(self): r = datetime(2019, 5, 1, 0, 0) self.assertEqual(b.get_datetime("a", format="%Y-%m-%d"), r) - def test_get_datetime_with_invalid_format(self): + def test_get_datetime_with_invalid_format(self) -> None: now = datetime.now() d = { "a": "2019-05-01", @@ -179,7 +179,7 @@ def test_get_datetime_with_invalid_format(self): now, ) - def test_get_datetime_without_format(self): + def test_get_datetime_without_format(self) -> None: d = { "a": "2019-05-01", } @@ -188,7 +188,7 @@ def test_get_datetime_without_format(self): self.assertEqual(b.get_datetime("a"), r) @patch("benedict.dicts.parse.parse_util.parse_installed", False) - def test_get_datetime_with_with_extra_not_installed(self): + def test_get_datetime_with_with_extra_not_installed(self) -> None: with self.assertRaises(ExtrasRequireModuleNotFoundError): d = { "a": "2019-05-01", @@ -197,7 +197,7 @@ def test_get_datetime_with_with_extra_not_installed(self): r = datetime(2019, 5, 1, 0, 0) self.assertEqual(b.get_datetime("a", format="%Y-%m-%d"), r) - def test_get_datetime_list(self): + def test_get_datetime_list(self) -> None: d = { "a": ["2019-05-01", "2018-12-31", "Hello World"], "b": "2019-05-01,2018-12-31", @@ -212,7 +212,7 @@ def test_get_datetime_list(self): [datetime(2019, 5, 1, 0, 0), datetime(2018, 12, 31, 0, 0)], ) - def test_get_decimal(self): + def test_get_decimal(self) -> None: d = { "a": 1, "b": True, @@ -224,7 +224,7 @@ def test_get_decimal(self): self.assertEqual(b.get_decimal("b", Decimal("2.5")), Decimal("2.5")) self.assertEqual(b.get_decimal("c"), Decimal("4.25")) - def test_get_decimal_with_choices(self): + def test_get_decimal_with_choices(self) -> None: d = { "a": Decimal("0.25"), "b": Decimal("0.35"), @@ -240,7 +240,7 @@ def test_get_decimal_with_choices(self): self.assertEqual(b.get_decimal("a", Decimal("0.5"), choices=o), Decimal("0.25")) self.assertEqual(b.get_decimal("b", Decimal("0.5"), choices=o), Decimal("0.5")) - def test_get_decimal_list(self): + def test_get_decimal_list(self) -> None: d = { "a": ["0.0", "0.5", "1.0", "Hello World"], "b": "0.0,0.5,1.0", @@ -254,7 +254,7 @@ def test_get_decimal_list(self): b.get_decimal_list("b"), [Decimal("0.0"), Decimal("0.5"), Decimal("1.0")] ) - def test_get_dict(self): + def test_get_dict(self) -> None: d = { "a": {"x": 1, "y": 2}, "b": {}, @@ -266,7 +266,7 @@ def test_get_dict(self): self.assertEqual(b.get_dict("c"), {}) self.assertEqual(b.get_dict("c", {"default": True}), {"default": True}) - def test_get_dict_from_json(self): + def test_get_dict_from_json(self) -> None: d = { "a": '{"numbers": ["0", "1", "2", "3", "4"], "letters": ["a", "b", "c", "d", "e"]}', "b": '["0", "1", "2", "3", "4"]', @@ -291,7 +291,7 @@ def test_get_dict_from_json(self): self.assertEqual(b.get_dict("f"), {}) self.assertEqual(b.get_dict("g", {"default": True}), {"default": True}) - def test_get_float(self): + def test_get_float(self) -> None: d = { "a": 1.0, "b": True, @@ -303,7 +303,7 @@ def test_get_float(self): self.assertEqual(b.get_float("b", float(2.5)), float(2.5)) self.assertEqual(b.get_float("c"), float(4.25)) - def test_get_float_with_choices(self): + def test_get_float_with_choices(self) -> None: d = { "a": float(0.25), "b": float(0.35), @@ -313,7 +313,7 @@ def test_get_float_with_choices(self): self.assertEqual(b.get_float("a", float(0.5), choices=o), float(0.25)) self.assertEqual(b.get_float("b", float(0.5), choices=o), float(0.5)) - def test_get_float_list(self): + def test_get_float_list(self) -> None: d = { "a": ["0.0", "0.5", "1.0", "Hello World"], "b": "0.0,0.5,1.0", @@ -322,7 +322,7 @@ def test_get_float_list(self): self.assertEqual(b.get_float_list("a"), [0.0, 0.5, 1.0, None]) self.assertEqual(b.get_float_list("b"), [0.0, 0.5, 1.0]) - def test_get_email(self): + def test_get_email(self) -> None: d = { "a": "fabio@caccamo.com", "b": "fabio@@caccamo.com", @@ -348,7 +348,7 @@ def test_get_email(self): self.assertEqual(b.get_email("e"), "") @patch("benedict.dicts.parse.parse_util.parse_installed", False) - def test_get_email_with_extra_not_installed(self): + def test_get_email_with_extra_not_installed(self) -> None: with self.assertRaises(ExtrasRequireModuleNotFoundError): d = { "a": "fabio@caccamo.com", @@ -356,7 +356,7 @@ def test_get_email_with_extra_not_installed(self): b = ParseDict(d) b.get_email("a") - def test_get_int(self): + def test_get_int(self) -> None: d = { "a": 1, "b": None, @@ -376,7 +376,7 @@ def test_get_int(self): self.assertEqual(b.get_int("f", 2), 3) self.assertEqual(b.get_int("g", 2), 2) - def test_get_int_with_choices(self): + def test_get_int_with_choices(self) -> None: d = { "a": 25, "b": 35, @@ -386,7 +386,7 @@ def test_get_int_with_choices(self): self.assertEqual(b.get_int("a", 50, choices=o), 25) self.assertEqual(b.get_int("b", 50, choices=o), 50) - def test_get_int_list(self): + def test_get_int_list(self) -> None: d = { "a": ["0", "1", "2", "Hello World"], "b": "0,1,2", @@ -403,7 +403,7 @@ def test_get_int_list(self): self.assertEqual(b.get_int_list("e"), []) self.assertEqual(b.get_int_list("f"), []) - def test_get_list(self): + def test_get_list(self) -> None: d = { "a": (0, 1, 2, 3), "b": [0, 1, 2, 3], @@ -422,7 +422,7 @@ def test_get_list(self): self.assertEqual(b.get_list("f"), []) self.assertEqual(b.get_list("g", [0]), [0]) - def test_get_list_from_json(self): + def test_get_list_from_json(self) -> None: d = { "a": '{"numbers": ["0", "1", "2", "3", "4"], "letters": ["a", "b", "c", "d", "e"]}', "b": '["0", "1", "2", "3", "4"]', @@ -435,7 +435,7 @@ def test_get_list_from_json(self): self.assertEqual(b.get_list("c", [0]), []) self.assertEqual(b.get_list("d", [0]), [0]) - def test_get_list_with_separator(self): + def test_get_list_with_separator(self) -> None: d = { "a": "0,1,2,3,4", "b": "5|6|7|8|9", @@ -449,7 +449,7 @@ def test_get_list_with_separator(self): self.assertEqual(b.get_list("d", separator=","), []) self.assertEqual(b.get_list("e", [0], separator=","), [0]) - def test_get_list_item(self): + def test_get_list_item(self) -> None: d = { "a": (1, 2, 3, 4, 5), "b": [6, 7, 8, 9, 0], @@ -466,7 +466,7 @@ def test_get_list_item(self): self.assertEqual(b.get_list_item("b", index=10), None) self.assertEqual(b.get_list_item("c", index=1), None) - def test_get_phonenumber(self): + def test_get_phonenumber(self) -> None: d = { "b": " (0039) 3334445566 ", # valid phone number with 00 prefix "c": "+393334445566 ", # valid phone number with + prefix @@ -526,7 +526,7 @@ def test_get_phonenumber(self): self.assertEqual(p, {}) @patch("benedict.dicts.parse.parse_util.parse_installed", False) - def test_get_phonenumber_with_extra_not_installed(self): + def test_get_phonenumber_with_extra_not_installed(self) -> None: with self.assertRaises(ExtrasRequireModuleNotFoundError): d = { "a": "3334445566", @@ -534,7 +534,7 @@ def test_get_phonenumber_with_extra_not_installed(self): b = ParseDict(d) b.get_phonenumber("a") - def test_get_slug(self): + def test_get_slug(self) -> None: d = { "a": " Hello World ", "b": 1, @@ -544,7 +544,7 @@ def test_get_slug(self): self.assertEqual(b.get_slug("b", "none"), "1") self.assertEqual(b.get_slug("c", "none"), "none") - def test_get_slug_with_choices(self): + def test_get_slug_with_choices(self) -> None: d = { "a": "Sunday", "b": "Noneday", @@ -560,7 +560,7 @@ def test_get_slug_with_choices(self): "saturday", ) - def test_get_slug_list(self): + def test_get_slug_list(self) -> None: d = { "a": ["Hello World", " See you later ", 99.9], "b": "Hello World, See you later, 99.9", @@ -569,7 +569,7 @@ def test_get_slug_list(self): self.assertEqual(b.get_slug_list("a"), ["hello-world", "see-you-later", "99-9"]) self.assertEqual(b.get_slug_list("b"), ["hello-world", "see-you-later", "99-9"]) - def test_get_str(self): + def test_get_str(self) -> None: d = { "a": "Hello World", "b": "Hello World", @@ -581,7 +581,7 @@ def test_get_str(self): self.assertEqual(b.get_str("c"), "1") @patch("benedict.dicts.parse.parse_util.parse_installed", False) - def test_get_str_with_extra_not_installed(self): + def test_get_str_with_extra_not_installed(self) -> None: with self.assertRaises(ExtrasRequireModuleNotFoundError): d = { "a": "Hello World", @@ -589,7 +589,7 @@ def test_get_str_with_extra_not_installed(self): b = ParseDict(d) b.get_str("a") - def test_get_str_fix_encoding(self): + def test_get_str_fix_encoding(self) -> None: d = { "a": "Sexâ\x80\x99n Drug", "b": "Localit\xe0", @@ -598,7 +598,7 @@ def test_get_str_fix_encoding(self): self.assertEqual(b.get_str("a"), "Sex'n Drug") self.assertEqual(b.get_str("b"), "Località") - def test_get_str_list(self): + def test_get_str_list(self) -> None: d = { "a": ["Hello World", "See you later", 99.9], "b": "Hello World,See you later,99.9", @@ -607,7 +607,7 @@ def test_get_str_list(self): self.assertEqual(b.get_str_list("a"), ["Hello World", "See you later", "99.9"]) self.assertEqual(b.get_str_list("b"), ["Hello World", "See you later", "99.9"]) - def test_get_str_with_choices(self): + def test_get_str_with_choices(self) -> None: d = { "a": "Sunday", "b": "Noneday", @@ -623,7 +623,7 @@ def test_get_str_with_choices(self): "Saturday", ) - def test_get_uuid(self): + def test_get_uuid(self) -> None: d = { "a": "CA761232-ED42-11CE-BACD-00AA0057B223", "b": " CA761232-ED42-11CE-BACD-00AA0057B223 ", @@ -647,7 +647,7 @@ def test_get_uuid(self): self.assertEqual(b.get_uuid("h"), "") self.assertEqual(b.get_uuid("i"), "") - def test_get_uuid_list(self): + def test_get_uuid_list(self) -> None: d = { "a": [ "CA761232-ED42-11CE-BACD-00AA0057B223", diff --git a/tests/dicts/parse/test_parse_util.py b/tests/dicts/parse/test_parse_util.py index 49eac9dd..70d45f8d 100644 --- a/tests/dicts/parse/test_parse_util.py +++ b/tests/dicts/parse/test_parse_util.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict.dicts.parse import parse_util @@ -8,7 +11,7 @@ class parse_util_test_case(unittest.TestCase): This class describes a parse utility test case. """ - def test_parse_bool(self): + def test_parse_bool(self) -> None: f = parse_util.parse_bool self.assertTrue(f(1)) self.assertTrue(f(True)) @@ -26,44 +29,44 @@ def test_parse_bool(self): self.assertFalse(f("Ko")) self.assertFalse(f("Off")) - def test_parse_date(self): + def test_parse_date(self) -> None: # TODO pass - def test_parse_datetime(self): + def test_parse_datetime(self) -> None: # TODO pass - def test_parse_datetime_with_extra_not_installed(self): + def test_parse_datetime_with_extra_not_installed(self) -> None: # TODO pass - def test_parse_decimal(self): + def test_parse_decimal(self) -> None: # TODO pass - def test_parse_dict(self): + def test_parse_dict(self) -> None: # TODO pass - def test_parse_float(self): + def test_parse_float(self) -> None: # TODO pass - def test_parse_email(self): + def test_parse_email(self) -> None: # TODO pass - def test_parse_email_with_extra_not_installed(self): + def test_parse_email_with_extra_not_installed(self) -> None: # TODO pass - def test_parse_int(self): + def test_parse_int(self) -> None: # TODO pass - def test_parse_list(self): - def f(value): + def test_parse_list(self) -> None: + def f(value: Any) -> list[Any] | None: return parse_util.parse_list(value, separator=",") self.assertEqual( @@ -76,44 +79,44 @@ def f(value): self.assertEqual(f(""), None) self.assertEqual(f(None), None) - def test_parse_list_with_valid_json(self): - def f(value): + def test_parse_list_with_valid_json(self) -> None: + def f(value: Any) -> list[Any] | None: return parse_util.parse_list(value, separator=None) self.assertEqual(f("[0,1,2,3]"), [0, 1, 2, 3]) - def test_parse_list_with_invalid_json_with_separator(self): - def f(value): + def test_parse_list_with_invalid_json_with_separator(self) -> None: + def f(value: Any) -> list[Any] | None: return parse_util.parse_list(value, separator=",") self.assertEqual(f("[a,b,c]"), ["[a", "b", "c]"]) - def test_parse_list_with_invalid_json_without_separator(self): - def f(value): + def test_parse_list_with_invalid_json_without_separator(self) -> None: + def f(value: Any) -> list[Any] | None: return parse_util.parse_list(value, separator=None) self.assertEqual(f("[a,b,c]"), None) - def test_parse_phonenumber(self): + def test_parse_phonenumber(self) -> None: # TODO pass - def test_parse_phonenumber_with_extra_not_installed(self): + def test_parse_phonenumber_with_extra_not_installed(self) -> None: # TODO pass - def test_parse_slug(self): + def test_parse_slug(self) -> None: # TODO pass - def test_parse_str(self): + def test_parse_str(self) -> None: # TODO pass - def test_parse_str_with_extra_not_installed(self): + def test_parse_str_with_extra_not_installed(self) -> None: # TODO pass - def test_parse_uuid(self): + def test_parse_uuid(self) -> None: # TODO pass diff --git a/tests/dicts/test_benedict.py b/tests/dicts/test_benedict.py index 2dac0727..e526861d 100644 --- a/tests/dicts/test_benedict.py +++ b/tests/dicts/test_benedict.py @@ -1,7 +1,10 @@ +from __future__ import annotations + import re import unittest from datetime import datetime from decimal import Decimal +from typing import Any from benedict import benedict @@ -11,7 +14,7 @@ class benedict_test_case(unittest.TestCase): This class describes a benedict test case. """ - def test_clean(self): + def test_clean(self) -> None: d = { "a": {}, "b": {"x": 1}, @@ -55,7 +58,7 @@ def test_clean(self): } self.assertEqual(bd, r) - def test_clone(self): + def test_clone(self) -> None: d = { "a": { "b": { @@ -73,7 +76,7 @@ def test_clone(self): self.assertEqual(b["a"]["b"]["c"], 1) self.assertEqual(c["a"]["b"]["c"], 2) - def test_copy(self): + def test_copy(self) -> None: d = { "a": { "b": { @@ -91,7 +94,7 @@ def test_copy(self): self.assertEqual(b.get("a.b.c"), 2) self.assertEqual(c.get("a.b.c"), 2) - def test_copy_with_custom_keypath_separator(self): + def test_copy_with_custom_keypath_separator(self) -> None: d = { "a": { "b": { @@ -103,7 +106,7 @@ def test_copy_with_custom_keypath_separator(self): c = b.copy() self.assertEqual(c.keypath_separator, "/") - def test_deepcopy(self): + def test_deepcopy(self) -> None: d = { "a": { "b": { @@ -120,7 +123,7 @@ def test_deepcopy(self): self.assertEqual(b.get("a.b.c"), 1) self.assertEqual(c.get("a.b.c"), 2) - def test_deepcopy_with_custom_keypath_separator(self): + def test_deepcopy_with_custom_keypath_separator(self) -> None: d = { "a": { "b": { @@ -132,7 +135,7 @@ def test_deepcopy_with_custom_keypath_separator(self): c = b.deepcopy() self.assertEqual(c.keypath_separator, "/") - def test_deepupdate_with_single_dict(self): + def test_deepupdate_with_single_dict(self) -> None: d = { "a": 1, "b": 1, @@ -150,7 +153,7 @@ def test_deepupdate_with_single_dict(self): } self.assertEqual(bd, r) - def test_deepupdate_with_multiple_dicts(self): + def test_deepupdate_with_multiple_dicts(self) -> None: d = { "a": 1, "b": 1, @@ -180,7 +183,7 @@ def test_deepupdate_with_multiple_dicts(self): } self.assertEqual(bd, r) - def test_deepupdate(self): + def test_deepupdate(self) -> None: d = { "a": 1, "b": { @@ -245,7 +248,7 @@ def test_deepupdate(self): } self.assertEqual(bd, r) - def test_dump(self): + def test_dump(self) -> None: d = { "a": { "b": { @@ -266,7 +269,7 @@ def test_dump(self): output = b.dump() self.assertEqual(output, expected_output) - def test_dump_with_datetime(self): + def test_dump_with_datetime(self) -> None: d = { "datetime": datetime(2019, 6, 11), } @@ -277,7 +280,7 @@ def test_dump_with_datetime(self): output = b.dump() self.assertEqual(output, expected_output) - def test_dump_with_decimal(self): + def test_dump_with_decimal(self) -> None: d = { "decimal": Decimal("1.75"), } @@ -288,7 +291,7 @@ def test_dump_with_decimal(self): output = b.dump() self.assertEqual(output, expected_output) - def test_filter(self): + def test_filter(self) -> None: d = { "a": 1, "b": 2, @@ -299,7 +302,7 @@ def test_filter(self): } b = benedict(d) with self.assertRaises(ValueError): - f = b.filter(True) + f = b.filter(True) # type: ignore[arg-type] f = b.filter(lambda key, val: isinstance(val, int)) r = { "a": 1, @@ -312,7 +315,7 @@ def test_filter(self): self.assertEqual(f, r) self.assertFalse(b is f) - def test_filter_with_custom_keypath_separator(self): + def test_filter_with_custom_keypath_separator(self) -> None: d = { "a.b": 1, "b.c": 2, @@ -333,7 +336,7 @@ def test_filter_with_custom_keypath_separator(self): self.assertFalse(b is f) self.assertEqual(b.keypath_separator, f.keypath_separator) - def test_filter_with_parse(self): + def test_filter_with_parse(self) -> None: d = { "a": { "ok": "yes", @@ -370,7 +373,7 @@ def test_filter_with_parse(self): self.assertEqual(f, r) self.assertTrue(isinstance(f, benedict)) - def test_find(self): + def test_find(self) -> None: d = { "a": 1, "b": 2, @@ -385,7 +388,7 @@ def test_find(self): r = b.find(["x.y.z", "a.b.c", "c.d.e"]) self.assertEqual(r, 3) - def test_flatten(self): + def test_flatten(self) -> None: d = { "a": 1, "b": 2, @@ -412,7 +415,7 @@ def test_flatten(self): self.assertEqual(type(b), type(f)) self.assertTrue(isinstance(f, benedict)) - def test_flatten_with_custom_keypath_separator(self): + def test_flatten_with_custom_keypath_separator(self) -> None: d = { "a": 1, "b": 2, @@ -440,7 +443,7 @@ def test_flatten_with_custom_keypath_separator(self): self.assertTrue(isinstance(f, benedict)) self.assertEqual(b.keypath_separator, f.keypath_separator) - def test_flatten_with_custom_separator(self): + def test_flatten_with_custom_separator(self) -> None: d = { "a": 1, "b": 2, @@ -466,7 +469,7 @@ def test_flatten_with_custom_separator(self): self.assertEqual(f, r) self.assertFalse(b is f) - def test_flatten_with_key_conflict(self): + def test_flatten_with_key_conflict(self) -> None: d = { "a": 1, "b": 2, @@ -486,7 +489,7 @@ def test_flatten_with_key_conflict(self): # self.assertEqual(f, r) # self.assertFalse(b is f) - def test_fromkeys(self): + def test_fromkeys(self) -> None: k = [ "a", "a.b", @@ -514,7 +517,7 @@ def test_fromkeys(self): self.assertEqual(b, r) self.assertEqual(type(b), benedict) - def test_fromkeys_with_value(self): + def test_fromkeys_with_value(self) -> None: k = [ "a", "a.b", @@ -542,7 +545,7 @@ def test_fromkeys_with_value(self): self.assertEqual(b, r) self.assertEqual(type(b), benedict) - def test_from_base64(self): + def test_from_base64(self) -> None: j = "eyJhIjogMSwgImIiOiAyLCAiYyI6IDN9" # static method d = benedict.from_base64(j) @@ -589,7 +592,7 @@ def test_from_base64(self): }, ) - def test_from_csv_with_valid_data(self): + def test_from_csv_with_valid_data(self) -> None: s = """id,name,age,height,weight 1,Alice,20,62,120.6 2,Freddie,21,74,190.6 @@ -637,7 +640,7 @@ def test_from_csv_with_valid_data(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, r) - def test_from_json(self): + def test_from_json(self) -> None: j = '{"a": 1, "b": 2, "c": 3}' # static method d = benedict.from_json(j) @@ -662,7 +665,7 @@ def test_from_json(self): }, ) - def test_from_query_string_with_valid_data(self): + def test_from_query_string_with_valid_data(self) -> None: s = "ok=1&test=2&page=3&lib=python%20benedict&author=Fabio+Caccamo&author=Fabio%20Caccamo" r = { "ok": "1", @@ -680,7 +683,7 @@ def test_from_query_string_with_valid_data(self): self.assertTrue(isinstance(d, benedict)) self.assertEqual(d, r) - def test_from_toml(self): + def test_from_toml(self) -> None: j = """ a = 1 @@ -715,7 +718,7 @@ def test_from_toml(self): }, ) - def test_from_yaml_with_keypath_separator_in_keys(self): + def test_from_yaml_with_keypath_separator_in_keys(self) -> None: # fix: https://github.com/fabiocaccamo/python-benedict/issues/12 j = """ 192.168.0.1: @@ -769,7 +772,7 @@ def test_from_yaml_with_keypath_separator_in_keys(self): self.assertTrue(isinstance(d, dict)) self.assertEqual(d, r) - def test_from_xml(self): + def test_from_xml(self) -> None: j = """ @@ -807,7 +810,7 @@ def test_from_xml(self): }, ) - def test_from_yaml(self): + def test_from_yaml(self) -> None: j = """ a: 1 b: @@ -841,7 +844,7 @@ def test_from_yaml(self): }, ) - def test_get(self): + def test_get(self) -> None: d = { "a": 1, "b": { @@ -859,7 +862,7 @@ def test_get(self): # bb = b.get('b') # self.assertTrue(isinstance(bb.get('d'), benedict)) - def test_get_item(self): + def test_get_item(self) -> None: d = { "a": 1, "b": { @@ -877,7 +880,7 @@ def test_get_item(self): # bb = b['b'] # self.assertTrue(isinstance(bb['d'], benedict)) - def test_get_dict(self): + def test_get_dict(self) -> None: d = { "a": { "x": 1, @@ -889,7 +892,7 @@ def test_get_dict(self): # self.assertTrue(isinstance(b.get_dict('a'), benedict)) self.assertEqual(b.get("a.x"), 1) - def test_get_list(self): + def test_get_list(self) -> None: d = { "a": [ { @@ -921,7 +924,7 @@ def test_get_list(self): self.assertEqual(benedict(ls[1]).get("b.c"), 2) self.assertEqual(benedict(ls[2]).get("b.c"), 3) - def test_get_list_item(self): + def test_get_list_item(self) -> None: d = { "a": [ { @@ -946,7 +949,7 @@ def test_get_list_item(self): # self.assertTrue(isinstance(i, benedict)) self.assertEqual(i.get("b.c"), 2) - def test_get_phonenumber(self): + def test_get_phonenumber(self) -> None: d = { "a": { "b": " (0039) 3334445566 ", @@ -973,7 +976,7 @@ def test_get_phonenumber(self): self.assertEqual(p, {}) # self.assertTrue(isinstance(p, benedict)) - def test_groupby(self): + def test_groupby(self) -> None: d = { "cities": [ { @@ -1043,7 +1046,7 @@ def test_groupby(self): self.assertTrue(bd_cities[1] in g["DE"]) self.assertTrue(bd_cities[8] in g["DE"]) - def test_invert(self): + def test_invert(self) -> None: d = { "a": 1, "b": 2, @@ -1062,7 +1065,7 @@ def test_invert(self): } self.assertEqual(i, r) - def test_invert_with_custom_keypath_separator(self): + def test_invert_with_custom_keypath_separator(self) -> None: d = { "a": "1.0", "b": "2.0", @@ -1082,7 +1085,7 @@ def test_invert_with_custom_keypath_separator(self): self.assertEqual(i, r) self.assertEqual(bd.keypath_separator, i.keypath_separator) - def test_invert_multiple_values(self): + def test_invert_multiple_values(self) -> None: d = { "a": 1, "b": 2, @@ -1097,7 +1100,7 @@ def test_invert_multiple_values(self): self.assertTrue("b" and "e" in i[2]) self.assertTrue("c" and "f" in i[3]) - def test_invert_flat(self): + def test_invert_flat(self) -> None: d = { "a": 1, "b": 2, @@ -1116,7 +1119,7 @@ def test_invert_flat(self): } self.assertEqual(i, r) - def test_items_sorted_by_keys(self): + def test_items_sorted_by_keys(self) -> None: d = { "y": 3, "a": 6, @@ -1137,7 +1140,7 @@ def test_items_sorted_by_keys(self): ], ) - def test_items_sorted_by_keys_reverse(self): + def test_items_sorted_by_keys_reverse(self) -> None: d = { "y": 3, "a": 6, @@ -1158,7 +1161,7 @@ def test_items_sorted_by_keys_reverse(self): ], ) - def test_items_sorted_by_values(self): + def test_items_sorted_by_values(self) -> None: d = { "a": 3, "b": 6, @@ -1179,7 +1182,7 @@ def test_items_sorted_by_values(self): ], ) - def test_items_sorted_by_values_reverse(self): + def test_items_sorted_by_values_reverse(self) -> None: d = { "a": 3, "b": 6, @@ -1200,7 +1203,7 @@ def test_items_sorted_by_values_reverse(self): ], ) - def test_keypaths(self): + def test_keypaths(self) -> None: d = { "x": { "y": True, @@ -1246,7 +1249,7 @@ def test_keypaths(self): ] self.assertEqual(b.keypaths(indexes=True), r) - def test_match_with_regex_pattern(self): + def test_match_with_regex_pattern(self) -> None: d = { "results": [ { @@ -1268,7 +1271,7 @@ def test_match_with_regex_pattern(self): m = b.match(r) self.assertEqual(m, ["A", "B", "C"]) - def test_match_with_regex_pattern_and_custom_keypath_separator(self): + def test_match_with_regex_pattern_and_custom_keypath_separator(self) -> None: d = { "results": [ { @@ -1290,7 +1293,7 @@ def test_match_with_regex_pattern_and_custom_keypath_separator(self): m = b.match(r) self.assertEqual(m, ["A", "B", "C"]) - def test_match_with_string_pattern(self): + def test_match_with_string_pattern(self) -> None: d = { "results": [ { @@ -1317,7 +1320,7 @@ def test_match_with_string_pattern(self): m = b.match("results[*].props[2]") self.assertEqual(m, [3, 6, 9]) - def test_match_with_string_pattern_and_custom_keypath_separator(self): + def test_match_with_string_pattern_and_custom_keypath_separator(self) -> None: d = { "results": [ { @@ -1340,7 +1343,7 @@ def test_match_with_string_pattern_and_custom_keypath_separator(self): m = b.match("results[*]/props[2]") self.assertEqual(m, [3, 6, 9]) - def test_merge_with_single_dict(self): + def test_merge_with_single_dict(self) -> None: d = { "a": 1, "b": 1, @@ -1358,7 +1361,7 @@ def test_merge_with_single_dict(self): } self.assertEqual(d, r) - def test_merge_with_multiple_dicts(self): + def test_merge_with_multiple_dicts(self) -> None: d = { "a": 1, "b": 1, @@ -1388,7 +1391,7 @@ def test_merge_with_multiple_dicts(self): } self.assertEqual(d, r) - def test_merge(self): + def test_merge(self) -> None: d = { "a": 1, "b": { @@ -1453,7 +1456,7 @@ def test_merge(self): } self.assertEqual(d, r) - def test_move(self): + def test_move(self) -> None: d = { "a": { "x": 1, @@ -1486,7 +1489,7 @@ def test_move(self): } self.assertEqual(b, r) - def test_nest(self): + def test_nest(self) -> None: d = { "values": [ {"id": 1, "parent_id": None, "name": "John"}, @@ -1563,7 +1566,7 @@ def test_nest(self): ] self.assertEqual(n, r) - def test_nest_with_custom_keys(self): + def test_nest_with_custom_keys(self) -> None: d = { "values": [ {"ID": 1, "PARENT": None, "name": "John"}, @@ -1640,7 +1643,7 @@ def test_nest_with_custom_keys(self): ] self.assertEqual(n, r) - def test_pop(self): + def test_pop(self) -> None: d = { "a": 1, "b": { @@ -1655,7 +1658,7 @@ def test_pop(self): self.assertEqual(b.pop("b.c"), 2) # self.assertTrue(isinstance(b.pop('b.d'), benedict)) - def test_remove_with_key(self): + def test_remove_with_key(self) -> None: d = { "a": 1, "b": 2, @@ -1669,7 +1672,7 @@ def test_remove_with_key(self): } self.assertEqual(b, r) - def test_remove_with_keys_list(self): + def test_remove_with_keys_list(self) -> None: d = { "a": 1, "b": 2, @@ -1686,7 +1689,7 @@ def test_remove_with_keys_list(self): } self.assertEqual(b, r) - def test_remove_with_keys_args(self): + def test_remove_with_keys_args(self) -> None: d = { "a": 1, "b": 2, @@ -1703,7 +1706,7 @@ def test_remove_with_keys_args(self): } self.assertEqual(b, r) - def test_remove_with_keypath(self): + def test_remove_with_keypath(self) -> None: d = { "a": { "x": 1, @@ -1731,7 +1734,7 @@ def test_remove_with_keypath(self): } self.assertEqual(b, r) - def test_rename(self): + def test_rename(self) -> None: d = { "a": { "x": 1, @@ -1760,8 +1763,8 @@ def test_rename(self): with self.assertRaises(KeyError): b.rename("aa", "b") - def test_search(self): - d = { + def test_search(self) -> None: + d: dict[str, Any] = { "a": "Hello world", "b": "Hello world!", "c": { @@ -1890,7 +1893,7 @@ def test_search(self): # self.assertTrue(isinstance(b.setdefault('b', 1), benedict)) # self.assertTrue(isinstance(b.setdefault('b.d', 1), benedict)) - def test_standardize(self): + def test_standardize(self) -> None: d = { "CamelCase": 1, "CamelCamelCase": 1, @@ -1924,7 +1927,7 @@ def test_standardize(self): self.assertEqual(b["location_coordinates.lat"], 0.0) self.assertEqual(b["location_coordinates.lng"], 0.0) - def test_subset(self): + def test_subset(self) -> None: d = { "a": 1, "b": 2, @@ -1945,7 +1948,7 @@ def test_subset(self): self.assertEqual(type(b), type(f)) self.assertTrue(isinstance(f, benedict)) - def test_subset_with_custom_keypath_separator(self): + def test_subset_with_custom_keypath_separator(self) -> None: d = { "a.x": 1, "b.x": 2, @@ -1967,7 +1970,7 @@ def test_subset_with_custom_keypath_separator(self): self.assertTrue(isinstance(f, benedict)) self.assertEqual(b.keypath_separator, f.keypath_separator) - def test_subset_with_keys_args(self): + def test_subset_with_keys_args(self) -> None: d = { "a": 1, "b": 2, @@ -1986,7 +1989,7 @@ def test_subset_with_keys_args(self): self.assertEqual(f, r) self.assertFalse(f is b) - def test_subset_with_keypath(self): + def test_subset_with_keypath(self) -> None: d = { "x": { "a": 1, @@ -2036,7 +2039,7 @@ def test_subset_with_keypath(self): self.assertEqual(f.get("x.a"), 1) self.assertEqual(f.get("y.b"), 2) - def test_swap(self): + def test_swap(self) -> None: d = { "a": 1, "b": 2, @@ -2051,7 +2054,7 @@ def test_swap(self): } self.assertEqual(b, r) - def test_swap_with_invalid_key(self): + def test_swap_with_invalid_key(self) -> None: d = { "a": 1, "b": 2, @@ -2061,7 +2064,7 @@ def test_swap_with_invalid_key(self): with self.assertRaises(KeyError): b.swap("a", "d") - def test_swap_with_keypath(self): + def test_swap_with_keypath(self) -> None: d = { "a": { "x": 1, @@ -2112,7 +2115,7 @@ def test_swap_with_keypath(self): } self.assertEqual(b, r) - def test_traverse(self): + def test_traverse(self) -> None: d = { "a": { "x": 2, @@ -2138,7 +2141,7 @@ def test_traverse(self): } b = benedict(d) - def f(parent, key, value): + def f(parent: Any, key: Any, value: Any) -> None: if not isinstance(value, dict): parent[key] = value + 1 @@ -2168,7 +2171,7 @@ def f(parent, key, value): } self.assertEqual(b, r) - def test_unflatten(self): + def test_unflatten(self) -> None: d = { "device_os": "Windows", "device_lang": "en-US", @@ -2194,7 +2197,7 @@ def test_unflatten(self): self.assertEqual(type(b), type(u)) self.assertTrue(isinstance(u, benedict)) - def test_unique(self): + def test_unique(self) -> None: d = { "a": { "x": 1, diff --git a/tests/dicts/test_benedict_casting.py b/tests/dicts/test_benedict_casting.py index e6505945..99fd16dc 100644 --- a/tests/dicts/test_benedict_casting.py +++ b/tests/dicts/test_benedict_casting.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict import benedict @@ -8,7 +11,7 @@ class benedict_casting_test_case(unittest.TestCase): This class describes a benedict casting test case. """ - def test__getitem__(self): + def test__getitem__(self) -> None: d = { "a": 1, "b": { @@ -21,10 +24,10 @@ def test__getitem__(self): c = b["b.c"] self.assertTrue(isinstance(c, benedict)) self.assertEqual(type(c), benedict) - self.assertTrue(c == d["b"]["c"]) - self.assertFalse(c is d["b"]["c"]) + self.assertTrue(c == d["b"]["c"]) # type: ignore[index] + self.assertFalse(c is d["b"]["c"]) # type: ignore[index] - def test_cast_dict_to_benedict(self): + def test_cast_dict_to_benedict(self) -> None: d = { "a": 1, "b": { @@ -41,7 +44,7 @@ def test_cast_dict_to_benedict(self): self.assertEqual(d, bbd) self.assertTrue(d is bbd) - def test_cast_benedict_to_dict(self): + def test_cast_benedict_to_dict(self) -> None: b = benedict( { "a": 1, @@ -65,7 +68,7 @@ def test_cast_benedict_to_dict(self): self.assertEqual(b, d) self.assertFalse(b is d) - def test_cast_benedict_kwargs_to_dict(self): + def test_cast_benedict_kwargs_to_dict(self) -> None: b = benedict( { "a": 1, @@ -82,7 +85,7 @@ def test_cast_benedict_kwargs_to_dict(self): self.assertEqual(b, d) self.assertFalse(b is d) - def test_dict(self): + def test_dict(self) -> None: d = { "a": 1, "b": { @@ -98,8 +101,8 @@ def test_dict(self): self.assertTrue(d == bd) self.assertTrue(d is bd) - def test_get(self): - d = { + def test_get(self) -> None: + d: dict[str, Any] = { "a": 1, "b": { "c": { @@ -114,8 +117,8 @@ def test_get(self): self.assertTrue(c == d["b"]["c"]) self.assertFalse(c is d["b"]["c"]) - def test_get_dict(self): - d = { + def test_get_dict(self) -> None: + d: dict[str, Any] = { "a": 1, "b": { "c": { @@ -130,8 +133,8 @@ def test_get_dict(self): self.assertTrue(c == d["b"]["c"]) self.assertFalse(c is d["b"]["c"]) - def test_get_list_item(self): - d = { + def test_get_list_item(self) -> None: + d: dict[str, Any] = { "a": 1, "b": { "c": [ @@ -154,8 +157,8 @@ def test_get_list_item(self): self.assertTrue(c == d["b"]["c"][1]) # self.assertFalse(c is d["b"]["c"][1]) - def test_pop(self): - d = { + def test_pop(self) -> None: + d: dict[str, Any] = { "a": 1, "b": { "c": { diff --git a/tests/dicts/test_benedict_keyattr.py b/tests/dicts/test_benedict_keyattr.py index f9abebe6..e495e9a2 100644 --- a/tests/dicts/test_benedict_keyattr.py +++ b/tests/dicts/test_benedict_keyattr.py @@ -4,7 +4,7 @@ class benedict_keyattr_test_case(unittest.TestCase): - def test_getitem(self): + def test_getitem(self) -> None: d = { "a": { "b": { @@ -20,7 +20,7 @@ def test_getitem(self): self.assertEqual(b.a.b.e[0].f, 4) self.assertEqual(b.a.b.e[-1].g[0], 5) - def test_getitem_not_dynamic(self): + def test_getitem_not_dynamic(self) -> None: d = { "a": { "b": { @@ -36,14 +36,14 @@ def test_getitem_not_dynamic(self): self.assertEqual(b.a.b.e[0].f, 4) self.assertEqual(b.a.b.e[-1].g[0], 5) - def test_getitem_with_non_existing_key(self): + def test_getitem_with_non_existing_key(self) -> None: d = { "a": "ok", } b = benedict(d, keyattr_enabled=True, keyattr_dynamic=True) self.assertEqual(b.b, {}) - def test_getitem_with_non_existing_key_not_dynamic(self): + def test_getitem_with_non_existing_key_not_dynamic(self) -> None: d = { "a": "ok", } @@ -51,7 +51,7 @@ def test_getitem_with_non_existing_key_not_dynamic(self): with self.assertRaises(AttributeError): _ = b.b - def test_getitem_with_non_existing_protected_item(self): + def test_getitem_with_non_existing_protected_item(self) -> None: d = { "a": { "b": { @@ -66,7 +66,7 @@ def test_getitem_with_non_existing_protected_item(self): with self.assertRaises(AttributeError): b.a.b.__test__() - def test_getitem_with_non_existing_protected_item_not_dynamic(self): + def test_getitem_with_non_existing_protected_item_not_dynamic(self) -> None: d = { "a": { "b": { @@ -81,7 +81,7 @@ def test_getitem_with_non_existing_protected_item_not_dynamic(self): with self.assertRaises(AttributeError): b.a.b.__test__() - def test_getitem_with_keyattr_disabled(self): + def test_getitem_with_keyattr_disabled(self) -> None: d = { "a": "ok", } @@ -89,7 +89,7 @@ def test_getitem_with_keyattr_disabled(self): with self.assertRaises(AttributeError): _ = b.a - def test_getitem_with_keyattr_disabled_not_dynamic(self): + def test_getitem_with_keyattr_disabled_not_dynamic(self) -> None: d = { "a": "ok", } @@ -97,7 +97,7 @@ def test_getitem_with_keyattr_disabled_not_dynamic(self): with self.assertRaises(AttributeError): _ = b.a - def test_setitem(self): + def test_setitem(self) -> None: d = { "a": { "b": { @@ -117,7 +117,7 @@ def test_setitem(self): b.a.b.e[-1].g += [6] self.assertEqual(b.a.b.e[-1].g[-1], 6) - def test_setitem_with_keyattr_disabled(self): + def test_setitem_with_keyattr_disabled(self) -> None: d = { "a": 1, } @@ -129,7 +129,7 @@ def test_setitem_with_keyattr_disabled(self): with self.assertRaises(AttributeError): b.c.d = 3 - def test_setitem_with_keyattr_not_dynamic(self): + def test_setitem_with_keyattr_not_dynamic(self) -> None: d = { "a": 1, } @@ -139,69 +139,69 @@ def test_setitem_with_keyattr_not_dynamic(self): with self.assertRaises(AttributeError): b.c.d = 3 - def test_setitem_with_non_existing_key(self): + def test_setitem_with_non_existing_key(self) -> None: b = benedict(keyattr_enabled=True, keyattr_dynamic=True) b.a = "ok" self.assertEqual(b.a, "ok") - def test_setitem_with_non_existing_key_not_dynamic(self): + def test_setitem_with_non_existing_key_not_dynamic(self) -> None: b = benedict(keyattr_enabled=True, keyattr_dynamic=False) b.a = "ok" self.assertEqual(b.a, "ok") - def test_setitem_with_non_existing_nested_keys(self): + def test_setitem_with_non_existing_nested_keys(self) -> None: b = benedict(keyattr_enabled=True, keyattr_dynamic=True) b.a.b.c = "ok" self.assertEqual(b.a.b.c, "ok") - def test_setitem_with_non_existing_nested_keys_not_dynamic(self): + def test_setitem_with_non_existing_nested_keys_not_dynamic(self) -> None: b = benedict(keyattr_enabled=True, keyattr_dynamic=False) with self.assertRaises(AttributeError): b.a.b.c = "ok" - def test_setitem_with_conflicting_key(self): + def test_setitem_with_conflicting_key(self) -> None: d = { "items": [1, 2, 3], # 'items' is an existing method } b = benedict(d, keyattr_enabled=True) with self.assertRaises(AttributeError): - b.items.append([4, 5, 6]) + b.items.append([4, 5, 6]) # type: ignore[attr-defined] - def test_keyattr_enabled_default(self): + def test_keyattr_enabled_default(self) -> None: d = benedict() self.assertTrue(d.keyattr_enabled) - def test_keyattr_dynamic_default(self): + def test_keyattr_dynamic_default(self) -> None: d = benedict() self.assertFalse(d.keyattr_dynamic) - def test_keyattr_enabled_from_constructor(self): + def test_keyattr_enabled_from_constructor(self) -> None: d = benedict(keyattr_enabled=True) self.assertTrue(d.keyattr_enabled) d = benedict(keyattr_enabled=False) self.assertFalse(d.keyattr_enabled) - def test_keyattr_dynamic_from_constructor(self): + def test_keyattr_dynamic_from_constructor(self) -> None: d = benedict(keyattr_dynamic=True) self.assertTrue(d.keyattr_dynamic) d = benedict(keyattr_dynamic=False) self.assertFalse(d.keyattr_dynamic) - def test_keyattr_enabled_getter_setter(self): + def test_keyattr_enabled_getter_setter(self) -> None: d = benedict() d.keyattr_enabled = False self.assertFalse(d.keyattr_enabled) d.keyattr_enabled = True self.assertTrue(d.keyattr_enabled) - def test_keyattr_dynamic_getter_setter(self): + def test_keyattr_dynamic_getter_setter(self) -> None: d = benedict() d.keyattr_dynamic = False self.assertFalse(d.keyattr_dynamic) d.keyattr_dynamic = True self.assertTrue(d.keyattr_dynamic) - def test_keyattr_enabled_inheritance_on_casting(self): + def test_keyattr_enabled_inheritance_on_casting(self) -> None: d = benedict(keyattr_enabled=True) c = benedict(d) self.assertTrue(c.keyattr_enabled) @@ -209,7 +209,7 @@ def test_keyattr_enabled_inheritance_on_casting(self): c = benedict(d) self.assertFalse(c.keyattr_enabled) - def test_keyattr_dynamic_inheritance_on_casting(self): + def test_keyattr_dynamic_inheritance_on_casting(self) -> None: d = benedict(keyattr_dynamic=True) c = benedict(d) self.assertTrue(c.keyattr_dynamic) diff --git a/tests/dicts/test_benedict_subclass.py b/tests/dicts/test_benedict_subclass.py index dc23dc72..7dc4814d 100644 --- a/tests/dicts/test_benedict_subclass.py +++ b/tests/dicts/test_benedict_subclass.py @@ -12,7 +12,7 @@ class benedict_subclass_test_case(unittest.TestCase): This class describes a benedict subclass test case. """ - def test_cast(self): + def test_cast(self) -> None: d = subbenedict( { "a": { @@ -29,7 +29,7 @@ def test_cast(self): self.assertTrue(isinstance(c, subbenedict)) self.assertEqual(c, {"d": True}) - def test_clone(self): + def test_clone(self) -> None: d = subbenedict({"a": True}) c = d.clone() self.assertTrue(issubclass(type(c), benedict)) diff --git a/tests/github/test_issue_0011.py b/tests/github/test_issue_0011.py index af9b8205..cfd1b75a 100644 --- a/tests/github/test_issue_0011.py +++ b/tests/github/test_issue_0011.py @@ -8,7 +8,7 @@ class github_issue_0011_test_case(unittest.TestCase): This class describes a github issue 0011 test case. """ - def test_github_issue_0011(self): + def test_github_issue_0011(self) -> None: """ https://github.com/fabiocaccamo/python-benedict/issues/11 """ diff --git a/tests/github/test_issue_0016.py b/tests/github/test_issue_0016.py index 8e3a9e53..13c57233 100644 --- a/tests/github/test_issue_0016.py +++ b/tests/github/test_issue_0016.py @@ -8,7 +8,7 @@ class github_issue_0016_test_case(unittest.TestCase): This class describes a github issue 0016 test case. """ - def test_github_issue_0016(self): + def test_github_issue_0016(self) -> None: """ https://github.com/fabiocaccamo/python-benedict/issues/16 """ diff --git a/tests/github/test_issue_0020.py b/tests/github/test_issue_0020.py index 3a7011e3..1a2204e9 100644 --- a/tests/github/test_issue_0020.py +++ b/tests/github/test_issue_0020.py @@ -1,21 +1,24 @@ import unittest +from typing import Any + +import yaml +from yaml import Node from benedict import benedict -from benedict.serializers.yaml import yaml class GetAtt(yaml.YAMLObject): yaml_loader = yaml.SafeLoader yaml_tag = "!GetAtt" - def __init__(self, val): + def __init__(self, val: Any) -> None: self.val = val @classmethod - def from_yaml(cls, loader, node): + def from_yaml(cls, loader: Any, node: Node) -> Any: return cls(node.value) - def __repr__(self): + def __repr__(self) -> str: return f"GetAtt({self.val})" @@ -27,7 +30,7 @@ class github_issue_0020_test_case(unittest.TestCase): This class describes a github issue 0020 test case. """ - def test_github_issue_0020(self): + def test_github_issue_0020(self) -> None: """ https://github.com/fabiocaccamo/python-benedict/issues/20 """ diff --git a/tests/github/test_issue_0025.py b/tests/github/test_issue_0025.py index 631693dd..4411fe0e 100644 --- a/tests/github/test_issue_0025.py +++ b/tests/github/test_issue_0025.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any, cast import yaml @@ -16,7 +19,7 @@ class github_issue_0025_test_case(unittest.TestCase): """ @staticmethod - def load_dict(): + def load_dict() -> dict[str, Any]: yaml_str = """ SERVER: S01: @@ -32,9 +35,9 @@ def load_dict(): room: s01_room """ servers = yaml.safe_load(yaml_str) - return servers + return cast("dict[str, Any]", servers) - def test_pointers_with_dict(self): + def test_pointers_with_dict(self) -> None: servers = dict(self.load_dict()) s01_ptr = servers["SERVER"]["S01"] s01_ptr["alias"] = "ptr_alias" @@ -42,7 +45,7 @@ def test_pointers_with_dict(self): s01_ptr["credentials"]["username"] = "ptr_unsername" self.assertEqual(s01_ptr, servers["SERVER"]["S01"]) - def test_pointers_with_benedict_casting(self): + def test_pointers_with_benedict_casting(self) -> None: d = self.load_dict() servers = benedict(d) s01_ptr = servers["SERVER.S01"] @@ -53,7 +56,7 @@ def test_pointers_with_benedict_casting(self): s01_ptr["credentials"]["username"] = "ptr_unsername" self.assertEqual(s01_ptr, servers["SERVER.S01"]) - def test_pointers_after_pointer_update(self): + def test_pointers_after_pointer_update(self) -> None: d = self.load_dict() b = benedict(d) d["SERVER"]["S01"]["alias"] = "new_alias" @@ -61,7 +64,7 @@ def test_pointers_after_pointer_update(self): self.assertEqual(b, d) self.assertEqual(b.dict(), d) - def test_pointers_after_pointer_clear(self): + def test_pointers_after_pointer_clear(self) -> None: d = self.load_dict() b = benedict(d) d.clear() diff --git a/tests/github/test_issue_0027.py b/tests/github/test_issue_0027.py index 88ab4fcb..91c58b8e 100644 --- a/tests/github/test_issue_0027.py +++ b/tests/github/test_issue_0027.py @@ -12,7 +12,7 @@ class github_issue_0027_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0027 """ - def test_append_to_list_with_empty_index(self): + def test_append_to_list_with_empty_index(self) -> None: d = benedict( { "results": [ diff --git a/tests/github/test_issue_0032.py b/tests/github/test_issue_0032.py index 1ded749d..eff4511c 100644 --- a/tests/github/test_issue_0032.py +++ b/tests/github/test_issue_0032.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from typing import Any from benedict import benedict @@ -14,7 +17,7 @@ class github_issue_0032_test_case(unittest.TestCase): """ @staticmethod - def load_dict(): + def load_dict() -> dict[str, Any]: return { "a": { "b": { @@ -26,7 +29,7 @@ def load_dict(): } } - def test_pointers_with_dict(self): + def test_pointers_with_dict(self) -> None: b = benedict(self.load_dict()) ab = benedict(b["a.b"]) ab["c"]["d"] = 20 diff --git a/tests/github/test_issue_0034.py b/tests/github/test_issue_0034.py index 13db5126..7e213b9d 100644 --- a/tests/github/test_issue_0034.py +++ b/tests/github/test_issue_0034.py @@ -1,5 +1,8 @@ +from __future__ import annotations + import json import unittest +from typing import Any from benedict import benedict @@ -14,7 +17,7 @@ class github_issue_0034_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0034 """ - def test_json_dumps(self): + def test_json_dumps(self) -> None: b = benedict( { "a": 1, @@ -28,8 +31,8 @@ def test_json_dumps(self): dumped = json.dumps(b, sort_keys=True) self.assertEqual(dumped, '{"a": 1, "b": {"c": {"d": 2}}}') - def test_json_dumps_after_pointer_update(self): - d = { + def test_json_dumps_after_pointer_update(self) -> None: + d: dict[str, Any] = { "a": 1, "b": { "c": { @@ -45,7 +48,7 @@ def test_json_dumps_after_pointer_update(self): dumped = json.dumps(b, sort_keys=True) self.assertEqual(dumped, '{"a": 2, "b": {"c": {"d": 3}}}') - def test_json_dumps_after_instance_update(self): + def test_json_dumps_after_instance_update(self) -> None: d = { "a": 1, "b": { diff --git a/tests/github/test_issue_0035.py b/tests/github/test_issue_0035.py index d8a906e6..c349703f 100644 --- a/tests/github/test_issue_0035.py +++ b/tests/github/test_issue_0035.py @@ -13,7 +13,7 @@ class github_issue_0035_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0035 """ - def test_keypath_separator_inheritance(self): + def test_keypath_separator_inheritance(self) -> None: b = benedict({"a.b": 1}, keypath_separator=None) c = benedict(b, keypath_separator=None) self.assertEqual(c.keypath_separator, None) diff --git a/tests/github/test_issue_0038.py b/tests/github/test_issue_0038.py index c9227e6b..08b0bfa0 100644 --- a/tests/github/test_issue_0038.py +++ b/tests/github/test_issue_0038.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import unittest +from collections.abc import Iterator from benedict import benedict @@ -13,10 +16,10 @@ class github_issue_0038_test_case(unittest.TestCase): """ @staticmethod - def get_dict_generator(): + def get_dict_generator() -> Iterator[tuple[int, str]]: yield from enumerate("abcd") - def test_init_with_generator(self): + def test_init_with_generator(self) -> None: b = benedict(self.get_dict_generator()) self.assertEqual(b, {0: "a", 1: "b", 2: "c", 3: "d"}) self.assertEqual(b.to_json(), '{"0": "a", "1": "b", "2": "c", "3": "d"}') diff --git a/tests/github/test_issue_0039.py b/tests/github/test_issue_0039.py index a162c2d9..abcc898d 100644 --- a/tests/github/test_issue_0039.py +++ b/tests/github/test_issue_0039.py @@ -13,7 +13,7 @@ class github_issue_0039_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0039 """ - def test_performance(self): + def test_performance(self) -> None: b = benedict() i_iterations = 500 diff --git a/tests/github/test_issue_0043.py b/tests/github/test_issue_0043.py index 2b7dd4f3..3b4fca96 100644 --- a/tests/github/test_issue_0043.py +++ b/tests/github/test_issue_0043.py @@ -14,7 +14,7 @@ class github_issue_0043_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0043 """ - def test_to_yaml(self): + def test_to_yaml(self) -> None: b = benedict({"level1": {"level2": "Hello world"}}) s = b.to_yaml() r = """level1: @@ -22,7 +22,7 @@ def test_to_yaml(self): """ self.assertEqual(s, r) - def test_dict_compatibility(self): + def test_dict_compatibility(self) -> None: yaml.safe_dump(dict(benedict({}))) yaml.safe_dump(dict(benedict({"level1": None}))) yaml.safe_dump(dict(benedict({"level1": {"level2": "blablabla"}}))) diff --git a/tests/github/test_issue_0046.py b/tests/github/test_issue_0046.py index a14ec1fe..891275c9 100644 --- a/tests/github/test_issue_0046.py +++ b/tests/github/test_issue_0046.py @@ -1,5 +1,6 @@ import json import unittest +from typing import Any from benedict import benedict @@ -13,7 +14,7 @@ class github_issue_0046_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0046 """ - def test_json_dumps_with_cloned_instance(self): + def test_json_dumps_with_cloned_instance(self) -> None: d = { "id": "37e4f6e876", "meta": { @@ -76,7 +77,7 @@ def test_json_dumps_with_cloned_instance(self): # json.encoder.c_make_encoder = None json_encoder = None - def json_dumps(d): + def json_dumps(d: Any) -> str: return json.dumps(d, sort_keys=True, cls=json_encoder) d_new_raw = { diff --git a/tests/github/test_issue_0048.py b/tests/github/test_issue_0048.py index 48b1364b..eaff9285 100644 --- a/tests/github/test_issue_0048.py +++ b/tests/github/test_issue_0048.py @@ -12,7 +12,7 @@ class github_issue_0048_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0048 """ - def test_json_dumps_with_cloned_instance(self): + def test_json_dumps_with_cloned_instance(self) -> None: test = benedict( { "foo": { diff --git a/tests/github/test_issue_0053.py b/tests/github/test_issue_0053.py index 7afd23d9..ea20fbc1 100644 --- a/tests/github/test_issue_0053.py +++ b/tests/github/test_issue_0053.py @@ -12,7 +12,7 @@ class github_issue_0053_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0053 """ - def test_toml_dump_circular_reference(self): + def test_toml_dump_circular_reference(self) -> None: toml_str = """[build-system] requires = [ "poetry-core>=1.0.0",] build-backend = "poetry.core.masonry.api" diff --git a/tests/github/test_issue_0057.py b/tests/github/test_issue_0057.py index d8a2a0fe..f94989ed 100644 --- a/tests/github/test_issue_0057.py +++ b/tests/github/test_issue_0057.py @@ -1,5 +1,8 @@ +from __future__ import annotations + import json import unittest +from typing import Any from benedict import benedict @@ -13,8 +16,8 @@ class github_issue_0057_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0057 """ - def test_json_dump_with_initial_empty_dict_reference(self): - r = {} + def test_json_dump_with_initial_empty_dict_reference(self) -> None: + r: dict[str, Any] = {} d = benedict(r) d["a"] = 1 self.assertEqual(d, {"a": 1}) diff --git a/tests/github/test_issue_0059.py b/tests/github/test_issue_0059.py index c58577ba..729bee2f 100644 --- a/tests/github/test_issue_0059.py +++ b/tests/github/test_issue_0059.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import unittest from benedict import benedict @@ -12,8 +14,10 @@ class github_issue_0059_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0059 """ - def test_init_with_empty_dict_then_merge_with_dict_should_affect_both_dicts(self): - initial_empty_dict = {} + def test_init_with_empty_dict_then_merge_with_dict_should_affect_both_dicts( + self, + ) -> None: + initial_empty_dict: dict[str, str] = {} the_benedict = benedict(initial_empty_dict) the_benedict.merge({"foo": "bar"}) self.assertEqual(initial_empty_dict, {"foo": "bar"}) @@ -21,7 +25,7 @@ def test_init_with_empty_dict_then_merge_with_dict_should_affect_both_dicts(self def test_init_empty_dict_then_assign_another_empty_dict_as_first_key_should_work( self, - ): + ) -> None: d = benedict() # these two lines are inefficient # d["a"] = {"b": {}} is better. Regardless, will test both diff --git a/tests/github/test_issue_0066.py b/tests/github/test_issue_0066.py index 82c84091..3a06738d 100644 --- a/tests/github/test_issue_0066.py +++ b/tests/github/test_issue_0066.py @@ -1,4 +1,5 @@ import unittest +from typing import Any from benedict import benedict @@ -12,7 +13,7 @@ class github_issue_0066_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0066 """ - def _get_dict(self): + def _get_dict(self) -> benedict[str, Any]: d = benedict( { "results": [ @@ -33,22 +34,22 @@ def _get_dict(self): ) return d - def test_contains_with_tuple(self): + def test_contains_with_tuple(self) -> None: d = self._get_dict() self.assertTrue("results[-1].locations[-1]" in d) self.assertFalse("results[-1].locations[3]" in d) - def test_get_item_with_tuple(self): + def test_get_item_with_tuple(self) -> None: d = self._get_dict() loc = d["results[-1].locations[-1]"] self.assertEqual(loc, 12) - def test_get_with_tuple(self): + def test_get_with_tuple(self) -> None: d = self._get_dict() loc = d.get("results[-1].locations[-1]") self.assertEqual(loc, 12) - def test_delete_item_with_tuple(self): + def test_delete_item_with_tuple(self) -> None: d = self._get_dict() with self.assertRaises(TypeError): del d["results[-1].locations[-1]"] @@ -56,7 +57,7 @@ def test_delete_item_with_tuple(self): # loc = d.get('results[-1].locations[-1]') # self.assertEqual(loc, 11) - def test_delete_item_with_tuple_at_root_level(self): + def test_delete_item_with_tuple_at_root_level(self) -> None: d = benedict( { "locations": (10, 11, 12), @@ -69,7 +70,7 @@ def test_delete_item_with_tuple_at_root_level(self): # self.assertEqual(loc, 11) # self.assertEqual(len(d.get('locations')), 2) - def test_pop_with_tuple(self): + def test_pop_with_tuple(self) -> None: d = self._get_dict() with self.assertRaises(TypeError): _ = d.pop("results[-1].locations[-1]") @@ -79,7 +80,7 @@ def test_pop_with_tuple(self): # loc = d.get('results[-1].locations[-1]') # self.assertEqual(loc, 11) - def test_set_item_with_tuple(self): + def test_set_item_with_tuple(self) -> None: d = self._get_dict() with self.assertRaises(TypeError): d["results[-1].locations[-1]"] = 13 diff --git a/tests/github/test_issue_0088.py b/tests/github/test_issue_0088.py index a91c9044..7d96bbe1 100644 --- a/tests/github/test_issue_0088.py +++ b/tests/github/test_issue_0088.py @@ -12,12 +12,12 @@ class github_issue_0088_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0088 """ - def test_flatten_without_keypath_separator(self): + def test_flatten_without_keypath_separator(self) -> None: d = benedict({"a": {"b": {"c": 1}}}, keypath_separator=None) f = d.flatten(".") self.assertEqual(f, {"a.b.c": 1}) - def test_flatten_with_separator_equal_to_keypath_separator(self): + def test_flatten_with_separator_equal_to_keypath_separator(self) -> None: d = benedict({"a": {"b": {"c": 1}}}, keypath_separator=".") with self.assertRaises(ValueError): _ = d.flatten(".") @@ -25,7 +25,7 @@ def test_flatten_with_separator_equal_to_keypath_separator(self): with self.assertRaises(ValueError): _ = d.flatten("_") - def test_flatten_with_separator_different_from_keypath_separator(self): + def test_flatten_with_separator_different_from_keypath_separator(self) -> None: d = benedict({"a": {"b": {"c": 1}}}, keypath_separator="_") f = d.flatten(".") self.assertEqual(f, {"a.b.c": 1}) diff --git a/tests/github/test_issue_0089.py b/tests/github/test_issue_0089.py index 9664a163..ad2dea15 100644 --- a/tests/github/test_issue_0089.py +++ b/tests/github/test_issue_0089.py @@ -12,7 +12,7 @@ class github_issue_0089_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0089 """ - def test_broken_serialization_with_benedict_attributes(self): + def test_broken_serialization_with_benedict_attributes(self) -> None: d1 = benedict() d1["a"] = benedict({"b": 2}) yaml_str = d1.to_yaml() diff --git a/tests/github/test_issue_0102.py b/tests/github/test_issue_0102.py index 90d6e755..13d01d62 100644 --- a/tests/github/test_issue_0102.py +++ b/tests/github/test_issue_0102.py @@ -14,7 +14,7 @@ class github_issue_0102_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0102 """ - def test_orjson_benedict_reference_with_setitem(self): + def test_orjson_benedict_reference_with_setitem(self) -> None: d = benedict( { "internal": {"mykey": "OLD"}, @@ -29,7 +29,7 @@ def test_orjson_benedict_reference_with_setitem(self): self.assertEqual(d, {"internal": {"mykey": "NEW"}}) - def test_orjson_benedict_reference_with_contructor_and_setitem(self): + def test_orjson_benedict_reference_with_contructor_and_setitem(self) -> None: d = benedict( { "internal": benedict({"mykey": "OLD"}), @@ -46,7 +46,7 @@ def test_orjson_benedict_reference_with_contructor_and_setitem(self): def test_orjson_benedict_reference_with_contructor_and_nested_benedict_instances( self, - ): + ) -> None: d = benedict( { "a": benedict( diff --git a/tests/github/test_issue_0109.py b/tests/github/test_issue_0109.py index 9f2f3e2c..d77f7964 100644 --- a/tests/github/test_issue_0109.py +++ b/tests/github/test_issue_0109.py @@ -12,7 +12,7 @@ class github_issue_0109_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0109 """ - def test_set_dict_item_value_in_list(self): + def test_set_dict_item_value_in_list(self) -> None: d = benedict() d["a"] = "1" d["b[1]"] = "a" diff --git a/tests/github/test_issue_0110.py b/tests/github/test_issue_0110.py index 1e88d46f..347e9463 100644 --- a/tests/github/test_issue_0110.py +++ b/tests/github/test_issue_0110.py @@ -13,7 +13,7 @@ class github_issue_0110_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0110 """ - def test_toml_circular_reference_detected(self): + def test_toml_circular_reference_detected(self) -> None: d = {"a": {"b": {"c": 1}}} d["e"] = benedict({"f": 2}) diff --git a/tests/github/test_issue_0144.py b/tests/github/test_issue_0144.py index f44346a2..6a367056 100644 --- a/tests/github/test_issue_0144.py +++ b/tests/github/test_issue_0144.py @@ -13,12 +13,12 @@ class github_issue_0144_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0144 """ - def test_init_with_pathlib_path_object_and_valid_path(self): + def test_init_with_pathlib_path_object_and_valid_path(self) -> None: # print(pathlib.Path("./test_issue_0144.json")) d = benedict(pathlib.Path("tests/github/test_issue_0144.json"), format="json") self.assertEqual(d, {"a": 1, "b": 2, "c": 3, "x": 7, "y": 8, "z": 9}) - def test_init_with_pathlib_path_object_and_invalid_path(self): + def test_init_with_pathlib_path_object_and_invalid_path(self) -> None: with self.assertRaises(ValueError): benedict( pathlib.Path("tests/github/test_issue_0144_invalid.json"), format="json" diff --git a/tests/github/test_issue_0198.py b/tests/github/test_issue_0198.py index 04a4a572..bc55e364 100644 --- a/tests/github/test_issue_0198.py +++ b/tests/github/test_issue_0198.py @@ -14,7 +14,7 @@ class github_issue_0198_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0198 """ - def test_constructor_with_s3_url_and_s3_options_with_file_json(self): + def test_constructor_with_s3_url_and_s3_options_with_file_json(self) -> None: aws_access_key_id = config("AWS_ACCESS_KEY_ID", default=None) aws_secret_access_key = config("AWS_SECRET_ACCESS_KEY", default=None) if not all([aws_access_key_id, aws_secret_access_key]): @@ -31,7 +31,7 @@ def test_constructor_with_s3_url_and_s3_options_with_file_json(self): expected_dict = {"a": 1, "b": 2, "c": 3, "x": 7, "y": 8, "z": 9} self.assertEqual(d, expected_dict) - def test_constructor_with_s3_url_and_s3_options_with_file_xlsx(self): + def test_constructor_with_s3_url_and_s3_options_with_file_xlsx(self) -> None: aws_access_key_id = config("AWS_ACCESS_KEY_ID", default=None) aws_secret_access_key = config("AWS_SECRET_ACCESS_KEY", default=None) if not all([aws_access_key_id, aws_secret_access_key]): diff --git a/tests/github/test_issue_0226.py b/tests/github/test_issue_0226.py index dcc2e612..2d09a890 100644 --- a/tests/github/test_issue_0226.py +++ b/tests/github/test_issue_0226.py @@ -14,7 +14,7 @@ class github_issue_0226_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0226 """ - def test_file_not_found_error_if_filepath_is_just_filename(self): + def test_file_not_found_error_if_filepath_is_just_filename(self) -> None: b = benedict({"a": 1, "b": 2, "c": 3, "x": 7, "y": 8, "z": 9}) filepath = "test.yml" b.to_yaml(filepath=filepath) diff --git a/tests/github/test_issue_0284.py b/tests/github/test_issue_0284.py index 15de7b65..91cedf01 100644 --- a/tests/github/test_issue_0284.py +++ b/tests/github/test_issue_0284.py @@ -12,7 +12,7 @@ class github_issue_0284_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0284 """ - def test_from_ini_returns_str_instead_of_dict(self): + def test_from_ini_returns_str_instead_of_dict(self) -> None: original = benedict( { "section1": { diff --git a/tests/github/test_issue_0287.py b/tests/github/test_issue_0287.py index b9fe9232..4c4dc3d0 100644 --- a/tests/github/test_issue_0287.py +++ b/tests/github/test_issue_0287.py @@ -11,7 +11,7 @@ class github_issue_0287_test_case(io_dict_test_case): - Run python -m unittest tests.github.test_issue_0287 """ - def test_sources_argument_with_all_list(self): + def test_sources_argument_with_all_list(self) -> None: filepath = self.input_path("valid-content.json") _ = benedict(filepath, sources=["*"]) _ = benedict.from_json(filepath, sources=["*"]) @@ -22,7 +22,7 @@ def test_sources_argument_with_all_list(self): _ = benedict(filepath, sources=["auto"]) _ = benedict.from_json(filepath, sources=["auto"]) - def test_sources_argument_with_all_string(self): + def test_sources_argument_with_all_string(self) -> None: filepath = self.input_path("valid-content.json") _ = benedict(filepath, sources="*") _ = benedict.from_json(filepath, sources="*") @@ -33,7 +33,7 @@ def test_sources_argument_with_all_string(self): _ = benedict(filepath, sources="auto") _ = benedict.from_json(filepath, sources="auto") - def test_sources_argument_with_list(self): + def test_sources_argument_with_list(self) -> None: filepath = self.input_path("valid-content.json") _ = benedict(filepath, sources=["file"]) @@ -52,7 +52,7 @@ def test_sources_argument_with_list(self): with self.assertRaises(ValueError): _ = benedict.from_json(filepath, sources=["data"]) - def test_sources_argument_with_string(self): + def test_sources_argument_with_string(self) -> None: filepath = self.input_path("valid-content.json") _ = benedict(filepath, sources="file") diff --git a/tests/github/test_issue_0294.py b/tests/github/test_issue_0294.py index 68e87e0b..f6964714 100644 --- a/tests/github/test_issue_0294.py +++ b/tests/github/test_issue_0294.py @@ -12,7 +12,7 @@ class github_issue_0294_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0294 """ - def test_assigning_benedict_element_to_itself_clears_the_element(self): + def test_assigning_benedict_element_to_itself_clears_the_element(self) -> None: d = benedict({"a": {"b": 1}}) d["a"] = d["a"] self.assertEqual(d, {"a": {"b": 1}}) diff --git a/tests/github/test_issue_0314.py b/tests/github/test_issue_0314.py index 8886e06d..3cadf545 100644 --- a/tests/github/test_issue_0314.py +++ b/tests/github/test_issue_0314.py @@ -14,7 +14,7 @@ class github_issue_0314_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0314 """ - def test_yaml_serializer_produces_inconsistent_results(self): + def test_yaml_serializer_produces_inconsistent_results(self) -> None: b = benedict({"foo": "foo"}) b["hello.world"] = "hello world" diff --git a/tests/github/test_issue_0333.py b/tests/github/test_issue_0333.py index 7ce5478a..a03d36fc 100644 --- a/tests/github/test_issue_0333.py +++ b/tests/github/test_issue_0333.py @@ -12,7 +12,7 @@ class github_issue_0333_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0333 """ - def test_items_value_type(self): + def test_items_value_type(self) -> None: likes = { "fruit": { "apple": "green", @@ -30,7 +30,7 @@ def test_items_value_type(self): for _, v in likes.items(): self.assertEqual(type(v), benedict) - def test_values_value_type(self): + def test_values_value_type(self) -> None: likes = { "fruit": { "apple": "green", diff --git a/tests/github/test_issue_0334.py b/tests/github/test_issue_0334.py index 4438733b..168ba5cb 100644 --- a/tests/github/test_issue_0334.py +++ b/tests/github/test_issue_0334.py @@ -17,7 +17,7 @@ class github_issue_0334_test_case(unittest.TestCase): sys.version_info < (3, 9), "The | operator supported since Pythopn 3.9", ) - def test_union_with_assignement_operator(self): + def test_union_with_assignement_operator(self) -> None: a = {"a": "a", "b": "b"} b = {"b": "b", "c": "c"} diff --git a/tests/github/test_issue_0355.py b/tests/github/test_issue_0355.py index dcc65362..38f6c496 100644 --- a/tests/github/test_issue_0355.py +++ b/tests/github/test_issue_0355.py @@ -13,7 +13,7 @@ class github_issue_0355_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0355 """ - def test_from_xls_with_options(self): + def test_from_xls_with_options(self) -> None: # print(pathlib.Path("./test_issue_0144.json")) filepath = pathlib.Path("tests/github/test_issue_0355.xlsx") diff --git a/tests/github/test_issue_0367.py b/tests/github/test_issue_0367.py index 03fb549d..a85b80bf 100644 --- a/tests/github/test_issue_0367.py +++ b/tests/github/test_issue_0367.py @@ -12,21 +12,21 @@ class github_issue_0367_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0367 """ - def test_dict_keys_with_separators_with_merge(self): + def test_dict_keys_with_separators_with_merge(self) -> None: d = {"foo.bar": 1} b = benedict() with self.assertRaises(ValueError): b.merge(d) # self.assertEqual(b, {"foo": {"bar": 1}}) - def test_dict_keys_with_separators_with_nested_merge(self): + def test_dict_keys_with_separators_with_nested_merge(self) -> None: d = {"baz": {"foo.bar": 1}} b = benedict() with self.assertRaises(ValueError): b.merge(d) # self.assertEqual(b, {"baz": {"foo.bar": 1}}) - def test_dict_keys_with_separators_with_constructor(self): + def test_dict_keys_with_separators_with_constructor(self) -> None: d = {"foo.bar": 1} with self.assertRaises(ValueError): benedict(d) diff --git a/tests/github/test_issue_0432.py b/tests/github/test_issue_0432.py index 76c0a736..887f1486 100644 --- a/tests/github/test_issue_0432.py +++ b/tests/github/test_issue_0432.py @@ -12,14 +12,14 @@ class github_issue_0432_test_case(unittest.TestCase): - Run python -m unittest tests.github.test_issue_0432 """ - def test_tuple_as_key_like_dict_432(self): + def test_tuple_as_key_like_dict_432(self) -> None: d1 = {} d2 = benedict() d1[(0, 0, 1)] = "a" d2[(0, 0, 1)] = "a" self.assertEqual(d1, d2) - def test_tuple_as_key_like_dict_412(self): + def test_tuple_as_key_like_dict_412(self) -> None: d = {} d[("a", True)] = "test" diff --git a/tests/serializers/test_abstract_serializer.py b/tests/serializers/test_abstract_serializer.py index 845239a7..22255dee 100644 --- a/tests/serializers/test_abstract_serializer.py +++ b/tests/serializers/test_abstract_serializer.py @@ -1,4 +1,5 @@ import unittest +from typing import Any from benedict.serializers import AbstractSerializer @@ -8,20 +9,20 @@ class abstract_serializer_test_case(unittest.TestCase): This class describes an abstract serializer test case. """ - def test_decode_abstract(self): + def test_decode_abstract(self) -> None: s = AbstractSerializer() with self.assertRaises(NotImplementedError): s.decode("") - def test_encode_abstract(self): + def test_encode_abstract(self) -> None: s = AbstractSerializer() with self.assertRaises(NotImplementedError): s.encode({}) - def test_inheritance(self): + def test_inheritance(self) -> None: class ConcreteSerializer(AbstractSerializer): @staticmethod - def encode(d): + def encode(d: Any, **kwargs: Any) -> str: return "" s = ConcreteSerializer() diff --git a/tests/serializers/test_base64_serializer.py b/tests/serializers/test_base64_serializer.py index 738d4101..aaa83a1a 100644 --- a/tests/serializers/test_base64_serializer.py +++ b/tests/serializers/test_base64_serializer.py @@ -8,10 +8,10 @@ class base64_serializer_test_case(unittest.TestCase): This class describes a base64 serializer test case. """ - def test_decode_base64(self): + def test_decode_base64(self) -> None: # TODO pass - def test_encode_base64(self): + def test_encode_base64(self) -> None: # TODO pass diff --git a/tests/serializers/test_csv_serializer.py b/tests/serializers/test_csv_serializer.py index 54312152..5b8bef14 100644 --- a/tests/serializers/test_csv_serializer.py +++ b/tests/serializers/test_csv_serializer.py @@ -8,10 +8,10 @@ class csv_serializer_test_case(unittest.TestCase): This class describes a csv serializer test case. """ - def test_decode_csv(self): + def test_decode_csv(self) -> None: # TODO pass - def test_encode_csv(self): + def test_encode_csv(self) -> None: # TODO pass diff --git a/tests/serializers/test_json_serializer.py b/tests/serializers/test_json_serializer.py index c039c3b8..dbba9b0b 100644 --- a/tests/serializers/test_json_serializer.py +++ b/tests/serializers/test_json_serializer.py @@ -8,10 +8,10 @@ class json_serializer_test_case(unittest.TestCase): This class describes a json serializer test case. """ - def test_decode_json(self): + def test_decode_json(self) -> None: # TODO pass - def test_encode_json(self): + def test_encode_json(self) -> None: # TODO pass diff --git a/tests/serializers/test_pickle_serializer.py b/tests/serializers/test_pickle_serializer.py index 7f874910..4a5b594f 100644 --- a/tests/serializers/test_pickle_serializer.py +++ b/tests/serializers/test_pickle_serializer.py @@ -26,7 +26,7 @@ class pickle_serializer_test_case(unittest.TestCase): # r = 'gAJ9cQBYBAAAAGRhdGVxAWNkYXRldGltZQpkYXRldGltZQpxAmNfY29kZWNzCmVuY29kZQpxA1gLAAAAB8OBBAMAAAAAAABxBFgGAAAAbGF0aW4xcQWGcQZScQeFcQhScQlzLg==' # self.assertEqual(s, r) - def test_encode_decode_pickle(self): + def test_encode_decode_pickle(self) -> None: d = { "date": dt.datetime(year=1985, month=4, day=3), } diff --git a/tests/serializers/test_plist_serializer.py b/tests/serializers/test_plist_serializer.py index 3377142d..e9010acc 100644 --- a/tests/serializers/test_plist_serializer.py +++ b/tests/serializers/test_plist_serializer.py @@ -8,10 +8,10 @@ class plist_serializer_test_case(unittest.TestCase): This class describes a plist serializer test case. """ - def test_decode_plist(self): + def test_decode_plist(self) -> None: # TODO pass - def test_encode_plist(self): + def test_encode_plist(self) -> None: # TODO pass diff --git a/tests/serializers/test_query_string_serializer.py b/tests/serializers/test_query_string_serializer.py index fc5f212e..ea982307 100644 --- a/tests/serializers/test_query_string_serializer.py +++ b/tests/serializers/test_query_string_serializer.py @@ -8,10 +8,10 @@ class query_string_serializer_test_case(unittest.TestCase): This class describes a query-string serializer test case. """ - def test_decode_query_string(self): + def test_decode_query_string(self) -> None: # TODO pass - def test_encode_query_string(self): + def test_encode_query_string(self) -> None: # TODO pass diff --git a/tests/serializers/test_toml_serializer.py b/tests/serializers/test_toml_serializer.py index b260588b..e16c9b2f 100644 --- a/tests/serializers/test_toml_serializer.py +++ b/tests/serializers/test_toml_serializer.py @@ -8,10 +8,10 @@ class toml_serializer_test_case(unittest.TestCase): This class describes a toml serializer test case. """ - def test_decode_toml(self): + def test_decode_toml(self) -> None: # TODO pass - def test_encode_toml(self): + def test_encode_toml(self) -> None: # TODO pass diff --git a/tests/serializers/test_xls_serializer.py b/tests/serializers/test_xls_serializer.py index 77609ec0..f8696bc3 100644 --- a/tests/serializers/test_xls_serializer.py +++ b/tests/serializers/test_xls_serializer.py @@ -8,10 +8,10 @@ class xls_serializer_test_case(unittest.TestCase): This class describes a xls serializer test case. """ - def test_decode_xls(self): + def test_decode_xls(self) -> None: # TODO pass - def test_encode_xls(self): + def test_encode_xls(self) -> None: # TODO pass diff --git a/tests/serializers/test_xml_serializer.py b/tests/serializers/test_xml_serializer.py index 5dc1b095..8927a01a 100644 --- a/tests/serializers/test_xml_serializer.py +++ b/tests/serializers/test_xml_serializer.py @@ -8,10 +8,10 @@ class xml_serializer_test_case(unittest.TestCase): This class describes a xml serializer test case. """ - def test_decode_xml(self): + def test_decode_xml(self) -> None: # TODO pass - def test_encode_xml(self): + def test_encode_xml(self) -> None: # TODO pass diff --git a/tests/serializers/test_yaml_serializer.py b/tests/serializers/test_yaml_serializer.py index deb13d39..37622a4f 100644 --- a/tests/serializers/test_yaml_serializer.py +++ b/tests/serializers/test_yaml_serializer.py @@ -8,10 +8,10 @@ class yaml_serializer_test_case(unittest.TestCase): This class describes an yaml serializer test case. """ - def test_decode_yaml(self): + def test_decode_yaml(self) -> None: # TODO pass - def test_encode_yaml(self): + def test_encode_yaml(self) -> None: # TODO pass diff --git a/tests/stackoverflow/test_question_20528081.py b/tests/stackoverflow/test_question_20528081.py index 275e4d5b..ca353493 100644 --- a/tests/stackoverflow/test_question_20528081.py +++ b/tests/stackoverflow/test_question_20528081.py @@ -2,7 +2,7 @@ class stackoverflow_question_20528081_test_case(unittest.TestCase): - def test_stackoverflow_question_20528081(self): + def test_stackoverflow_question_20528081(self) -> None: """ https://stackoverflow.com/questions/20528081/performance-of-calculations-on-large-flattened-dictionary-with-implied-hierarchy """ diff --git a/tests/stackoverflow/test_question_58692636.py b/tests/stackoverflow/test_question_58692636.py index 554f96f9..12c70e84 100644 --- a/tests/stackoverflow/test_question_58692636.py +++ b/tests/stackoverflow/test_question_58692636.py @@ -2,7 +2,7 @@ class stackoverflow_question_58692636_test_case(unittest.TestCase): - def test_stackoverflow_question_58692636(self): + def test_stackoverflow_question_58692636(self) -> None: """ https://stackoverflow.com/questions/58692636/python-script-fails-to-extract-data-from-xml/58695393#58695393 """ @@ -120,6 +120,7 @@ def test_stackoverflow_question_58692636(self): # print(props.dump()) for key, value in props.items(): # print(key, value["#text"]) + assert isinstance(key, str) self.assertTrue(key.startswith("d:")) self.assertTrue(value["#text"] is not None) # print('-----') diff --git a/tests/stackoverflow/test_question_58827592.py b/tests/stackoverflow/test_question_58827592.py index e8ed7898..3316573c 100644 --- a/tests/stackoverflow/test_question_58827592.py +++ b/tests/stackoverflow/test_question_58827592.py @@ -1,8 +1,12 @@ +from __future__ import annotations + import unittest +from collections.abc import Mapping, Sequence +from typing import Any class stackoverflow_question_58827592_test_case(unittest.TestCase): - def test_stackoverflow_question_58827592(self): + def test_stackoverflow_question_58827592(self) -> None: """ https://stackoverflow.com/questions/58827592/is-there-a-way-to-convert-csv-columns-into-hierarchical-relationships """ @@ -39,7 +43,9 @@ def test_stackoverflow_question_58827592(self): data_output["children"] = [] - def transform_data(d, key, value): + def transform_data( + d: Mapping[str, Any] | Sequence[Any] | None, key: int | str, value: Any + ) -> None: if isinstance(value, dict): value.update({"name": key, "children": []}) diff --git a/tests/stackoverflow/test_question_59176476.py b/tests/stackoverflow/test_question_59176476.py index 0b540a73..b74b0b8f 100644 --- a/tests/stackoverflow/test_question_59176476.py +++ b/tests/stackoverflow/test_question_59176476.py @@ -2,7 +2,7 @@ class stackoverflow_question_59176476_test_case(unittest.TestCase): - def test_stackoverflow_question_59176476(self): + def test_stackoverflow_question_59176476(self) -> None: """ https://stackoverflow.com/questions/59176476/in-python-how-to-parse-a-multi-layered-json """ diff --git a/tests/stackoverflow/test_question_60066331.py b/tests/stackoverflow/test_question_60066331.py index 7b335b0e..8390138b 100644 --- a/tests/stackoverflow/test_question_60066331.py +++ b/tests/stackoverflow/test_question_60066331.py @@ -1,14 +1,15 @@ import unittest +from typing import Any class stackoverflow_question_60066331_test_case(unittest.TestCase): - def test_stackoverflow_question_60066331(self): + def test_stackoverflow_question_60066331(self) -> None: """ https://stackoverflow.com/questions/60066331/find-elements-in-python-dict """ from benedict import benedict as bdict - d = bdict( + d: bdict[Any] = bdict( { "ResponseMetadata": {"NOT IMPORTANT"}, "hasMoreResults": True, diff --git a/tests/stackoverflow/test_question_60072709.py b/tests/stackoverflow/test_question_60072709.py index 0cd0862e..a6f72f9d 100644 --- a/tests/stackoverflow/test_question_60072709.py +++ b/tests/stackoverflow/test_question_60072709.py @@ -1,8 +1,9 @@ import unittest +from typing import Any class stackoverflow_question_60072709_test_case(unittest.TestCase): - def test_stackoverflow_question_60072709(self): + def test_stackoverflow_question_60072709(self) -> None: """ https://stackoverflow.com/questions/60072709/i-want-to-convert-sample-json-data-into-nested-json-using-specific-key-value-in """ @@ -599,7 +600,7 @@ def test_stackoverflow_question_60072709(self): keys = list(data.keys()) items = [] for key in keys: - item = bdict(data.get(key)) + item: bdict[Any] = bdict(data.get(key)) # move all items to the top level items += list(item["breakdown"]) item["breakdown"] = [] @@ -611,7 +612,7 @@ def test_stackoverflow_question_60072709(self): if "parent_id" in item: item["parent_id"] = int(item["parent_id"]) - items_dict = bdict({"items": items}) + items_dict: bdict[Any] = bdict({"items": items}) items_dict["items_nested"] = items_dict.nest( "items", id_key="id", parent_id_key="parent_id", children_key="breakdown" ) diff --git a/tests/stackoverflow/test_question_60276839.py b/tests/stackoverflow/test_question_60276839.py index 71daba6f..3fb1e1cc 100644 --- a/tests/stackoverflow/test_question_60276839.py +++ b/tests/stackoverflow/test_question_60276839.py @@ -1,8 +1,10 @@ +from __future__ import annotations + import unittest class stackoverflow_question_60276839_test_case(unittest.TestCase): - def test_stackoverflow_question_60276839(self): + def test_stackoverflow_question_60276839(self) -> None: """ https://stackoverflow.com/questions/60276839/merge-list-of-nested-dictionaries """ @@ -26,7 +28,7 @@ def test_stackoverflow_question_60276839(self): {"12160150": {"Media Fee": "549173.00"}}, {"12160150": {"Platform Fee": "17573.00"}}, ] - data_output = bdict() + data_output: bdict[dict[str, str]] = bdict() data_output.merge(*data_input) # print(data_output.dump()) diff --git a/tests/utils/test_type_util.py b/tests/utils/test_type_util.py index 757d8878..2235b3d2 100644 --- a/tests/utils/test_type_util.py +++ b/tests/utils/test_type_util.py @@ -10,7 +10,7 @@ class type_util_test_case(unittest.TestCase): This class describes a type utility test case. """ - def test_is_bool(self): + def test_is_bool(self) -> None: f = type_util.is_bool self.assertFalse(f(None)) self.assertTrue(f(True)) @@ -26,7 +26,7 @@ def test_is_bool(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_collection(self): + def test_is_collection(self) -> None: f = type_util.is_collection self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -42,7 +42,7 @@ def test_is_collection(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_datetime(self): + def test_is_datetime(self) -> None: f = type_util.is_datetime self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -58,7 +58,7 @@ def test_is_datetime(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_decimal(self): + def test_is_decimal(self) -> None: f = type_util.is_decimal self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -74,7 +74,7 @@ def test_is_decimal(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_dict(self): + def test_is_dict(self) -> None: f = type_util.is_dict self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -90,7 +90,7 @@ def test_is_dict(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_dict_or_list(self): + def test_is_dict_or_list(self) -> None: f = type_util.is_dict_or_list self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -106,7 +106,7 @@ def test_is_dict_or_list(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_dict_or_list_or_tuple(self): + def test_is_dict_or_list_or_tuple(self) -> None: f = type_util.is_dict_or_list_or_tuple self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -122,7 +122,7 @@ def test_is_dict_or_list_or_tuple(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_float(self): + def test_is_float(self) -> None: f = type_util.is_float self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -138,7 +138,7 @@ def test_is_float(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_function(self): + def test_is_function(self) -> None: f = type_util.is_function self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -154,7 +154,7 @@ def test_is_function(self): self.assertFalse(f("hello world")) self.assertTrue(f(lambda a: a)) - def test_is_integer(self): + def test_is_integer(self) -> None: f = type_util.is_integer self.assertFalse(f(None)) self.assertTrue(f(True)) @@ -170,7 +170,7 @@ def test_is_integer(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_json_serializable(self): + def test_is_json_serializable(self) -> None: f = type_util.is_json_serializable self.assertTrue(f(None)) self.assertTrue(f(True)) @@ -186,7 +186,7 @@ def test_is_json_serializable(self): self.assertTrue(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_list(self): + def test_is_list(self) -> None: f = type_util.is_list self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -202,7 +202,7 @@ def test_is_list(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_list_or_tuple(self): + def test_is_list_or_tuple(self) -> None: f = type_util.is_list_or_tuple self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -218,7 +218,7 @@ def test_is_list_or_tuple(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_none(self): + def test_is_none(self) -> None: f = type_util.is_none self.assertTrue(f(None)) self.assertFalse(f(True)) @@ -234,7 +234,7 @@ def test_is_none(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_not_none(self): + def test_is_not_none(self) -> None: f = type_util.is_not_none self.assertFalse(f(None)) self.assertTrue(f(True)) @@ -250,7 +250,7 @@ def test_is_not_none(self): self.assertTrue(f("hello world")) self.assertTrue(f(lambda a: a)) - def test_is_set(self): + def test_is_set(self) -> None: f = type_util.is_set self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -266,7 +266,7 @@ def test_is_set(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_string(self): + def test_is_string(self) -> None: f = type_util.is_string self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -282,7 +282,7 @@ def test_is_string(self): self.assertTrue(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_tuple(self): + def test_is_tuple(self) -> None: f = type_util.is_tuple self.assertFalse(f(None)) self.assertFalse(f(True)) @@ -298,7 +298,7 @@ def test_is_tuple(self): self.assertFalse(f("hello world")) self.assertFalse(f(lambda a: a)) - def test_is_uuid(self): + def test_is_uuid(self) -> None: f = type_util.is_uuid self.assertTrue(f("ca761232ed4211cebacd00aa0057b223")) self.assertTrue(f("CA761232-ED42-11CE-BACD-00AA0057B223")) diff --git a/tox.ini b/tox.ini index 1750818e..f3d83cd1 100644 --- a/tox.ini +++ b/tox.ini @@ -16,6 +16,6 @@ deps = -r requirements-test.txt commands = - pre-commit run -a + pre-commit run --all-files coverage run --append --source=benedict -m unittest coverage report --show-missing --ignore-errors