From fb70739e65861d216f7ea3b518e260fca094ce81 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Mon, 18 Jul 2022 23:14:40 +0200 Subject: [PATCH 01/15] Add pre-commit config --- .pre-commit-config.yaml | 46 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d922f869..372cd001 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,15 @@ repos: - - repo: https://github.com/ambv/black - rev: 22.3.0 + - repo: https://github.com/psf/black + rev: 22.6.0 hooks: - id: black - - repo: https://github.com/timothycrosley/isort + - repo: https://github.com/pycqa/isort rev: 5.10.1 hooks: - id: isort - additional_dependencies: [toml] - exclude: ^.*/?setup\.py$ + args: [--add-import, from __future__ import annotations] + exclude: docs/.*|^.*__init__\.py$ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 @@ -19,3 +19,39 @@ repos: - id: end-of-file-fixer exclude: ^tests/(toml-test|toml-spec-tests)/.* - id: debug-statements + + - repo: https://github.com/asottile/yesqa + rev: v1.3.0 + hooks: + - id: yesqa + additional_dependencies: &flake8_deps +# - flake8-annotations==2.9.0 +# - flake8-broken-line==0.4.0 + - flake8-bugbear==22.4.25 +# - flake8-comprehensions==3.10.0 +# - flake8-eradicate==1.2.1 +# - flake8-quotes==3.3.1 +# - flake8-simplify==0.19.2 +# - flake8-tidy-imports==4.8.0 +# - flake8-type-checking==1.5.0 +# - flake8-typing-imports==1.12.0 +# - flake8-use-fstring==1.3 +# - pep8-naming==0.12.1 + + - repo: https://github.com/asottile/pyupgrade + rev: v2.37.1 + hooks: + - id: pyupgrade + args: [--py36-plus] + + - repo: https://github.com/hadialqattan/pycln + rev: v2.0.1 + hooks: + - id: pycln + args: [--all] + + - repo: https://github.com/pycqa/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + additional_dependencies: *flake8_deps From 60abdac8b3b54dede22ef9f6c3e365c890062e91 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Mon, 18 Jul 2022 23:21:47 +0200 Subject: [PATCH 02/15] First pre-commit swipe (isort, flake8-bugbear, black, pyupgrade) --- .pre-commit-config.yaml | 10 +- tests/conftest.py | 2 + tests/test_api.py | 2 + tests/test_build.py | 2 + tests/test_items.py | 4 +- tests/test_parser.py | 2 + tests/test_toml_document.py | 2 + tests/test_toml_file.py | 2 + tests/test_toml_spec_tests.py | 2 + tests/test_toml_tests.py | 2 + tests/test_utils.py | 2 + tests/test_write.py | 2 + tests/util.py | 4 +- tomlkit/_compat.py | 6 +- tomlkit/_utils.py | 8 +- tomlkit/api.py | 16 +-- tomlkit/container.py | 75 ++++++-------- tomlkit/exceptions.py | 7 +- tomlkit/items.py | 177 +++++++++++++++++----------------- tomlkit/parser.py | 32 +++--- tomlkit/source.py | 23 +++-- tomlkit/toml_char.py | 26 ++--- tomlkit/toml_document.py | 2 + tomlkit/toml_file.py | 2 + 24 files changed, 213 insertions(+), 199 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 372cd001..02c5bfd7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,11 +44,11 @@ repos: - id: pyupgrade args: [--py36-plus] - - repo: https://github.com/hadialqattan/pycln - rev: v2.0.1 - hooks: - - id: pycln - args: [--all] +# - repo: https://github.com/hadialqattan/pycln +# rev: v2.0.1 +# hooks: +# - id: pycln +# args: [--all] - repo: https://github.com/pycqa/flake8 rev: 4.0.1 diff --git a/tests/conftest.py b/tests/conftest.py index 73333790..710ce012 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import pytest diff --git a/tests/test_api.py b/tests/test_api.py index 3a27db4f..0e048e04 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import io import json import os diff --git a/tests/test_build.py b/tests/test_build.py index 136abb80..3972f32f 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import datetime from tomlkit import aot diff --git a/tests/test_items.py b/tests/test_items.py index f71bd602..c0b7cb2b 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import math import pickle @@ -120,7 +122,7 @@ def test_aot_unwrap(): d = item([{"a": "A"}, {"b": "B"}]) unwrapped = d.unwrap() assert_is_ppo(unwrapped, list) - for du, dw in zip(unwrapped, d): + for du, _dw in zip(unwrapped, d): assert_is_ppo(du, dict) for ku in du: vu = du[ku] diff --git a/tests/test_parser.py b/tests/test_parser.py index c293f828..2efa2cd2 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from tomlkit.exceptions import EmptyTableNameError diff --git a/tests/test_toml_document.py b/tests/test_toml_document.py index f5a5e8cc..f238d722 100644 --- a/tests/test_toml_document.py +++ b/tests/test_toml_document.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import copy import json import pickle diff --git a/tests/test_toml_file.py b/tests/test_toml_file.py index ae20656a..5774f728 100644 --- a/tests/test_toml_file.py +++ b/tests/test_toml_file.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from tomlkit.toml_document import TOMLDocument diff --git a/tests/test_toml_spec_tests.py b/tests/test_toml_spec_tests.py index 57ad1812..d56a18ce 100644 --- a/tests/test_toml_spec_tests.py +++ b/tests/test_toml_spec_tests.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import os import re diff --git a/tests/test_toml_tests.py b/tests/test_toml_tests.py index 88a1de35..0ac12461 100644 --- a/tests/test_toml_tests.py +++ b/tests/test_toml_tests.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import pytest diff --git a/tests/test_utils.py b/tests/test_utils.py index 7dde452c..287a8dda 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from datetime import date from datetime import datetime as dt from datetime import time diff --git a/tests/test_write.py b/tests/test_write.py index 58752d93..2ed2799c 100644 --- a/tests/test_write.py +++ b/tests/test_write.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from tomlkit import dumps from tomlkit import loads diff --git a/tests/util.py b/tests/util.py index 3a4c758b..91b1d4ba 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from tomlkit.items import AoT from tomlkit.items import Array from tomlkit.items import Bool @@ -43,7 +45,7 @@ def assert_not_tomlkit_type(v): - for i, T in enumerate(TOMLKIT_TYPES): + for _, T in enumerate(TOMLKIT_TYPES): assert not isinstance(v, T) diff --git a/tomlkit/_compat.py b/tomlkit/_compat.py index 1295df69..e7811134 100644 --- a/tomlkit/_compat.py +++ b/tomlkit/_compat.py @@ -1,14 +1,14 @@ +from __future__ import annotations + import sys from typing import Any -from typing import List -from typing import Optional PY38 = sys.version_info >= (3, 8) -def decode(string: Any, encodings: Optional[List[str]] = None): +def decode(string: Any, encodings: list[str] | None = None): if not isinstance(string, bytes): return string diff --git a/tomlkit/_utils.py b/tomlkit/_utils.py index 5c8113f0..99bf8d8a 100644 --- a/tomlkit/_utils.py +++ b/tomlkit/_utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import re from collections.abc import Mapping @@ -7,7 +9,6 @@ from datetime import timedelta from datetime import timezone from typing import Collection -from typing import Union from ._compat import decode @@ -41,7 +42,7 @@ _utc = timezone(timedelta(), "UTC") -def parse_rfc3339(string: str) -> Union[datetime, date, time]: +def parse_rfc3339(string: str) -> datetime | date | time: m = RFC_3339_DATETIME.match(string) if m: year = int(m.group(1)) @@ -125,7 +126,6 @@ def escape_string(s: str, escape_sequences: Collection[str] = _basic_escapes) -> res = [] start = 0 - l = len(s) def flush(inc=1): if start != i: @@ -134,7 +134,7 @@ def flush(inc=1): return i + inc i = 0 - while i < l: + while i < len(s): for seq in escape_sequences: seq_len = len(seq) if s[i:].startswith(seq): diff --git a/tomlkit/api.py b/tomlkit/api.py index 273efc55..214a20eb 100644 --- a/tomlkit/api.py +++ b/tomlkit/api.py @@ -1,10 +1,10 @@ +from __future__ import annotations + import datetime as _datetime from collections.abc import Mapping from typing import IO from typing import Iterable -from typing import Tuple -from typing import Union from ._utils import parse_rfc3339 from .container import Container @@ -33,7 +33,7 @@ from .toml_document import TOMLDocument -def loads(string: Union[str, bytes]) -> TOMLDocument: +def loads(string: str | bytes) -> TOMLDocument: """ Parses a string into a TOMLDocument. @@ -75,7 +75,7 @@ def dump(data: Mapping, fp: IO[str], *, sort_keys: bool = False) -> None: fp.write(dumps(data, sort_keys=sort_keys)) -def parse(string: Union[str, bytes]) -> TOMLDocument: +def parse(string: str | bytes) -> TOMLDocument: """ Parses a string or bytes into a TOMLDocument. """ @@ -90,12 +90,12 @@ def document() -> TOMLDocument: # Items -def integer(raw: Union[str, int]) -> Integer: +def integer(raw: str | int) -> Integer: """Create an integer item from a number or string.""" return item(int(raw)) -def float_(raw: Union[str, float]) -> Float: +def float_(raw: str | float) -> Float: """Create an float item from a number or string.""" return item(float(raw)) @@ -223,7 +223,7 @@ def aot() -> AoT: return AoT([]) -def key(k: Union[str, Iterable[str]]) -> Key: +def key(k: str | Iterable[str]) -> Key: """Create a key from a string. When a list of string is given, it will create a dotted key. @@ -260,7 +260,7 @@ def value(raw: str) -> _Item: return v -def key_value(src: str) -> Tuple[Key, _Item]: +def key_value(src: str) -> tuple[Key, _Item]: """Parse a key-value pair from a string. :Example: diff --git a/tomlkit/container.py b/tomlkit/container.py index 070c583b..13bc02e3 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -1,12 +1,9 @@ +from __future__ import annotations + import copy from typing import Any -from typing import Dict from typing import Iterator -from typing import List -from typing import Optional -from typing import Tuple -from typing import Union from ._compat import decode from ._utils import merge_dicts @@ -37,13 +34,13 @@ class Container(_CustomDict): """ def __init__(self, parsed: bool = False) -> None: - self._map: Dict[Key, int] = {} - self._body: List[Tuple[Optional[Key], Item]] = [] + self._map: dict[Key, int] = {} + self._body: list[tuple[Key | None, Item]] = [] self._parsed = parsed self._table_keys = [] @property - def body(self) -> List[Tuple[Optional[Key], Item]]: + def body(self) -> list[tuple[Key | None, Item]]: return self._body def unwrap(self) -> str: @@ -66,7 +63,7 @@ def unwrap(self) -> str: return unwrapped @property - def value(self) -> Dict[Any, Any]: + def value(self) -> dict[Any, Any]: d = {} for k, v in self._body: if k is None: @@ -95,9 +92,7 @@ def parsing(self, parsing: bool) -> None: for t in v.body: t.value.parsing(parsing) - def add( - self, key: Union[Key, Item, str], item: Optional[Item] = None - ) -> "Container": + def add(self, key: Key | Item | str, item: Item | None = None) -> Container: """ Adds an item to the current Container. @@ -176,7 +171,7 @@ def _handle_dotted_key(self, key: Key, value: Item) -> None: table = table[_name] - def append(self, key: Union[Key, str, None], item: Item) -> "Container": + def append(self, key: Key | str | None, item: Item) -> Container: """Similar to :meth:`add` but both key and value must be given.""" if not isinstance(key, Key) and key is not None: key = SingleKey(key) @@ -365,7 +360,7 @@ def _remove_at(self, idx: int) -> None: dict.__delitem__(self, key.key) self._map.pop(key) - def remove(self, key: Union[Key, str]) -> "Container": + def remove(self, key: Key | str) -> Container: """Remove a key from the container.""" if not isinstance(key, Key): key = SingleKey(key) @@ -385,8 +380,8 @@ def remove(self, key: Union[Key, str]) -> "Container": return self def _insert_after( - self, key: Union[Key, str], other_key: Union[Key, str], item: Any - ) -> "Container": + self, key: Key | str, other_key: Key | str, item: Any + ) -> Container: if key is None: raise ValueError("Key cannot be null in insert_after()") @@ -431,7 +426,7 @@ def _insert_after( return self - def _insert_at(self, idx: int, key: Union[Key, str], item: Any) -> "Container": + def _insert_at(self, idx: int, key: Key | str, item: Any) -> Container: if idx > len(self._body) - 1: raise ValueError(f"Unable to insert at position {idx}") @@ -472,7 +467,7 @@ def _insert_at(self, idx: int, key: Union[Key, str], item: Any) -> "Container": return self - def item(self, key: Union[Key, str]) -> Item: + def item(self, key: Key | str) -> Item: """Get an item for the given key.""" if not isinstance(key, Key): key = SingleKey(key) @@ -489,7 +484,7 @@ def item(self, key: Union[Key, str]) -> Item: return self._body[idx][1] - def last_item(self) -> Optional[Item]: + def last_item(self) -> Item | None: """Get the last item.""" if self._body: return self._body[-1][1] @@ -510,9 +505,7 @@ def as_string(self) -> str: return s - def _render_table( - self, key: Key, table: Table, prefix: Optional[str] = None - ) -> str: + def _render_table(self, key: Key, table: Table, prefix: str | None = None) -> str: cur = "" if table.display_name is not None: @@ -577,7 +570,7 @@ def _render_aot(self, key, aot, prefix=None): return cur - def _render_aot_table(self, table: Table, prefix: Optional[str] = None) -> str: + def _render_aot_table(self, table: Table, prefix: str | None = None) -> str: cur = "" _key = prefix or "" @@ -637,7 +630,7 @@ def __iter__(self) -> Iterator[str]: return iter(dict.keys(self)) # Dictionary methods - def __getitem__(self, key: Union[Key, str]) -> Union[Item, "Container"]: + def __getitem__(self, key: Key | str) -> Item | Container: if not isinstance(key, Key): key = SingleKey(key) @@ -657,23 +650,21 @@ def __getitem__(self, key: Union[Key, str]) -> Union[Item, "Container"]: return item - def __setitem__(self, key: Union[Key, str], value: Any) -> None: + def __setitem__(self, key: Key | str, value: Any) -> None: if key is not None and key in self: old_key = next(filter(lambda k: k == key, self._map)) self._replace(old_key, key, value) else: self.append(key, value) - def __delitem__(self, key: Union[Key, str]) -> None: + def __delitem__(self, key: Key | str) -> None: self.remove(key) - def setdefault(self, key: Union[Key, str], default: Any) -> Any: + def setdefault(self, key: Key | str, default: Any) -> Any: super().setdefault(key, default=default) return self[key] - def _replace( - self, key: Union[Key, str], new_key: Union[Key, str], value: Item - ) -> None: + def _replace(self, key: Key | str, new_key: Key | str, value: Item) -> None: if not isinstance(key, Key): key = SingleKey(key) @@ -684,7 +675,7 @@ def _replace( self._replace_at(idx, new_key, value) def _replace_at( - self, idx: Union[int, Tuple[int]], new_key: Union[Key, str], value: Item + self, idx: int | tuple[int], new_key: Key | str, value: Item ) -> None: value = _item(value) @@ -780,10 +771,10 @@ def __setstate__(self, state): if key is not None: dict.__setitem__(self, key.key, item.value) - def copy(self) -> "Container": + def copy(self) -> Container: return copy.copy(self) - def __copy__(self) -> "Container": + def __copy__(self) -> Container: c = self.__class__(self._parsed) for k, v in dict.items(self): dict.__setitem__(c, k, v) @@ -794,8 +785,8 @@ def __copy__(self) -> "Container": return c def _previous_item_with_index( - self, idx: Optional[int] = None, ignore=(Null,) - ) -> Optional[Tuple[int, Item]]: + self, idx: int | None = None, ignore=(Null,) + ) -> tuple[int, Item] | None: """Find the immediate previous item before index ``idx``""" if idx is None or idx > len(self._body): idx = len(self._body) @@ -805,9 +796,7 @@ def _previous_item_with_index( return i, v return None - def _previous_item( - self, idx: Optional[int] = None, ignore=(Null,) - ) -> Optional[Item]: + def _previous_item(self, idx: int | None = None, ignore=(Null,)) -> Item | None: """Find the immediate previous item before index ``idx``. If ``idx`` is not given, the last item is returned. """ @@ -816,7 +805,7 @@ def _previous_item( class OutOfOrderTableProxy(_CustomDict): - def __init__(self, container: Container, indices: Tuple[int]) -> None: + def __init__(self, container: Container, indices: tuple[int]) -> None: self._container = container self._internal_container = Container(True) self._tables = [] @@ -841,13 +830,13 @@ def unwrap(self) -> str: def value(self): return self._internal_container.value - def __getitem__(self, key: Union[Key, str]) -> Any: + def __getitem__(self, key: Key | str) -> Any: if key not in self._internal_container: raise NonExistentKey(key) return self._internal_container[key] - def __setitem__(self, key: Union[Key, str], item: Any) -> None: + def __setitem__(self, key: Key | str, item: Any) -> None: if key in self._tables_map: table = self._tables[self._tables_map[key]] table[key] = item @@ -869,7 +858,7 @@ def _remove_table(self, table: Table) -> None: self._container._remove_at(idx) break - def __delitem__(self, key: Union[Key, str]) -> None: + def __delitem__(self, key: Key | str) -> None: if key in self._tables_map: table = self._tables[self._tables_map[key]] del table[key] @@ -889,7 +878,7 @@ def __iter__(self) -> Iterator[str]: def __len__(self) -> int: return dict.__len__(self) - def setdefault(self, key: Union[Key, str], default: Any) -> Any: + def setdefault(self, key: Key | str, default: Any) -> Any: super().setdefault(key, default=default) return self[key] diff --git a/tomlkit/exceptions.py b/tomlkit/exceptions.py index d43abd1c..88977787 100644 --- a/tomlkit/exceptions.py +++ b/tomlkit/exceptions.py @@ -1,5 +1,6 @@ +from __future__ import annotations + from typing import Collection -from typing import Optional class TOMLKitError(Exception): @@ -14,7 +15,7 @@ class ParseError(ValueError, TOMLKitError): location within the line where the error was encountered. """ - def __init__(self, line: int, col: int, message: Optional[str] = None) -> None: + def __init__(self, line: int, col: int, message: str | None = None) -> None: self._line = line self._col = col @@ -169,7 +170,7 @@ class InternalParserError(ParseError): An error that indicates a bug in the parser. """ - def __init__(self, line: int, col: int, message: Optional[str] = None) -> None: + def __init__(self, line: int, col: int, message: str | None = None) -> None: msg = "Internal parser error" if message: msg += f" ({message})" diff --git a/tomlkit/items.py b/tomlkit/items.py index 0e36b88e..91296507 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import abc import copy import re @@ -12,14 +14,10 @@ from typing import TYPE_CHECKING from typing import Any from typing import Collection -from typing import Dict from typing import Iterable from typing import Iterator -from typing import List -from typing import Optional from typing import Sequence from typing import TypeVar -from typing import Union from typing import cast from typing import overload @@ -63,59 +61,57 @@ class _CustomDict(MutableMapping, dict): @overload -def item(value: bool) -> "Bool": +def item(value: bool) -> Bool: ... @overload -def item(value: int) -> "Integer": +def item(value: int) -> Integer: ... @overload -def item(value: float) -> "Float": +def item(value: float) -> Float: ... @overload -def item(value: str) -> "String": +def item(value: str) -> String: ... @overload -def item(value: datetime) -> "DateTime": +def item(value: datetime) -> DateTime: ... @overload -def item(value: date) -> "Date": +def item(value: date) -> Date: ... @overload -def item(value: time) -> "Time": +def item(value: time) -> Time: ... @overload -def item(value: Sequence[dict]) -> "AoT": +def item(value: Sequence[dict]) -> AoT: ... @overload -def item(value: Sequence) -> "Array": +def item(value: Sequence) -> Array: ... @overload -def item(value: dict, _parent: "Array" = ..., _sort_keys: bool = ...) -> "InlineTable": +def item(value: dict, _parent: Array = ..., _sort_keys: bool = ...) -> InlineTable: ... @overload -def item( - value: dict, _parent: Optional["Item"] = ..., _sort_keys: bool = ... -) -> "Table": +def item(value: dict, _parent: Item | None = ..., _sort_keys: bool = ...) -> Table: ... @@ -124,9 +120,7 @@ def item(value: ItemT) -> ItemT: ... -def item( - value: Any, _parent: Optional["Item"] = None, _sort_keys: bool = False -) -> "Item": +def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> Item: """Create a TOML item from a Python object. :Example: @@ -233,8 +227,17 @@ class StringType(Enum): # Multi Line Literal MLL = "'''" + def __init__(self, *args, **kwargs): + self.unit = property(lru_cache(maxsize=None)(self._unit)) + self.is_basic = lru_cache(maxsize=None)(self._is_basic) + self.is_literal = lru_cache(maxsize=None)(self._is_literal) + self.is_singleline = lru_cache(maxsize=None)(self._is_singleline) + self.is_multiline = lru_cache(maxsize=None)(self._is_multiline) + self.toggle = lru_cache(maxsize=None)(self._toggle) + super().__init__(*args, **kwargs) + @classmethod - def select(cls, literal=False, multiline=False) -> "StringType": + def select(cls, literal=False, multiline=False) -> StringType: return { (False, False): cls.SLB, (False, True): cls.MLB, @@ -266,29 +269,22 @@ def invalid_sequences(self) -> Collection[str]: StringType.MLL: (forbidden_in_literal | {"'''"}) - allowed_in_multiline, }[self] - @property - @lru_cache(maxsize=None) - def unit(self) -> str: + def _unit(self) -> str: return self.value[0] - @lru_cache(maxsize=None) - def is_basic(self) -> bool: + def _is_basic(self) -> bool: return self in {StringType.SLB, StringType.MLB} - @lru_cache(maxsize=None) - def is_literal(self) -> bool: + def _is_literal(self) -> bool: return self in {StringType.SLL, StringType.MLL} - @lru_cache(maxsize=None) - def is_singleline(self) -> bool: + def _is_singleline(self) -> bool: return self in {StringType.SLB, StringType.SLL} - @lru_cache(maxsize=None) - def is_multiline(self) -> bool: + def _is_multiline(self) -> bool: return self in {StringType.MLB, StringType.MLL} - @lru_cache(maxsize=None) - def toggle(self) -> "StringType": + def _toggle(self) -> StringType: return { StringType.SLB: StringType.MLB, StringType.MLB: StringType.SLB, @@ -301,8 +297,11 @@ class BoolType(Enum): TRUE = "true" FALSE = "false" - @lru_cache(maxsize=None) - def __bool__(self): + def __init__(self, *args, **kwargs): + self.__bool__ = lru_cache(maxsize=None)(self._bool) + super().__init__(*args, **kwargs) + + def _bool(self): return {BoolType.TRUE: True, BoolType.FALSE: False}[self] def __iter__(self): @@ -336,7 +335,7 @@ def __init__( self.trail = trail - def copy(self) -> "Trivia": + def copy(self) -> Trivia: return type(self)(self.indent, self.comment_ws, self.comment, self.trail) @@ -358,7 +357,7 @@ class Key(abc.ABC): sep: str _original: str - _keys: List["SingleKey"] + _keys: list[SingleKey] _dotted: bool key: str @@ -374,10 +373,10 @@ def is_dotted(self) -> bool: """If the key is followed by other keys""" return self._dotted - def __iter__(self) -> Iterator["SingleKey"]: + def __iter__(self) -> Iterator[SingleKey]: return iter(self._keys) - def concat(self, other: "Key") -> "DottedKey": + def concat(self, other: Key) -> DottedKey: """Concatenate keys into a dotted key""" keys = self._keys + other._keys return DottedKey(keys, sep=self.sep) @@ -403,9 +402,9 @@ class SingleKey(Key): def __init__( self, k: str, - t: Optional[KeyType] = None, - sep: Optional[str] = None, - original: Optional[str] = None, + t: KeyType | None = None, + sep: str | None = None, + original: str | None = None, ) -> None: if t is None: if not k or any( @@ -452,8 +451,8 @@ class DottedKey(Key): def __init__( self, keys: Iterable[Key], - sep: Optional[str] = None, - original: Optional[str] = None, + sep: str | None = None, + original: str | None = None, ) -> None: self._keys = list(keys) if original is None: @@ -498,7 +497,7 @@ def unwrap(self): # Helpers - def comment(self, comment: str) -> "Item": + def comment(self, comment: str) -> Item: """Attach a comment to this item""" if not comment.strip().startswith("#"): comment = "# " + comment @@ -508,7 +507,7 @@ def comment(self, comment: str) -> "Item": return self - def indent(self, indent: int) -> "Item": + def indent(self, indent: int) -> Item: """Indent this item with given number of spaces""" if self._trivia.indent.startswith("\n"): self._trivia.indent = "\n" + " " * indent @@ -602,7 +601,7 @@ class Integer(int, Item): An integer literal. """ - def __new__(cls, value: int, trivia: Trivia, raw: str) -> "Integer": + def __new__(cls, value: int, trivia: Trivia, raw: str) -> Integer: return super().__new__(cls, value) def __init__(self, _: int, trivia: Trivia, raw: str) -> None: @@ -799,7 +798,7 @@ def __new__( minute: int, second: int, microsecond: int, - tzinfo: Optional[tzinfo], + tzinfo: tzinfo | None, *_: Any, **kwargs: Any, ) -> datetime: @@ -825,9 +824,9 @@ def __init__( minute: int, second: int, microsecond: int, - tzinfo: Optional[tzinfo], - trivia: Optional[Trivia] = None, - raw: Optional[str] = None, + tzinfo: tzinfo | None, + trivia: Trivia | None = None, + raw: str | None = None, **kwargs: Any, ) -> None: super().__init__(trivia or Trivia()) @@ -906,7 +905,7 @@ def astimezone(self, tz: tzinfo) -> datetime: return result return self._new(result) - def _new(self, result) -> "DateTime": + def _new(self, result) -> DateTime: raw = result.isoformat() return DateTime( @@ -1009,7 +1008,7 @@ def __new__( minute: int, second: int, microsecond: int, - tzinfo: Optional[tzinfo], + tzinfo: tzinfo | None, *_: Any, ) -> time: return time.__new__(cls, hour, minute, second, microsecond, tzinfo) @@ -1020,7 +1019,7 @@ def __init__( minute: int, second: int, microsecond: int, - tzinfo: Optional[tzinfo], + tzinfo: tzinfo | None, trivia: Trivia, raw: str, ) -> None: @@ -1078,7 +1077,7 @@ class Array(Item, _CustomList): def __init__(self, value: list, trivia: Trivia, multiline: bool = False) -> None: super().__init__(trivia) - self._index_map: Dict[int, int] = {} + self._index_map: dict[int, int] = {} list.__init__( self, [v.value for v in value if not isinstance(v, (Whitespace, Comment))] ) @@ -1104,7 +1103,7 @@ def discriminant(self) -> int: def value(self) -> list: return self - def multiline(self, multiline: bool) -> "Array": + def multiline(self, multiline: bool) -> Array: """Change the array to display in multiline or not. :Example: @@ -1153,7 +1152,7 @@ def add_line( self, *items: Any, indent: str = " ", - comment: Optional[str] = None, + comment: str | None = None, add_comma: bool = True, newline: bool = True, ) -> None: @@ -1225,10 +1224,10 @@ def clear(self) -> None: def __len__(self) -> int: return list.__len__(self) - def __getitem__(self, key: Union[int, slice]) -> Any: + def __getitem__(self, key: int | slice) -> Any: return list.__getitem__(self, key) - def __setitem__(self, key: Union[int, slice], value: Any) -> Any: + def __setitem__(self, key: int | slice, value: Any) -> Any: it = item(value, _parent=self) list.__setitem__(self, key, it.value) if isinstance(key, slice): @@ -1288,7 +1287,7 @@ def insert(self, pos: int, value: Any) -> None: self._reindex() - def __delitem__(self, key: Union[int, slice]): + def __delitem__(self, key: int | slice): length = len(self) list.__delitem__(self, key) @@ -1331,7 +1330,7 @@ def _getstate(self, protocol=3): class AbstractTable(Item, _CustomDict): """Common behaviour of both :class:`Table` and :class:`InlineTable`""" - def __init__(self, value: "container.Container", trivia: Trivia): + def __init__(self, value: container.Container, trivia: Trivia): Item.__init__(self, trivia) self._value = value @@ -1352,26 +1351,26 @@ def unwrap(self): return unwrapped @property - def value(self) -> "container.Container": + def value(self) -> container.Container: return self._value @overload - def append(self: AT, key: None, value: Union[Comment, Whitespace]) -> AT: + def append(self: AT, key: None, value: Comment | Whitespace) -> AT: ... @overload - def append(self: AT, key: Union[Key, str], value: Any) -> AT: + def append(self: AT, key: Key | str, value: Any) -> AT: ... def append(self, key, value): raise NotImplementedError @overload - def add(self: AT, value: Union[Comment, Whitespace]) -> AT: + def add(self: AT, value: Comment | Whitespace) -> AT: ... @overload - def add(self: AT, key: Union[Key, str], value: Any) -> AT: + def add(self: AT, key: Key | str, value: Any) -> AT: ... def add(self, key, value=None): @@ -1384,7 +1383,7 @@ def add(self, key, value=None): return self.append(key, value) - def remove(self: AT, key: Union[Key, str]) -> AT: + def remove(self: AT, key: Key | str) -> AT: self._value.remove(key) if isinstance(key, Key): @@ -1395,7 +1394,7 @@ def remove(self: AT, key: Union[Key, str]) -> AT: return self - def setdefault(self, key: Union[Key, str], default: Any) -> Any: + def setdefault(self, key: Key | str, default: Any) -> Any: super().setdefault(key, default) return self[key] @@ -1414,13 +1413,13 @@ def __iter__(self) -> Iterator[str]: def __len__(self) -> int: return len(self._value) - def __delitem__(self, key: Union[Key, str]) -> None: + def __delitem__(self, key: Key | str) -> None: self.remove(key) - def __getitem__(self, key: Union[Key, str]) -> Item: + def __getitem__(self, key: Key | str) -> Item: return cast(Item, self._value[key]) - def __setitem__(self, key: Union[Key, str], value: Any) -> None: + def __setitem__(self, key: Key | str, value: Any) -> None: if not isinstance(value, Item): value = item(value) @@ -1453,12 +1452,12 @@ class Table(AbstractTable): def __init__( self, - value: "container.Container", + value: container.Container, trivia: Trivia, is_aot_element: bool, is_super_table: bool = False, - name: Optional[str] = None, - display_name: Optional[str] = None, + name: str | None = None, + display_name: str | None = None, ) -> None: super().__init__(value, trivia) @@ -1471,7 +1470,7 @@ def __init__( def discriminant(self) -> int: return 9 - def __copy__(self) -> "Table": + def __copy__(self) -> Table: return type(self)( self._value.copy(), self._trivia.copy(), @@ -1512,7 +1511,7 @@ def append(self, key, _item): return self - def raw_append(self, key: Union[Key, str], _item: Any) -> "Table": + def raw_append(self, key: Key | str, _item: Any) -> Table: """Similar to :meth:`append` but does not copy indentation.""" if not isinstance(_item, Item): _item = item(_item) @@ -1542,7 +1541,7 @@ def as_string(self) -> str: # Helpers - def indent(self, indent: int) -> "Table": + def indent(self, indent: int) -> Table: """Indent the table with given number of spaces.""" super().indent(indent) @@ -1582,7 +1581,7 @@ class InlineTable(AbstractTable): """ def __init__( - self, value: "container.Container", trivia: Trivia, new: bool = False + self, value: container.Container, trivia: Trivia, new: bool = False ) -> None: super().__init__(value, trivia) @@ -1647,12 +1646,12 @@ def as_string(self) -> str: return buf - def __setitem__(self, key: Union[Key, str], value: Any) -> None: + def __setitem__(self, key: Key | str, value: Any) -> None: if hasattr(value, "trivia") and value.trivia.comment: value.trivia.comment = "" super().__setitem__(key, value) - def __copy__(self) -> "InlineTable": + def __copy__(self) -> InlineTable: return type(self)(self._value.copy(), self._trivia.copy(), self._new) def _getstate(self, protocol: int = 3) -> tuple: @@ -1704,7 +1703,7 @@ def _getstate(self, protocol=3): return self._t, str(self), self._original, self._trivia @classmethod - def from_raw(cls, value: str, type_=StringType.SLB, escape=True) -> "String": + def from_raw(cls, value: str, type_=StringType.SLB, escape=True) -> String: value = decode(value) invalid = type_.invalid_sequences @@ -1723,10 +1722,10 @@ class AoT(Item, _CustomList): """ def __init__( - self, body: List[Table], name: Optional[str] = None, parsed: bool = False + self, body: list[Table], name: str | None = None, parsed: bool = False ) -> None: self.name = name - self._body: List[Table] = [] + self._body: list[Table] = [] self._parsed = parsed super().__init__(Trivia(trail="")) @@ -1744,7 +1743,7 @@ def unwrap(self) -> str: return unwrapped @property - def body(self) -> List[Table]: + def body(self) -> list[Table]: return self._body @property @@ -1752,14 +1751,14 @@ def discriminant(self) -> int: return 12 @property - def value(self) -> List[Dict[Any, Any]]: + def value(self) -> list[dict[Any, Any]]: return [v.value for v in self._body] def __len__(self) -> int: return len(self._body) @overload - def __getitem__(self, key: slice) -> List[Table]: + def __getitem__(self, key: slice) -> list[Table]: ... @overload @@ -1769,10 +1768,10 @@ def __getitem__(self, key: int) -> Table: def __getitem__(self, key): return self._body[key] - def __setitem__(self, key: Union[slice, int], value: Any) -> None: + def __setitem__(self, key: slice | int, value: Any) -> None: raise NotImplementedError - def __delitem__(self, key: Union[slice, int]) -> None: + def __delitem__(self, key: slice | int) -> None: del self._body[key] list.__delitem__(self, key) diff --git a/tomlkit/parser.py b/tomlkit/parser.py index 2f94daa9..a40bd087 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -1,12 +1,8 @@ +from __future__ import annotations + import re import string -from typing import List -from typing import Optional -from typing import Tuple -from typing import Type -from typing import Union - from ._compat import decode from ._utils import RFC_3339_LOOSE from ._utils import _escaped @@ -67,7 +63,7 @@ def __init__(self, string: str) -> None: # Input to parse self._src = Source(decode(string)) - self._aot_stack: List[Key] = [] + self._aot_stack: list[Key] = [] @property def _state(self): @@ -91,14 +87,14 @@ def extract(self) -> str: """ return self._src.extract() - def inc(self, exception: Optional[Type[ParseError]] = None) -> bool: + def inc(self, exception: type[ParseError] | None = None) -> bool: """ Increments the parser if the end of the input has not been reached. Returns whether or not it was able to advance. """ return self._src.inc(exception=exception) - def inc_n(self, n: int, exception: Optional[Type[ParseError]] = None) -> bool: + def inc_n(self, n: int, exception: type[ParseError] | None = None) -> bool: """ Increments the parser by n characters if the end of the input has not been reached. @@ -204,7 +200,7 @@ def _is_child(self, parent: Key, child: Key) -> bool: return parent_parts == child_parts[: len(parent_parts)] - def _parse_item(self) -> Optional[Tuple[Optional[Key], Item]]: + def _parse_item(self) -> tuple[Key | None, Item] | None: """ Attempts to parse the next item and returns it, along with its key if the item is value-like. @@ -240,7 +236,7 @@ def _parse_item(self) -> Optional[Tuple[Optional[Key], Item]]: return self._parse_key_value(True) - def _parse_comment_trail(self, parse_trail: bool = True) -> Tuple[str, str, str]: + def _parse_comment_trail(self, parse_trail: bool = True) -> tuple[str, str, str]: """ Returns (comment_ws, comment, trail) If there is no comment, comment_ws and comment will @@ -301,7 +297,7 @@ def _parse_comment_trail(self, parse_trail: bool = True) -> Tuple[str, str, str] return comment_ws, comment, trail - def _parse_key_value(self, parse_comment: bool = False) -> Tuple[Key, Item]: + def _parse_key_value(self, parse_comment: bool = False) -> tuple[Key, Item]: # Leading indent self.mark() @@ -558,7 +554,7 @@ def _parse_array(self) -> Array: # Consume opening bracket, EOF here is an issue (middle of array) self.inc(exception=UnexpectedEofError) - elems: List[Item] = [] + elems: list[Item] = [] prev_value = None while True: # consume whitespace @@ -665,7 +661,7 @@ def _parse_inline_table(self) -> InlineTable: return InlineTable(elems, Trivia()) - def _parse_number(self, raw: str, trivia: Trivia) -> Optional[Item]: + def _parse_number(self, raw: str, trivia: Trivia) -> Item | None: # Leading zeros are not allowed sign = "" if raw.startswith(("+", "-")): @@ -877,8 +873,8 @@ def _parse_string(self, delim: StringType) -> String: self.inc(exception=UnexpectedEofError) def _parse_table( - self, parent_name: Optional[Key] = None, parent: Optional[Table] = None - ) -> Tuple[Key, Union[Table, AoT]]: + self, parent_name: Key | None = None, parent: Table | None = None + ) -> tuple[Key, Table | AoT]: """ Parses a table element. """ @@ -1032,7 +1028,7 @@ def _parse_table( return key, result - def _peek_table(self) -> Tuple[bool, Key]: + def _peek_table(self) -> tuple[bool, Key]: """ Peeks ahead non-intrusively by cloning then restoring the initial state of the parser. @@ -1096,7 +1092,7 @@ def _peek(self, n: int) -> str: break return buf - def _peek_unicode(self, is_long: bool) -> Tuple[Optional[str], Optional[str]]: + def _peek_unicode(self, is_long: bool) -> tuple[str | None, str | None]: """ Peeks ahead non-intrusively by cloning then restoring the initial state of the parser. diff --git a/tomlkit/source.py b/tomlkit/source.py index 6f82d946..89a606cd 100644 --- a/tomlkit/source.py +++ b/tomlkit/source.py @@ -1,8 +1,7 @@ +from __future__ import annotations + from copy import copy from typing import Any -from typing import Optional -from typing import Tuple -from typing import Type from .exceptions import ParseError from .exceptions import UnexpectedCharError @@ -12,15 +11,15 @@ class _State: def __init__( self, - source: "Source", - save_marker: Optional[bool] = False, - restore: Optional[bool] = False, + source: Source, + save_marker: bool | None = False, + restore: bool | None = False, ) -> None: self._source = source self._save_marker = save_marker self.restore = restore - def __enter__(self) -> "_State": + def __enter__(self) -> _State: # Entering this context manager - save the state self._chars = copy(self._source._chars) self._idx = self._source._idx @@ -44,7 +43,7 @@ class _StateHandler: State preserver for the Parser. """ - def __init__(self, source: "Source") -> None: + def __init__(self, source: Source) -> None: self._source = source self._states = [] @@ -107,7 +106,7 @@ def extract(self) -> str: """ return self[self._marker : self._idx] - def inc(self, exception: Optional[Type[ParseError]] = None) -> bool: + def inc(self, exception: type[ParseError] | None = None) -> bool: """ Increments the parser if the end of the input has not been reached. Returns whether or not it was able to advance. @@ -124,7 +123,7 @@ def inc(self, exception: Optional[Type[ParseError]] = None) -> bool: return False - def inc_n(self, n: int, exception: Optional[Type[ParseError]] = None) -> bool: + def inc_n(self, n: int, exception: type[ParseError] | None = None) -> bool: """ Increments the parser by n characters if the end of the input has not been reached. @@ -163,7 +162,7 @@ def mark(self) -> None: def parse_error( self, - exception: Type[ParseError] = ParseError, + exception: type[ParseError] = ParseError, *args: Any, **kwargs: Any, ) -> ParseError: @@ -174,7 +173,7 @@ def parse_error( return exception(line, col, *args, **kwargs) - def _to_linecol(self) -> Tuple[int, int]: + def _to_linecol(self) -> tuple[int, int]: cur = 0 for i, line in enumerate(self.splitlines()): if cur + len(line) + 1 > self.idx: diff --git a/tomlkit/toml_char.py b/tomlkit/toml_char.py index 487657b5..d85b62cc 100644 --- a/tomlkit/toml_char.py +++ b/tomlkit/toml_char.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import string from functools import lru_cache @@ -5,6 +7,12 @@ class TOMLChar(str): def __init__(self, c): + self.is_bare_key_char = lru_cache(maxsize=None)(self._is_bare_key_char) + self.is_kv_sep = lru_cache(maxsize=None)(self._is_kv_sep) + self.is_int_float_char = lru_cache(maxsize=None)(self._is_int_float_char) + self.is_ws = lru_cache(maxsize=None)(self._is_ws) + self.is_nl = lru_cache(maxsize=None)(self._is_nl) + self.is_spaces = lru_cache(maxsize=None)(self._is_spaces) super().__init__() if len(self) > 1: @@ -17,43 +25,37 @@ def __init__(self, c): NL = "\n\r" WS = SPACES + NL - @lru_cache(maxsize=None) - def is_bare_key_char(self) -> bool: + def _is_bare_key_char(self) -> bool: """ Whether the character is a valid bare key name or not. """ return self in self.BARE - @lru_cache(maxsize=None) - def is_kv_sep(self) -> bool: + def _is_kv_sep(self) -> bool: """ Whether the character is a valid key/value separator or not. """ return self in self.KV - @lru_cache(maxsize=None) - def is_int_float_char(self) -> bool: + def _is_int_float_char(self) -> bool: """ Whether the character if a valid integer or float value character or not. """ return self in self.NUMBER - @lru_cache(maxsize=None) - def is_ws(self) -> bool: + def _is_ws(self) -> bool: """ Whether the character is a whitespace character or not. """ return self in self.WS - @lru_cache(maxsize=None) - def is_nl(self) -> bool: + def _is_nl(self) -> bool: """ Whether the character is a new line character or not. """ return self in self.NL - @lru_cache(maxsize=None) - def is_spaces(self) -> bool: + def _is_spaces(self) -> bool: """ Whether the character is a space or not """ diff --git a/tomlkit/toml_document.py b/tomlkit/toml_document.py index b485e302..af7fa377 100644 --- a/tomlkit/toml_document.py +++ b/tomlkit/toml_document.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from .container import Container diff --git a/tomlkit/toml_file.py b/tomlkit/toml_file.py index 5b28cb03..33a686f4 100644 --- a/tomlkit/toml_file.py +++ b/tomlkit/toml_file.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import re From c49da4ca04a58d9d165d4a7a7a397f8330171547 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Mon, 18 Jul 2022 23:25:23 +0200 Subject: [PATCH 03/15] Flake8-broken-line and flake8-comprehensions --- .pre-commit-config.yaml | 4 ++-- tomlkit/items.py | 4 ++-- tomlkit/parser.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 02c5bfd7..8fdcc9ef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,9 +26,9 @@ repos: - id: yesqa additional_dependencies: &flake8_deps # - flake8-annotations==2.9.0 -# - flake8-broken-line==0.4.0 + - flake8-broken-line==0.4.0 - flake8-bugbear==22.4.25 -# - flake8-comprehensions==3.10.0 + - flake8-comprehensions==3.10.0 # - flake8-eradicate==1.2.1 # - flake8-quotes==3.3.1 # - flake8-simplify==0.19.2 diff --git a/tomlkit/items.py b/tomlkit/items.py index 91296507..b482f651 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -1847,5 +1847,5 @@ def value(self) -> None: def as_string(self) -> str: return "" - def _getstate(self, protocol=3): - return tuple() + def _getstate(self, protocol=3) -> tuple: + return () diff --git a/tomlkit/parser.py b/tomlkit/parser.py index a40bd087..42a5274b 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -918,7 +918,7 @@ def _parse_table( if parent_name: parent_name_parts = tuple(parent_name) else: - parent_name_parts = tuple() + parent_name_parts = () if len(name_parts) > len(parent_name_parts) + 1: missing_table = True From c4ec49d0694f8b5c550833b5c06bb3ed0e055911 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Tue, 19 Jul 2022 00:09:48 +0200 Subject: [PATCH 04/15] Flake8-eradicate, flake8-simplify, pep8-naming --- .flake8 | 25 ++++++++++++++++++++++++- .pre-commit-config.yaml | 6 +++--- tests/test_items.py | 30 +++++++++++++++--------------- tests/test_toml_document.py | 20 ++++++++++---------- tests/test_toml_spec_tests.py | 5 ++--- tests/test_toml_tests.py | 7 ++++--- tests/util.py | 12 ++++++------ tomlkit/_compat.py | 5 ++--- tomlkit/items.py | 4 ++-- tomlkit/parser.py | 14 ++++++-------- tomlkit/source.py | 6 +----- 11 files changed, 75 insertions(+), 59 deletions(-) diff --git a/.flake8 b/.flake8 index dc51aa93..e077f26c 100644 --- a/.flake8 +++ b/.flake8 @@ -1,7 +1,14 @@ [flake8] +min_python_version = 3.6.0 max-line-length = 88 ignore = E501, E203, W503 -per-file-ignores = __init__.py:F401 +per-file-ignores = + __init__.py:F401, + # N818: error suffix in exception names (API-breaking change) + tomlkit/exceptions.py: N818, + # ANN201: Missing return type annotation for public function + tests/test_*:ANN201 + tests/**/test_*:ANN201 exclude = .git __pycache__ @@ -15,3 +22,19 @@ exclude = .pytest_cache .vscode .github +ban-relative-imports = true +# flake8-use-fstring: https://github.com/MichaelKim0407/flake8-use-fstring#--percent-greedy-and---format-greedy +format-greedy = 1 +inline-quotes = double +enable-extensions = TC, TC1 +type-checking-exempt-modules = typing, typing-extensions +eradicate-whitelist-extend = ^-.*; +extend-ignore = + # E203: Whitespace before ':' (pycqa/pycodestyle#373) + E203, + # SIM106: Handle error-cases first + SIM106, + # ANN101: Missing type annotation for self in method + ANN101, + # ANN102: Missing type annotation for cls in classmethod + ANN102, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8fdcc9ef..a3c3af04 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,14 +29,14 @@ repos: - flake8-broken-line==0.4.0 - flake8-bugbear==22.4.25 - flake8-comprehensions==3.10.0 -# - flake8-eradicate==1.2.1 + - flake8-eradicate==1.2.1 # - flake8-quotes==3.3.1 -# - flake8-simplify==0.19.2 + - flake8-simplify==0.19.2 # - flake8-tidy-imports==4.8.0 # - flake8-type-checking==1.5.0 # - flake8-typing-imports==1.12.0 # - flake8-use-fstring==1.3 -# - pep8-naming==0.12.1 + - pep8-naming==0.13.0 - repo: https://github.com/asottile/pyupgrade rev: v2.37.1 diff --git a/tests/test_items.py b/tests/test_items.py index c0b7cb2b..828ea535 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -184,26 +184,26 @@ def test_items_can_be_appended_to_and_removed_from_a_table(): _, table = parser._parse_table() assert isinstance(table, Table) - assert "" == table.as_string() + assert table.as_string() == "" table.append(Key("foo"), String(StringType.SLB, "bar", "bar", Trivia(trail="\n"))) - assert 'foo = "bar"\n' == table.as_string() + assert table.as_string() == 'foo = "bar"\n' table.append( Key("baz"), Integer(34, Trivia(comment_ws=" ", comment="# Integer", trail=""), "34"), ) - assert 'foo = "bar"\nbaz = 34 # Integer' == table.as_string() + assert table.as_string() == 'foo = "bar"\nbaz = 34 # Integer' table.remove(Key("baz")) - assert 'foo = "bar"\n' == table.as_string() + assert table.as_string() == 'foo = "bar"\n' table.remove(Key("foo")) - assert "" == table.as_string() + assert table.as_string() == "" with pytest.raises(NonExistentKey): table.remove(Key("foo")) @@ -217,23 +217,23 @@ def test_items_can_be_appended_to_and_removed_from_an_inline_table(): _, table = parser._parse_item() assert isinstance(table, InlineTable) - assert "{}" == table.as_string() + assert table.as_string() == "{}" table.append(Key("foo"), String(StringType.SLB, "bar", "bar", Trivia(trail=""))) - assert '{foo = "bar"}' == table.as_string() + assert table.as_string() == '{foo = "bar"}' table.append(Key("baz"), Integer(34, Trivia(trail=""), "34")) - assert '{foo = "bar", baz = 34}' == table.as_string() + assert table.as_string() == '{foo = "bar", baz = 34}' table.remove(Key("baz")) - assert '{foo = "bar"}' == table.as_string() + assert table.as_string() == '{foo = "bar"}' table.remove(Key("foo")) - assert "{}" == table.as_string() + assert table.as_string() == "{}" with pytest.raises(NonExistentKey): table.remove(Key("foo")) @@ -341,7 +341,7 @@ def test_array_multiline(): t = item([]) t.multiline(True) - assert "[]" == t.as_string() + assert t.as_string() == "[]" def test_array_multiline_modify(): @@ -793,7 +793,7 @@ def test_trim_comments_when_building_inline_table(): value.comment("Another comment") table.append("baz", value) assert "# Another comment" not in table.as_string() - assert '{foo = "bar", baz = "foobaz"}' == table.as_string() + assert table.as_string() == '{foo = "bar", baz = "foobaz"}' def test_deleting_inline_table_elemeent_does_not_leave_trailing_separator(): @@ -801,11 +801,11 @@ def test_deleting_inline_table_elemeent_does_not_leave_trailing_separator(): table["foo"] = "bar" table["baz"] = "boom" - assert '{foo = "bar", baz = "boom"}' == table.as_string() + assert table.as_string() == '{foo = "bar", baz = "boom"}' del table["baz"] - assert '{foo = "bar"}' == table.as_string() + assert table.as_string() == '{foo = "bar"}' table = api.inline_table() table["foo"] = "bar" @@ -814,7 +814,7 @@ def test_deleting_inline_table_elemeent_does_not_leave_trailing_separator(): table["baz"] = "boom" - assert '{baz = "boom"}' == table.as_string() + assert table.as_string() == '{baz = "boom"}' def test_booleans_comparison(): diff --git a/tests/test_toml_document.py b/tests/test_toml_document.py index f238d722..78228de2 100644 --- a/tests/test_toml_document.py +++ b/tests/test_toml_document.py @@ -463,7 +463,8 @@ def test_getting_inline_table_is_still_an_inline_table(): dev_dependencies["baz"]["source"] = "other" assert ( - """\ + doc.as_string() + == """\ [tool.poetry] name = "foo" @@ -474,7 +475,6 @@ def test_getting_inline_table_is_still_an_inline_table(): [tool.poetry.dev-dependencies] baz = {version = "^4.0", source = "other"} """ - == doc.as_string() ) @@ -510,7 +510,7 @@ def test_values_can_still_be_set_for_out_of_order_tables(): doc = parse(content) doc["a"]["a"]["key"] = "new_value" - assert "new_value" == doc["a"]["a"]["key"] + assert doc["a"]["a"]["key"] == "new_value" expected = """ [a.a] @@ -590,18 +590,18 @@ def test_out_of_order_tables_are_still_dicts(): table = doc["a"]["a"] assert "key" in table assert "c" in table - assert "value" == table.get("key") + assert table.get("key") == "value" assert {} == table.get("c") assert table.get("d") is None - assert "foo" == table.get("d", "foo") + assert table.get("d", "foo") == "foo" - assert "bar" == table.setdefault("d", "bar") - assert "bar" == table["d"] + assert table.setdefault("d", "bar") == "bar" + assert table["d"] == "bar" - assert "value" == table.pop("key") + assert table.pop("key") == "value" assert "key" not in table - assert "baz" == table.pop("missing", default="baz") + assert table.pop("missing", default="baz") == "baz" with pytest.raises(KeyError): table.pop("missing") @@ -631,7 +631,7 @@ def test_string_output_order_is_preserved_for_out_of_order_tables(): constraint["version"] = "^1.0" doc["tool"]["poetry"]["dependencies"]["bar"] = constraint - assert "^1.0" == doc["tool"]["poetry"]["dependencies"]["bar"]["version"] + assert doc["tool"]["poetry"]["dependencies"]["bar"]["version"] == "^1.0" expected = """ [tool.poetry] diff --git a/tests/test_toml_spec_tests.py b/tests/test_toml_spec_tests.py index d56a18ce..db7ae9c5 100644 --- a/tests/test_toml_spec_tests.py +++ b/tests/test_toml_spec_tests.py @@ -109,6 +109,5 @@ def test_valid_decode(test): @pytest.mark.parametrize("test", ERROR_TESTS) def test_invalid_decode(test): toml_file = os.path.join(SPEC_TEST_DIR, "errors", test + ".toml") - with pytest.raises(TOMLKitError): - with open(toml_file, encoding="utf-8") as f: - parse(f.read()) + with pytest.raises(TOMLKitError), open(toml_file, encoding="utf-8") as f: + parse(f.read()) diff --git a/tests/test_toml_tests.py b/tests/test_toml_tests.py index 0ac12461..36c596c4 100644 --- a/tests/test_toml_tests.py +++ b/tests/test_toml_tests.py @@ -59,6 +59,7 @@ def test_invalid_decode(invalid_decode_case): def test_invalid_encode(invalid_encode_case): - with pytest.raises((TOMLKitError, UnicodeDecodeError)): - with open(invalid_encode_case, encoding="utf-8") as f: - load(f) + with pytest.raises((TOMLKitError, UnicodeDecodeError)), open( + invalid_encode_case, encoding="utf-8" + ) as f: + load(f) diff --git a/tests/util.py b/tests/util.py index 91b1d4ba..ef8eb60c 100644 --- a/tests/util.py +++ b/tests/util.py @@ -45,15 +45,15 @@ def assert_not_tomlkit_type(v): - for _, T in enumerate(TOMLKIT_TYPES): - assert not isinstance(v, T) + for _, tomlkit_type in enumerate(TOMLKIT_TYPES): + assert not isinstance(v, tomlkit_type) -def assert_is_ppo(v_unwrapped, unwrappedType): +def assert_is_ppo(v_unwrapped, unwrapped_type): assert_not_tomlkit_type(v_unwrapped) - assert isinstance(v_unwrapped, unwrappedType) + assert isinstance(v_unwrapped, unwrapped_type) -def elementary_test(v, unwrappedType): +def elementary_test(v, unwrapped_type): v_unwrapped = v.unwrap() - assert_is_ppo(v_unwrapped, unwrappedType) + assert_is_ppo(v_unwrapped, unwrapped_type) diff --git a/tomlkit/_compat.py b/tomlkit/_compat.py index e7811134..8e76b7fd 100644 --- a/tomlkit/_compat.py +++ b/tomlkit/_compat.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import sys from typing import Any @@ -15,9 +16,7 @@ def decode(string: Any, encodings: list[str] | None = None): encodings = encodings or ["utf-8", "latin1", "ascii"] for encoding in encodings: - try: + with contextlib.suppress(UnicodeEncodeError, UnicodeDecodeError): return string.decode(encoding) - except (UnicodeEncodeError, UnicodeDecodeError): - pass return string.decode(encodings[0], errors="ignore") diff --git a/tomlkit/items.py b/tomlkit/items.py index b482f651..ef569712 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -41,8 +41,8 @@ # Importing from builtins is preferred over simple assignment, see issues: # https://github.com/python/mypy/issues/8715 # https://github.com/python/mypy/issues/10068 - from builtins import dict as _CustomDict - from builtins import list as _CustomList + from builtins import dict as _CustomDict # noqa: N812 + from builtins import list as _CustomList # noqa: N812 # Allow type annotations but break circular imports from . import container diff --git a/tomlkit/parser.py b/tomlkit/parser.py index 42a5274b..4025ebf8 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -804,9 +804,7 @@ def _parse_string(self, delim: StringType) -> String: delim.is_singleline() and not escaped and (code == CHR_DEL or code <= CTRL_CHAR_LIMIT and code != CTRL_I) - ): - raise self.parse_error(InvalidControlChar, code, "strings") - elif ( + ) or ( delim.is_multiline() and not escaped and ( @@ -962,10 +960,9 @@ def _parse_table( key = name_parts[0] for i, _name in enumerate(name_parts[1:]): - if _name in table: - child = table[_name] - else: - child = Table( + child = table.get( + _name, + Table( Container(True), Trivia(indent, cws, comment, trail), is_aot and i == len(name_parts) - 2, @@ -974,7 +971,8 @@ def _parse_table( display_name=full_key.as_string() if i == len(name_parts) - 2 else None, - ) + ), + ) if is_aot and i == len(name_parts) - 2: table.raw_append(_name, AoT([child], name=table.name, parsed=True)) diff --git a/tomlkit/source.py b/tomlkit/source.py index 89a606cd..73ef2a69 100644 --- a/tomlkit/source.py +++ b/tomlkit/source.py @@ -128,11 +128,7 @@ def inc_n(self, n: int, exception: type[ParseError] | None = None) -> bool: Increments the parser by n characters if the end of the input has not been reached. """ - for _ in range(n): - if not self.inc(exception=exception): - return False - - return True + return all(self.inc(exception=exception) for _ in range(n)) def consume(self, chars, min=0, max=-1): """ From bf69ad1543a347b01507fc9c34fd4dedbc962590 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Tue, 19 Jul 2022 00:33:11 +0200 Subject: [PATCH 05/15] flake8-use-fstring --- .flake8 | 4 +++ .pre-commit-config.yaml | 2 +- tests/test_toml_spec_tests.py | 2 +- tests/test_toml_tests.py | 2 +- tomlkit/container.py | 53 ++++++++++++++++++----------------- tomlkit/exceptions.py | 5 ++-- tomlkit/items.py | 21 +++++++------- 7 files changed, 49 insertions(+), 40 deletions(-) diff --git a/.flake8 b/.flake8 index e077f26c..47b7161c 100644 --- a/.flake8 +++ b/.flake8 @@ -6,6 +6,10 @@ per-file-ignores = __init__.py:F401, # N818: error suffix in exception names (API-breaking change) tomlkit/exceptions.py: N818, + # FS003: f-string missing prefix + tests/test_items.py: FS003, + tests/test_api.py: FS003, + tests/test_toml_document.py: FS003, # ANN201: Missing return type annotation for public function tests/test_*:ANN201 tests/**/test_*:ANN201 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a3c3af04..68c077f4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: # - flake8-tidy-imports==4.8.0 # - flake8-type-checking==1.5.0 # - flake8-typing-imports==1.12.0 -# - flake8-use-fstring==1.3 + - flake8-use-fstring==1.3 - pep8-naming==0.13.0 - repo: https://github.com/asottile/pyupgrade diff --git a/tests/test_toml_spec_tests.py b/tests/test_toml_spec_tests.py index db7ae9c5..99fcfc5a 100644 --- a/tests/test_toml_spec_tests.py +++ b/tests/test_toml_spec_tests.py @@ -80,7 +80,7 @@ def untag(value): elif value["type"] == "array": return [untag(i) for i in value["value"]] else: - raise Exception("Unsupported type {}".format(value["type"])) + raise Exception(f'Unsupported type {value["type"]}') else: return {k: untag(v) for k, v in value.items()} diff --git a/tests/test_toml_tests.py b/tests/test_toml_tests.py index 36c596c4..2a2db192 100644 --- a/tests/test_toml_tests.py +++ b/tests/test_toml_tests.py @@ -40,7 +40,7 @@ def untag(value): elif value["type"] == "array": return [untag(i) for i in value["value"]] else: - raise Exception("Unsupported type {}".format(value["type"])) + raise Exception(f'Unsupported type {value["type"]}') else: return {k: untag(v) for k, v in value.items()} diff --git a/tomlkit/container.py b/tomlkit/container.py index 13bc02e3..65ba47e2 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -526,15 +526,18 @@ def _render_table(self, key: Key, table: Table, prefix: str | None = None) -> st if table.is_aot_element(): open_, close = "[[", "]]" - cur += "{}{}{}{}{}{}{}{}".format( - table.trivia.indent, - open_, - decode(_key), - close, - table.trivia.comment_ws, - decode(table.trivia.comment), - table.trivia.trail, - "\n" if "\n" not in table.trivia.trail and len(table.value) > 0 else "", + newline_in_table_trivia = ( + "\n" if "\n" not in table.trivia.trail and len(table.value) > 0 else "" + ) + cur += ( + f"{table.trivia.indent}" + f"{open_}" + f"{decode(_key)}" + f"{close}" + f"{table.trivia.comment_ws}" + f"{decode(table.trivia.comment)}" + f"{table.trivia.trail}" + f"{newline_in_table_trivia}" ) elif table.trivia.indent == "\n": cur += table.trivia.indent @@ -578,14 +581,14 @@ def _render_aot_table(self, table: Table, prefix: str | None = None) -> str: if not table.is_super_table(): open_, close = "[[", "]]" - cur += "{}{}{}{}{}{}{}".format( - table.trivia.indent, - open_, - decode(_key), - close, - table.trivia.comment_ws, - decode(table.trivia.comment), - table.trivia.trail, + cur += ( + f"{table.trivia.indent}" + f"{open_}" + f"{decode(_key)}" + f"{close}" + f"{table.trivia.comment_ws}" + f"{decode(table.trivia.comment)}" + f"{table.trivia.trail}" ) for k, v in table.value.body: @@ -613,14 +616,14 @@ def _render_simple_item(self, key, item, prefix=None): if prefix is not None: _key = prefix + "." + _key - return "{}{}{}{}{}{}{}".format( - item.trivia.indent, - decode(_key), - key.sep, - decode(item.as_string()), - item.trivia.comment_ws, - decode(item.trivia.comment), - item.trivia.trail, + return ( + f"{item.trivia.indent}" + f"{decode(_key)}" + f"{key.sep}" + f"{decode(item.as_string())}" + f"{item.trivia.comment_ws}" + f"{decode(item.trivia.comment)}" + f"{item.trivia.trail}" ) def __len__(self) -> int: diff --git a/tomlkit/exceptions.py b/tomlkit/exceptions.py index 88977787..7e1b7818 100644 --- a/tomlkit/exceptions.py +++ b/tomlkit/exceptions.py @@ -211,8 +211,9 @@ def __init__(self, line: int, col: int, char: int, type: str) -> None: display_code += hex(char)[2:] message = ( - "Control characters (codes less than 0x1f and 0x7f) are not allowed in {}, " - "use {} instead".format(type, display_code) + "Control characters (codes less than 0x1f and 0x7f)" + f" are not allowed in {type}, " + f"use {display_code} instead" ) super().__init__(line, col, message=message) diff --git a/tomlkit/items.py b/tomlkit/items.py index ef569712..88402eee 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -588,8 +588,8 @@ def discriminant(self) -> int: return 1 def as_string(self) -> str: - return "{}{}{}".format( - self._trivia.indent, decode(self._trivia.comment), self._trivia.trail + return ( + f"{self._trivia.indent}{decode(self._trivia.comment)}{self._trivia.trail}" ) def __str__(self) -> str: @@ -1124,7 +1124,7 @@ def multiline(self, multiline: bool) -> Array: def as_string(self) -> str: if not self._multiline or not self._value: - return "[{}]".format("".join(v.as_string() for v in self._value)) + return f'[{"".join(v.as_string() for v in self._value)}]' s = "[\n" s += "".join( @@ -1628,13 +1628,14 @@ def as_string(self) -> str: continue - buf += "{}{}{}{}{}{}".format( - v.trivia.indent, - k.as_string() + ("." if k.is_dotted() else ""), - k.sep, - v.as_string(), - v.trivia.comment, - v.trivia.trail.replace("\n", ""), + v_trivia_trail = v.trivia.trail.replace("\n", "") + buf += ( + f"{v.trivia.indent}" + f'{k.as_string() + ("." if k.is_dotted() else "")}' + f"{k.sep}" + f"{v.as_string()}" + f"{v.trivia.comment}" + f"{v_trivia_trail}" ) if i != len(self._value.body) - 1: From 178fefe9bcaeb0d70386f1a5297120865990abab Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Tue, 19 Jul 2022 00:39:52 +0200 Subject: [PATCH 06/15] flake8-tidy-imports, flake8-quotes --- .pre-commit-config.yaml | 4 +- tests/test_items.py | 5 +-- tests/test_toml_document.py | 3 +- tomlkit/__init__.py | 50 +++++++++++----------- tomlkit/_utils.py | 2 +- tomlkit/api.py | 50 +++++++++++----------- tomlkit/container.py | 32 +++++++------- tomlkit/items.py | 16 +++---- tomlkit/parser.py | 84 ++++++++++++++++++------------------- tomlkit/source.py | 6 +-- tomlkit/toml_document.py | 2 +- tomlkit/toml_file.py | 4 +- 12 files changed, 128 insertions(+), 130 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 68c077f4..453a9745 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,9 +30,9 @@ repos: - flake8-bugbear==22.4.25 - flake8-comprehensions==3.10.0 - flake8-eradicate==1.2.1 -# - flake8-quotes==3.3.1 + - flake8-quotes==3.3.1 - flake8-simplify==0.19.2 -# - flake8-tidy-imports==4.8.0 + - flake8-tidy-imports==4.8.0 # - flake8-type-checking==1.5.0 # - flake8-typing-imports==1.12.0 - flake8-use-fstring==1.3 diff --git a/tests/test_items.py b/tests/test_items.py index 828ea535..4294f20b 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -10,6 +10,8 @@ import pytest +from tests.util import assert_is_ppo +from tests.util import elementary_test from tomlkit import api from tomlkit import parse from tomlkit.exceptions import NonExistentKey @@ -29,9 +31,6 @@ from tomlkit.items import item from tomlkit.parser import Parser -from .util import assert_is_ppo -from .util import elementary_test - @pytest.fixture() def tz_pst(): diff --git a/tests/test_toml_document.py b/tests/test_toml_document.py index 78228de2..ea9b8518 100644 --- a/tests/test_toml_document.py +++ b/tests/test_toml_document.py @@ -11,14 +11,13 @@ import tomlkit +from tests.util import assert_is_ppo from tomlkit import parse from tomlkit import ws from tomlkit._utils import _utc from tomlkit.api import document from tomlkit.exceptions import NonExistentKey -from .util import assert_is_ppo - def test_document_is_a_dict(example): content = example("example") diff --git a/tomlkit/__init__.py b/tomlkit/__init__.py index 1abb1eb2..a0eff6a2 100644 --- a/tomlkit/__init__.py +++ b/tomlkit/__init__.py @@ -1,28 +1,28 @@ -from .api import TOMLDocument -from .api import aot -from .api import array -from .api import boolean -from .api import comment -from .api import date -from .api import datetime -from .api import document -from .api import dump -from .api import dumps -from .api import float_ -from .api import inline_table -from .api import integer -from .api import item -from .api import key -from .api import key_value -from .api import load -from .api import loads -from .api import nl -from .api import parse -from .api import string -from .api import table -from .api import time -from .api import value -from .api import ws +from tomlkit.api import TOMLDocument +from tomlkit.api import aot +from tomlkit.api import array +from tomlkit.api import boolean +from tomlkit.api import comment +from tomlkit.api import date +from tomlkit.api import datetime +from tomlkit.api import document +from tomlkit.api import dump +from tomlkit.api import dumps +from tomlkit.api import float_ +from tomlkit.api import inline_table +from tomlkit.api import integer +from tomlkit.api import item +from tomlkit.api import key +from tomlkit.api import key_value +from tomlkit.api import load +from tomlkit.api import loads +from tomlkit.api import nl +from tomlkit.api import parse +from tomlkit.api import string +from tomlkit.api import table +from tomlkit.api import time +from tomlkit.api import value +from tomlkit.api import ws __version__ = "0.11.1" diff --git a/tomlkit/_utils.py b/tomlkit/_utils.py index 99bf8d8a..2ca41158 100644 --- a/tomlkit/_utils.py +++ b/tomlkit/_utils.py @@ -10,7 +10,7 @@ from datetime import timezone from typing import Collection -from ._compat import decode +from tomlkit._compat import decode RFC_3339_LOOSE = re.compile( diff --git a/tomlkit/api.py b/tomlkit/api.py index 214a20eb..0584dffb 100644 --- a/tomlkit/api.py +++ b/tomlkit/api.py @@ -6,31 +6,31 @@ from typing import IO from typing import Iterable -from ._utils import parse_rfc3339 -from .container import Container -from .exceptions import UnexpectedCharError -from .items import AoT -from .items import Array -from .items import Bool -from .items import Comment -from .items import Date -from .items import DateTime -from .items import DottedKey -from .items import Float -from .items import InlineTable -from .items import Integer -from .items import Item as _Item -from .items import Key -from .items import SingleKey -from .items import String -from .items import StringType as _StringType -from .items import Table -from .items import Time -from .items import Trivia -from .items import Whitespace -from .items import item -from .parser import Parser -from .toml_document import TOMLDocument +from tomlkit._utils import parse_rfc3339 +from tomlkit.container import Container +from tomlkit.exceptions import UnexpectedCharError +from tomlkit.items import AoT +from tomlkit.items import Array +from tomlkit.items import Bool +from tomlkit.items import Comment +from tomlkit.items import Date +from tomlkit.items import DateTime +from tomlkit.items import DottedKey +from tomlkit.items import Float +from tomlkit.items import InlineTable +from tomlkit.items import Integer +from tomlkit.items import Item as _Item +from tomlkit.items import Key +from tomlkit.items import SingleKey +from tomlkit.items import String +from tomlkit.items import StringType as _StringType +from tomlkit.items import Table +from tomlkit.items import Time +from tomlkit.items import Trivia +from tomlkit.items import Whitespace +from tomlkit.items import item +from tomlkit.parser import Parser +from tomlkit.toml_document import TOMLDocument def loads(string: str | bytes) -> TOMLDocument: diff --git a/tomlkit/container.py b/tomlkit/container.py index 65ba47e2..132e41f8 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -5,22 +5,22 @@ from typing import Any from typing import Iterator -from ._compat import decode -from ._utils import merge_dicts -from .exceptions import KeyAlreadyPresent -from .exceptions import NonExistentKey -from .exceptions import TOMLKitError -from .items import AoT -from .items import Comment -from .items import Item -from .items import Key -from .items import Null -from .items import SingleKey -from .items import Table -from .items import Trivia -from .items import Whitespace -from .items import _CustomDict -from .items import item as _item +from tomlkit._compat import decode +from tomlkit._utils import merge_dicts +from tomlkit.exceptions import KeyAlreadyPresent +from tomlkit.exceptions import NonExistentKey +from tomlkit.exceptions import TOMLKitError +from tomlkit.items import AoT +from tomlkit.items import Comment +from tomlkit.items import Item +from tomlkit.items import Key +from tomlkit.items import Null +from tomlkit.items import SingleKey +from tomlkit.items import Table +from tomlkit.items import Trivia +from tomlkit.items import Whitespace +from tomlkit.items import _CustomDict +from tomlkit.items import item as _item _NOT_SET = object() diff --git a/tomlkit/items.py b/tomlkit/items.py index 88402eee..8455ed7a 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -21,12 +21,12 @@ from typing import cast from typing import overload -from ._compat import PY38 -from ._compat import decode -from ._utils import CONTROL_CHARS -from ._utils import escape_string -from .exceptions import InvalidStringError -from .toml_char import TOMLChar +from tomlkit._compat import PY38 +from tomlkit._compat import decode +from tomlkit._utils import CONTROL_CHARS +from tomlkit._utils import escape_string +from tomlkit.exceptions import InvalidStringError +from tomlkit.toml_char import TOMLChar if TYPE_CHECKING: # pragma: no cover @@ -45,7 +45,7 @@ from builtins import list as _CustomList # noqa: N812 # Allow type annotations but break circular imports - from . import container + from tomlkit import container else: from collections.abc import MutableMapping from collections.abc import MutableSequence @@ -134,7 +134,7 @@ def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> I b = 2 """ - from .container import Container + from tomlkit.container import Container if isinstance(value, Item): return value diff --git a/tomlkit/parser.py b/tomlkit/parser.py index 4025ebf8..765be581 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -3,48 +3,48 @@ import re import string -from ._compat import decode -from ._utils import RFC_3339_LOOSE -from ._utils import _escaped -from ._utils import parse_rfc3339 -from .container import Container -from .exceptions import EmptyKeyError -from .exceptions import EmptyTableNameError -from .exceptions import InternalParserError -from .exceptions import InvalidCharInStringError -from .exceptions import InvalidControlChar -from .exceptions import InvalidDateError -from .exceptions import InvalidDateTimeError -from .exceptions import InvalidNumberError -from .exceptions import InvalidTimeError -from .exceptions import InvalidUnicodeValueError -from .exceptions import ParseError -from .exceptions import UnexpectedCharError -from .exceptions import UnexpectedEofError -from .items import AoT -from .items import Array -from .items import Bool -from .items import BoolType -from .items import Comment -from .items import Date -from .items import DateTime -from .items import Float -from .items import InlineTable -from .items import Integer -from .items import Item -from .items import Key -from .items import KeyType -from .items import Null -from .items import SingleKey -from .items import String -from .items import StringType -from .items import Table -from .items import Time -from .items import Trivia -from .items import Whitespace -from .source import Source -from .toml_char import TOMLChar -from .toml_document import TOMLDocument +from tomlkit._compat import decode +from tomlkit._utils import RFC_3339_LOOSE +from tomlkit._utils import _escaped +from tomlkit._utils import parse_rfc3339 +from tomlkit.container import Container +from tomlkit.exceptions import EmptyKeyError +from tomlkit.exceptions import EmptyTableNameError +from tomlkit.exceptions import InternalParserError +from tomlkit.exceptions import InvalidCharInStringError +from tomlkit.exceptions import InvalidControlChar +from tomlkit.exceptions import InvalidDateError +from tomlkit.exceptions import InvalidDateTimeError +from tomlkit.exceptions import InvalidNumberError +from tomlkit.exceptions import InvalidTimeError +from tomlkit.exceptions import InvalidUnicodeValueError +from tomlkit.exceptions import ParseError +from tomlkit.exceptions import UnexpectedCharError +from tomlkit.exceptions import UnexpectedEofError +from tomlkit.items import AoT +from tomlkit.items import Array +from tomlkit.items import Bool +from tomlkit.items import BoolType +from tomlkit.items import Comment +from tomlkit.items import Date +from tomlkit.items import DateTime +from tomlkit.items import Float +from tomlkit.items import InlineTable +from tomlkit.items import Integer +from tomlkit.items import Item +from tomlkit.items import Key +from tomlkit.items import KeyType +from tomlkit.items import Null +from tomlkit.items import SingleKey +from tomlkit.items import String +from tomlkit.items import StringType +from tomlkit.items import Table +from tomlkit.items import Time +from tomlkit.items import Trivia +from tomlkit.items import Whitespace +from tomlkit.source import Source +from tomlkit.toml_char import TOMLChar +from tomlkit.toml_document import TOMLDocument CTRL_I = 0x09 # Tab diff --git a/tomlkit/source.py b/tomlkit/source.py index 73ef2a69..ab29854a 100644 --- a/tomlkit/source.py +++ b/tomlkit/source.py @@ -3,9 +3,9 @@ from copy import copy from typing import Any -from .exceptions import ParseError -from .exceptions import UnexpectedCharError -from .toml_char import TOMLChar +from tomlkit.exceptions import ParseError +from tomlkit.exceptions import UnexpectedCharError +from tomlkit.toml_char import TOMLChar class _State: diff --git a/tomlkit/toml_document.py b/tomlkit/toml_document.py index af7fa377..7e3b4024 100644 --- a/tomlkit/toml_document.py +++ b/tomlkit/toml_document.py @@ -1,6 +1,6 @@ from __future__ import annotations -from .container import Container +from tomlkit.container import Container class TOMLDocument(Container): diff --git a/tomlkit/toml_file.py b/tomlkit/toml_file.py index 33a686f4..92a0816b 100644 --- a/tomlkit/toml_file.py +++ b/tomlkit/toml_file.py @@ -3,8 +3,8 @@ import os import re -from .api import loads -from .toml_document import TOMLDocument +from tomlkit.api import loads +from tomlkit.toml_document import TOMLDocument class TOMLFile: From 5aea0d6b4e12d426f719d25e7da08548f846c00b Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Tue, 19 Jul 2022 00:52:15 +0200 Subject: [PATCH 07/15] flake8-typing-imports, flake8-type-checking --- .flake8 | 4 +--- .pre-commit-config.yaml | 5 +++-- tomlkit/api.py | 22 +++++++++++++--------- tomlkit/items.py | 4 ++-- tomlkit/parser.py | 9 +++++++-- tomlkit/toml_file.py | 7 ++++++- 6 files changed, 32 insertions(+), 19 deletions(-) diff --git a/.flake8 b/.flake8 index 47b7161c..07693533 100644 --- a/.flake8 +++ b/.flake8 @@ -3,7 +3,7 @@ min_python_version = 3.6.0 max-line-length = 88 ignore = E501, E203, W503 per-file-ignores = - __init__.py:F401, + __init__.py:F401, TC002 # N818: error suffix in exception names (API-breaking change) tomlkit/exceptions.py: N818, # FS003: f-string missing prefix @@ -11,8 +11,6 @@ per-file-ignores = tests/test_api.py: FS003, tests/test_toml_document.py: FS003, # ANN201: Missing return type annotation for public function - tests/test_*:ANN201 - tests/**/test_*:ANN201 exclude = .git __pycache__ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 453a9745..a2879a53 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,10 +33,11 @@ repos: - flake8-quotes==3.3.1 - flake8-simplify==0.19.2 - flake8-tidy-imports==4.8.0 -# - flake8-type-checking==1.5.0 -# - flake8-typing-imports==1.12.0 + - flake8-type-checking==1.5.0 + - flake8-typing-imports==1.12.0 - flake8-use-fstring==1.3 - pep8-naming==0.13.0 + exclude: ^tomlkit/items\.py - repo: https://github.com/asottile/pyupgrade rev: v2.37.1 diff --git a/tomlkit/api.py b/tomlkit/api.py index 0584dffb..f1b3213d 100644 --- a/tomlkit/api.py +++ b/tomlkit/api.py @@ -4,28 +4,20 @@ from collections.abc import Mapping from typing import IO +from typing import TYPE_CHECKING from typing import Iterable from tomlkit._utils import parse_rfc3339 from tomlkit.container import Container from tomlkit.exceptions import UnexpectedCharError from tomlkit.items import AoT -from tomlkit.items import Array -from tomlkit.items import Bool from tomlkit.items import Comment -from tomlkit.items import Date -from tomlkit.items import DateTime from tomlkit.items import DottedKey -from tomlkit.items import Float from tomlkit.items import InlineTable -from tomlkit.items import Integer -from tomlkit.items import Item as _Item -from tomlkit.items import Key from tomlkit.items import SingleKey from tomlkit.items import String from tomlkit.items import StringType as _StringType from tomlkit.items import Table -from tomlkit.items import Time from tomlkit.items import Trivia from tomlkit.items import Whitespace from tomlkit.items import item @@ -33,6 +25,18 @@ from tomlkit.toml_document import TOMLDocument +if TYPE_CHECKING: + from tomlkit.items import Array + from tomlkit.items import Bool + from tomlkit.items import Date + from tomlkit.items import DateTime + from tomlkit.items import Float + from tomlkit.items import Integer + from tomlkit.items import Item as _Item + from tomlkit.items import Key + from tomlkit.items import Time + + def loads(string: str | bytes) -> TOMLDocument: """ Parses a string into a TOMLDocument. diff --git a/tomlkit/items.py b/tomlkit/items.py index 8455ed7a..50a0ceae 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -41,8 +41,8 @@ # Importing from builtins is preferred over simple assignment, see issues: # https://github.com/python/mypy/issues/8715 # https://github.com/python/mypy/issues/10068 - from builtins import dict as _CustomDict # noqa: N812 - from builtins import list as _CustomList # noqa: N812 + from builtins import dict as _CustomDict # noqa: N812, TC004 + from builtins import list as _CustomList # noqa: N812, TC004 # Allow type annotations but break circular imports from tomlkit import container diff --git a/tomlkit/parser.py b/tomlkit/parser.py index 765be581..fd0e66b2 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -3,6 +3,8 @@ import re import string +from typing import TYPE_CHECKING + from tomlkit._compat import decode from tomlkit._utils import RFC_3339_LOOSE from tomlkit._utils import _escaped @@ -31,8 +33,6 @@ from tomlkit.items import Float from tomlkit.items import InlineTable from tomlkit.items import Integer -from tomlkit.items import Item -from tomlkit.items import Key from tomlkit.items import KeyType from tomlkit.items import Null from tomlkit.items import SingleKey @@ -47,6 +47,11 @@ from tomlkit.toml_document import TOMLDocument +if TYPE_CHECKING: + from tomlkit.items import Item + from tomlkit.items import Key + + CTRL_I = 0x09 # Tab CTRL_J = 0x0A # Line feed CTRL_M = 0x0D # Carriage return diff --git a/tomlkit/toml_file.py b/tomlkit/toml_file.py index 92a0816b..b8b5b998 100644 --- a/tomlkit/toml_file.py +++ b/tomlkit/toml_file.py @@ -3,8 +3,13 @@ import os import re +from typing import TYPE_CHECKING + from tomlkit.api import loads -from tomlkit.toml_document import TOMLDocument + + +if TYPE_CHECKING: + from tomlkit.toml_document import TOMLDocument class TOMLFile: From 7b1084fe6c51fcb987ad17234c8461ca1d9a0989 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Tue, 19 Jul 2022 00:56:29 +0200 Subject: [PATCH 08/15] pycln --- .pre-commit-config.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a2879a53..807eb4a2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,6 @@ repos: hooks: - id: yesqa additional_dependencies: &flake8_deps -# - flake8-annotations==2.9.0 - flake8-broken-line==0.4.0 - flake8-bugbear==22.4.25 - flake8-comprehensions==3.10.0 @@ -45,11 +44,12 @@ repos: - id: pyupgrade args: [--py36-plus] -# - repo: https://github.com/hadialqattan/pycln -# rev: v2.0.1 -# hooks: -# - id: pycln -# args: [--all] + - repo: https://github.com/hadialqattan/pycln + rev: v2.0.1 + hooks: + - id: pycln + args: [--all] + exclude: ^.*__init__\.py$ # without this, pycln removes TOMLDocument import - repo: https://github.com/pycqa/flake8 rev: 4.0.1 From ae7d1d9d1c0cc4f8517acce83937848fc71a16b2 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Tue, 19 Jul 2022 01:00:22 +0200 Subject: [PATCH 09/15] Remove unnecessary comments --- .flake8 | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.flake8 b/.flake8 index 07693533..f41ead72 100644 --- a/.flake8 +++ b/.flake8 @@ -10,7 +10,6 @@ per-file-ignores = tests/test_items.py: FS003, tests/test_api.py: FS003, tests/test_toml_document.py: FS003, - # ANN201: Missing return type annotation for public function exclude = .git __pycache__ @@ -36,7 +35,3 @@ extend-ignore = E203, # SIM106: Handle error-cases first SIM106, - # ANN101: Missing type annotation for self in method - ANN101, - # ANN102: Missing type annotation for cls in classmethod - ANN102, From 4379a00f336dd83ac17ea6766f7e6af707830fef Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Tue, 19 Jul 2022 01:06:24 +0200 Subject: [PATCH 10/15] Update versions of plugins --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 807eb4a2..d8dff890 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,13 +26,13 @@ repos: - id: yesqa additional_dependencies: &flake8_deps - flake8-broken-line==0.4.0 - - flake8-bugbear==22.4.25 + - flake8-bugbear==22.7.1 - flake8-comprehensions==3.10.0 - flake8-eradicate==1.2.1 - flake8-quotes==3.3.1 - flake8-simplify==0.19.2 - flake8-tidy-imports==4.8.0 - - flake8-type-checking==1.5.0 + - flake8-type-checking==2.0.3 - flake8-typing-imports==1.12.0 - flake8-use-fstring==1.3 - pep8-naming==0.13.0 From 578e59fb691dd0d7672c19e29fb35760d3ae63dd Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Tue, 19 Jul 2022 09:18:37 +0200 Subject: [PATCH 11/15] Code review changes - removed `exclude` from `pycln` pre-commit config - removed adding of futture-annotations import from isort config - added `TOMLDocument` to `__all__` list in tomlkit/__init__.py --- .pre-commit-config.yaml | 2 -- tomlkit/__init__.py | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8dff890..c463c2f7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,6 @@ repos: rev: 5.10.1 hooks: - id: isort - args: [--add-import, from __future__ import annotations] exclude: docs/.*|^.*__init__\.py$ - repo: https://github.com/pre-commit/pre-commit-hooks @@ -49,7 +48,6 @@ repos: hooks: - id: pycln args: [--all] - exclude: ^.*__init__\.py$ # without this, pycln removes TOMLDocument import - repo: https://github.com/pycqa/flake8 rev: 4.0.1 diff --git a/tomlkit/__init__.py b/tomlkit/__init__.py index a0eff6a2..dc3f0afe 100644 --- a/tomlkit/__init__.py +++ b/tomlkit/__init__.py @@ -49,6 +49,7 @@ "string", "table", "time", + "TOMLDocument", "value", "ws", ] From 59854331d049b9219f847f636671e9641cbbc555 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Mon, 25 Jul 2022 14:35:17 +0200 Subject: [PATCH 12/15] Revert "First pre-commit swipe (isort, flake8-bugbear, black, pyupgrade)" This reverts commit 60abdac8b3b54dede22ef9f6c3e365c890062e91. --- .flake8 | 4 +- .pre-commit-config.yaml | 1 - tests/conftest.py | 2 - tests/test_api.py | 2 - tests/test_build.py | 2 - tests/test_items.py | 4 +- tests/test_parser.py | 2 - tests/test_toml_document.py | 2 - tests/test_toml_file.py | 2 - tests/test_toml_spec_tests.py | 2 - tests/test_toml_tests.py | 2 - tests/test_utils.py | 2 - tests/test_write.py | 2 - tests/util.py | 2 - tomlkit/_compat.py | 6 +- tomlkit/_utils.py | 5 +- tomlkit/api.py | 16 ++-- tomlkit/container.py | 75 ++++++++++-------- tomlkit/exceptions.py | 7 +- tomlkit/items.py | 145 ++++++++++++++++++---------------- tomlkit/parser.py | 32 ++++---- tomlkit/source.py | 23 +++--- tomlkit/toml_char.py | 2 - tomlkit/toml_document.py | 2 - tomlkit/toml_file.py | 2 - 25 files changed, 166 insertions(+), 180 deletions(-) diff --git a/.flake8 b/.flake8 index f41ead72..0ec0d0a7 100644 --- a/.flake8 +++ b/.flake8 @@ -3,7 +3,7 @@ min_python_version = 3.6.0 max-line-length = 88 ignore = E501, E203, W503 per-file-ignores = - __init__.py:F401, TC002 + __init__.py:F401 # N818: error suffix in exception names (API-breaking change) tomlkit/exceptions.py: N818, # FS003: f-string missing prefix @@ -27,8 +27,6 @@ ban-relative-imports = true # flake8-use-fstring: https://github.com/MichaelKim0407/flake8-use-fstring#--percent-greedy-and---format-greedy format-greedy = 1 inline-quotes = double -enable-extensions = TC, TC1 -type-checking-exempt-modules = typing, typing-extensions eradicate-whitelist-extend = ^-.*; extend-ignore = # E203: Whitespace before ':' (pycqa/pycodestyle#373) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c463c2f7..66c7de8d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,7 +31,6 @@ repos: - flake8-quotes==3.3.1 - flake8-simplify==0.19.2 - flake8-tidy-imports==4.8.0 - - flake8-type-checking==2.0.3 - flake8-typing-imports==1.12.0 - flake8-use-fstring==1.3 - pep8-naming==0.13.0 diff --git a/tests/conftest.py b/tests/conftest.py index 710ce012..73333790 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os import pytest diff --git a/tests/test_api.py b/tests/test_api.py index 0e048e04..3a27db4f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import io import json import os diff --git a/tests/test_build.py b/tests/test_build.py index 3972f32f..136abb80 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import datetime from tomlkit import aot diff --git a/tests/test_items.py b/tests/test_items.py index 4294f20b..3dcd5097 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import math import pickle @@ -121,7 +119,7 @@ def test_aot_unwrap(): d = item([{"a": "A"}, {"b": "B"}]) unwrapped = d.unwrap() assert_is_ppo(unwrapped, list) - for du, _dw in zip(unwrapped, d): + for du, _ in zip(unwrapped, d): assert_is_ppo(du, dict) for ku in du: vu = du[ku] diff --git a/tests/test_parser.py b/tests/test_parser.py index 2efa2cd2..c293f828 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import pytest from tomlkit.exceptions import EmptyTableNameError diff --git a/tests/test_toml_document.py b/tests/test_toml_document.py index ea9b8518..811c90c4 100644 --- a/tests/test_toml_document.py +++ b/tests/test_toml_document.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import copy import json import pickle diff --git a/tests/test_toml_file.py b/tests/test_toml_file.py index 5774f728..ae20656a 100644 --- a/tests/test_toml_file.py +++ b/tests/test_toml_file.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os from tomlkit.toml_document import TOMLDocument diff --git a/tests/test_toml_spec_tests.py b/tests/test_toml_spec_tests.py index 99fcfc5a..fdfb735b 100644 --- a/tests/test_toml_spec_tests.py +++ b/tests/test_toml_spec_tests.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import json import os import re diff --git a/tests/test_toml_tests.py b/tests/test_toml_tests.py index 2a2db192..61e1281d 100644 --- a/tests/test_toml_tests.py +++ b/tests/test_toml_tests.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import json import pytest diff --git a/tests/test_utils.py b/tests/test_utils.py index 287a8dda..7dde452c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from datetime import date from datetime import datetime as dt from datetime import time diff --git a/tests/test_write.py b/tests/test_write.py index 2ed2799c..58752d93 100644 --- a/tests/test_write.py +++ b/tests/test_write.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from tomlkit import dumps from tomlkit import loads diff --git a/tests/util.py b/tests/util.py index ef8eb60c..fbe6df2e 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from tomlkit.items import AoT from tomlkit.items import Array from tomlkit.items import Bool diff --git a/tomlkit/_compat.py b/tomlkit/_compat.py index 8e76b7fd..f1d3bccd 100644 --- a/tomlkit/_compat.py +++ b/tomlkit/_compat.py @@ -1,15 +1,15 @@ -from __future__ import annotations - import contextlib import sys from typing import Any +from typing import List +from typing import Optional PY38 = sys.version_info >= (3, 8) -def decode(string: Any, encodings: list[str] | None = None): +def decode(string: Any, encodings: Optional[List[str]] = None): if not isinstance(string, bytes): return string diff --git a/tomlkit/_utils.py b/tomlkit/_utils.py index 2ca41158..07ed7bad 100644 --- a/tomlkit/_utils.py +++ b/tomlkit/_utils.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import re from collections.abc import Mapping @@ -9,6 +7,7 @@ from datetime import timedelta from datetime import timezone from typing import Collection +from typing import Union from tomlkit._compat import decode @@ -42,7 +41,7 @@ _utc = timezone(timedelta(), "UTC") -def parse_rfc3339(string: str) -> datetime | date | time: +def parse_rfc3339(string: str) -> Union[datetime, date, time]: m = RFC_3339_DATETIME.match(string) if m: year = int(m.group(1)) diff --git a/tomlkit/api.py b/tomlkit/api.py index f1b3213d..6b5a2ca2 100644 --- a/tomlkit/api.py +++ b/tomlkit/api.py @@ -1,11 +1,11 @@ -from __future__ import annotations - import datetime as _datetime from collections.abc import Mapping from typing import IO from typing import TYPE_CHECKING from typing import Iterable +from typing import Tuple +from typing import Union from tomlkit._utils import parse_rfc3339 from tomlkit.container import Container @@ -37,7 +37,7 @@ from tomlkit.items import Time -def loads(string: str | bytes) -> TOMLDocument: +def loads(string: Union[str, bytes]) -> TOMLDocument: """ Parses a string into a TOMLDocument. @@ -79,7 +79,7 @@ def dump(data: Mapping, fp: IO[str], *, sort_keys: bool = False) -> None: fp.write(dumps(data, sort_keys=sort_keys)) -def parse(string: str | bytes) -> TOMLDocument: +def parse(string: Union[str, bytes]) -> TOMLDocument: """ Parses a string or bytes into a TOMLDocument. """ @@ -94,12 +94,12 @@ def document() -> TOMLDocument: # Items -def integer(raw: str | int) -> Integer: +def integer(raw: Union[str, int]) -> Integer: """Create an integer item from a number or string.""" return item(int(raw)) -def float_(raw: str | float) -> Float: +def float_(raw: Union[str, float]) -> Float: """Create an float item from a number or string.""" return item(float(raw)) @@ -227,7 +227,7 @@ def aot() -> AoT: return AoT([]) -def key(k: str | Iterable[str]) -> Key: +def key(k: Union[str, Iterable[str]]) -> Key: """Create a key from a string. When a list of string is given, it will create a dotted key. @@ -264,7 +264,7 @@ def value(raw: str) -> _Item: return v -def key_value(src: str) -> tuple[Key, _Item]: +def key_value(src: str) -> Tuple[Key, _Item]: """Parse a key-value pair from a string. :Example: diff --git a/tomlkit/container.py b/tomlkit/container.py index 132e41f8..606d7288 100644 --- a/tomlkit/container.py +++ b/tomlkit/container.py @@ -1,9 +1,12 @@ -from __future__ import annotations - import copy from typing import Any +from typing import Dict from typing import Iterator +from typing import List +from typing import Optional +from typing import Tuple +from typing import Union from tomlkit._compat import decode from tomlkit._utils import merge_dicts @@ -34,13 +37,13 @@ class Container(_CustomDict): """ def __init__(self, parsed: bool = False) -> None: - self._map: dict[Key, int] = {} - self._body: list[tuple[Key | None, Item]] = [] + self._map: Dict[Key, int] = {} + self._body: List[Tuple[Optional[Key], Item]] = [] self._parsed = parsed self._table_keys = [] @property - def body(self) -> list[tuple[Key | None, Item]]: + def body(self) -> List[Tuple[Optional[Key], Item]]: return self._body def unwrap(self) -> str: @@ -63,7 +66,7 @@ def unwrap(self) -> str: return unwrapped @property - def value(self) -> dict[Any, Any]: + def value(self) -> Dict[Any, Any]: d = {} for k, v in self._body: if k is None: @@ -92,7 +95,9 @@ def parsing(self, parsing: bool) -> None: for t in v.body: t.value.parsing(parsing) - def add(self, key: Key | Item | str, item: Item | None = None) -> Container: + def add( + self, key: Union[Key, Item, str], item: Optional[Item] = None + ) -> "Container": """ Adds an item to the current Container. @@ -171,7 +176,7 @@ def _handle_dotted_key(self, key: Key, value: Item) -> None: table = table[_name] - def append(self, key: Key | str | None, item: Item) -> Container: + def append(self, key: Union[Key, str, None], item: Item) -> "Container": """Similar to :meth:`add` but both key and value must be given.""" if not isinstance(key, Key) and key is not None: key = SingleKey(key) @@ -360,7 +365,7 @@ def _remove_at(self, idx: int) -> None: dict.__delitem__(self, key.key) self._map.pop(key) - def remove(self, key: Key | str) -> Container: + def remove(self, key: Union[Key, str]) -> "Container": """Remove a key from the container.""" if not isinstance(key, Key): key = SingleKey(key) @@ -380,8 +385,8 @@ def remove(self, key: Key | str) -> Container: return self def _insert_after( - self, key: Key | str, other_key: Key | str, item: Any - ) -> Container: + self, key: Union[Key, str], other_key: Union[Key, str], item: Any + ) -> "Container": if key is None: raise ValueError("Key cannot be null in insert_after()") @@ -426,7 +431,7 @@ def _insert_after( return self - def _insert_at(self, idx: int, key: Key | str, item: Any) -> Container: + def _insert_at(self, idx: int, key: Union[Key, str], item: Any) -> "Container": if idx > len(self._body) - 1: raise ValueError(f"Unable to insert at position {idx}") @@ -467,7 +472,7 @@ def _insert_at(self, idx: int, key: Key | str, item: Any) -> Container: return self - def item(self, key: Key | str) -> Item: + def item(self, key: Union[Key, str]) -> Item: """Get an item for the given key.""" if not isinstance(key, Key): key = SingleKey(key) @@ -484,7 +489,7 @@ def item(self, key: Key | str) -> Item: return self._body[idx][1] - def last_item(self) -> Item | None: + def last_item(self) -> Optional[Item]: """Get the last item.""" if self._body: return self._body[-1][1] @@ -505,7 +510,9 @@ def as_string(self) -> str: return s - def _render_table(self, key: Key, table: Table, prefix: str | None = None) -> str: + def _render_table( + self, key: Key, table: Table, prefix: Optional[str] = None + ) -> str: cur = "" if table.display_name is not None: @@ -573,7 +580,7 @@ def _render_aot(self, key, aot, prefix=None): return cur - def _render_aot_table(self, table: Table, prefix: str | None = None) -> str: + def _render_aot_table(self, table: Table, prefix: Optional[str] = None) -> str: cur = "" _key = prefix or "" @@ -633,7 +640,7 @@ def __iter__(self) -> Iterator[str]: return iter(dict.keys(self)) # Dictionary methods - def __getitem__(self, key: Key | str) -> Item | Container: + def __getitem__(self, key: Union[Key, str]) -> Union[Item, "Container"]: if not isinstance(key, Key): key = SingleKey(key) @@ -653,21 +660,23 @@ def __getitem__(self, key: Key | str) -> Item | Container: return item - def __setitem__(self, key: Key | str, value: Any) -> None: + def __setitem__(self, key: Union[Key, str], value: Any) -> None: if key is not None and key in self: old_key = next(filter(lambda k: k == key, self._map)) self._replace(old_key, key, value) else: self.append(key, value) - def __delitem__(self, key: Key | str) -> None: + def __delitem__(self, key: Union[Key, str]) -> None: self.remove(key) - def setdefault(self, key: Key | str, default: Any) -> Any: + def setdefault(self, key: Union[Key, str], default: Any) -> Any: super().setdefault(key, default=default) return self[key] - def _replace(self, key: Key | str, new_key: Key | str, value: Item) -> None: + def _replace( + self, key: Union[Key, str], new_key: Union[Key, str], value: Item + ) -> None: if not isinstance(key, Key): key = SingleKey(key) @@ -678,7 +687,7 @@ def _replace(self, key: Key | str, new_key: Key | str, value: Item) -> None: self._replace_at(idx, new_key, value) def _replace_at( - self, idx: int | tuple[int], new_key: Key | str, value: Item + self, idx: Union[int, Tuple[int]], new_key: Union[Key, str], value: Item ) -> None: value = _item(value) @@ -774,10 +783,10 @@ def __setstate__(self, state): if key is not None: dict.__setitem__(self, key.key, item.value) - def copy(self) -> Container: + def copy(self) -> "Container": return copy.copy(self) - def __copy__(self) -> Container: + def __copy__(self) -> "Container": c = self.__class__(self._parsed) for k, v in dict.items(self): dict.__setitem__(c, k, v) @@ -788,8 +797,8 @@ def __copy__(self) -> Container: return c def _previous_item_with_index( - self, idx: int | None = None, ignore=(Null,) - ) -> tuple[int, Item] | None: + self, idx: Optional[int] = None, ignore=(Null,) + ) -> Optional[Tuple[int, Item]]: """Find the immediate previous item before index ``idx``""" if idx is None or idx > len(self._body): idx = len(self._body) @@ -799,7 +808,9 @@ def _previous_item_with_index( return i, v return None - def _previous_item(self, idx: int | None = None, ignore=(Null,)) -> Item | None: + def _previous_item( + self, idx: Optional[int] = None, ignore=(Null,) + ) -> Optional[Item]: """Find the immediate previous item before index ``idx``. If ``idx`` is not given, the last item is returned. """ @@ -808,7 +819,7 @@ def _previous_item(self, idx: int | None = None, ignore=(Null,)) -> Item | None: class OutOfOrderTableProxy(_CustomDict): - def __init__(self, container: Container, indices: tuple[int]) -> None: + def __init__(self, container: Container, indices: Tuple[int]) -> None: self._container = container self._internal_container = Container(True) self._tables = [] @@ -833,13 +844,13 @@ def unwrap(self) -> str: def value(self): return self._internal_container.value - def __getitem__(self, key: Key | str) -> Any: + def __getitem__(self, key: Union[Key, str]) -> Any: if key not in self._internal_container: raise NonExistentKey(key) return self._internal_container[key] - def __setitem__(self, key: Key | str, item: Any) -> None: + def __setitem__(self, key: Union[Key, str], item: Any) -> None: if key in self._tables_map: table = self._tables[self._tables_map[key]] table[key] = item @@ -861,7 +872,7 @@ def _remove_table(self, table: Table) -> None: self._container._remove_at(idx) break - def __delitem__(self, key: Key | str) -> None: + def __delitem__(self, key: Union[Key, str]) -> None: if key in self._tables_map: table = self._tables[self._tables_map[key]] del table[key] @@ -881,7 +892,7 @@ def __iter__(self) -> Iterator[str]: def __len__(self) -> int: return dict.__len__(self) - def setdefault(self, key: Key | str, default: Any) -> Any: + def setdefault(self, key: Union[Key, str], default: Any) -> Any: super().setdefault(key, default=default) return self[key] diff --git a/tomlkit/exceptions.py b/tomlkit/exceptions.py index 7e1b7818..3147ca2a 100644 --- a/tomlkit/exceptions.py +++ b/tomlkit/exceptions.py @@ -1,6 +1,5 @@ -from __future__ import annotations - from typing import Collection +from typing import Optional class TOMLKitError(Exception): @@ -15,7 +14,7 @@ class ParseError(ValueError, TOMLKitError): location within the line where the error was encountered. """ - def __init__(self, line: int, col: int, message: str | None = None) -> None: + def __init__(self, line: int, col: int, message: Optional[str] = None) -> None: self._line = line self._col = col @@ -170,7 +169,7 @@ class InternalParserError(ParseError): An error that indicates a bug in the parser. """ - def __init__(self, line: int, col: int, message: str | None = None) -> None: + def __init__(self, line: int, col: int, message: Optional[str] = None) -> None: msg = "Internal parser error" if message: msg += f" ({message})" diff --git a/tomlkit/items.py b/tomlkit/items.py index 50a0ceae..71844825 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import abc import copy import re @@ -14,10 +12,14 @@ from typing import TYPE_CHECKING from typing import Any from typing import Collection +from typing import Dict from typing import Iterable from typing import Iterator +from typing import List +from typing import Optional from typing import Sequence from typing import TypeVar +from typing import Union from typing import cast from typing import overload @@ -61,57 +63,59 @@ class _CustomDict(MutableMapping, dict): @overload -def item(value: bool) -> Bool: +def item(value: bool) -> "Bool": ... @overload -def item(value: int) -> Integer: +def item(value: int) -> "Integer": ... @overload -def item(value: float) -> Float: +def item(value: float) -> "Float": ... @overload -def item(value: str) -> String: +def item(value: str) -> "String": ... @overload -def item(value: datetime) -> DateTime: +def item(value: datetime) -> "DateTime": ... @overload -def item(value: date) -> Date: +def item(value: date) -> "Date": ... @overload -def item(value: time) -> Time: +def item(value: time) -> "Time": ... @overload -def item(value: Sequence[dict]) -> AoT: +def item(value: Sequence[dict]) -> "AoT": ... @overload -def item(value: Sequence) -> Array: +def item(value: Sequence) -> "Array": ... @overload -def item(value: dict, _parent: Array = ..., _sort_keys: bool = ...) -> InlineTable: +def item(value: dict, _parent: "Array" = ..., _sort_keys: bool = ...) -> "InlineTable": ... @overload -def item(value: dict, _parent: Item | None = ..., _sort_keys: bool = ...) -> Table: +def item( + value: dict, _parent: Optional["Item"] = ..., _sort_keys: bool = ... +) -> "Table": ... @@ -120,7 +124,9 @@ def item(value: ItemT) -> ItemT: ... -def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> Item: +def item( + value: Any, _parent: Optional["Item"] = None, _sort_keys: bool = False +) -> "Item": """Create a TOML item from a Python object. :Example: @@ -237,7 +243,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @classmethod - def select(cls, literal=False, multiline=False) -> StringType: + def select(cls, literal=False, multiline=False) -> "StringType": return { (False, False): cls.SLB, (False, True): cls.MLB, @@ -284,7 +290,7 @@ def _is_singleline(self) -> bool: def _is_multiline(self) -> bool: return self in {StringType.MLB, StringType.MLL} - def _toggle(self) -> StringType: + def _toggle(self) -> "StringType": return { StringType.SLB: StringType.MLB, StringType.MLB: StringType.SLB, @@ -299,7 +305,6 @@ class BoolType(Enum): def __init__(self, *args, **kwargs): self.__bool__ = lru_cache(maxsize=None)(self._bool) - super().__init__(*args, **kwargs) def _bool(self): return {BoolType.TRUE: True, BoolType.FALSE: False}[self] @@ -335,7 +340,7 @@ def __init__( self.trail = trail - def copy(self) -> Trivia: + def copy(self) -> "Trivia": return type(self)(self.indent, self.comment_ws, self.comment, self.trail) @@ -357,7 +362,7 @@ class Key(abc.ABC): sep: str _original: str - _keys: list[SingleKey] + _keys: List["SingleKey"] _dotted: bool key: str @@ -373,10 +378,10 @@ def is_dotted(self) -> bool: """If the key is followed by other keys""" return self._dotted - def __iter__(self) -> Iterator[SingleKey]: + def __iter__(self) -> Iterator["SingleKey"]: return iter(self._keys) - def concat(self, other: Key) -> DottedKey: + def concat(self, other: "Key") -> "DottedKey": """Concatenate keys into a dotted key""" keys = self._keys + other._keys return DottedKey(keys, sep=self.sep) @@ -402,9 +407,9 @@ class SingleKey(Key): def __init__( self, k: str, - t: KeyType | None = None, - sep: str | None = None, - original: str | None = None, + t: Optional[KeyType] = None, + sep: Optional[str] = None, + original: Optional[str] = None, ) -> None: if t is None: if not k or any( @@ -451,8 +456,8 @@ class DottedKey(Key): def __init__( self, keys: Iterable[Key], - sep: str | None = None, - original: str | None = None, + sep: Optional[str] = None, + original: Optional[str] = None, ) -> None: self._keys = list(keys) if original is None: @@ -497,7 +502,7 @@ def unwrap(self): # Helpers - def comment(self, comment: str) -> Item: + def comment(self, comment: str) -> "Item": """Attach a comment to this item""" if not comment.strip().startswith("#"): comment = "# " + comment @@ -507,7 +512,7 @@ def comment(self, comment: str) -> Item: return self - def indent(self, indent: int) -> Item: + def indent(self, indent: int) -> "Item": """Indent this item with given number of spaces""" if self._trivia.indent.startswith("\n"): self._trivia.indent = "\n" + " " * indent @@ -601,7 +606,7 @@ class Integer(int, Item): An integer literal. """ - def __new__(cls, value: int, trivia: Trivia, raw: str) -> Integer: + def __new__(cls, value: int, trivia: Trivia, raw: str) -> "Integer": return super().__new__(cls, value) def __init__(self, _: int, trivia: Trivia, raw: str) -> None: @@ -798,7 +803,7 @@ def __new__( minute: int, second: int, microsecond: int, - tzinfo: tzinfo | None, + tzinfo: Optional[tzinfo], *_: Any, **kwargs: Any, ) -> datetime: @@ -824,9 +829,9 @@ def __init__( minute: int, second: int, microsecond: int, - tzinfo: tzinfo | None, - trivia: Trivia | None = None, - raw: str | None = None, + tzinfo: Optional[tzinfo], + trivia: Optional[Trivia] = None, + raw: Optional[str] = None, **kwargs: Any, ) -> None: super().__init__(trivia or Trivia()) @@ -905,7 +910,7 @@ def astimezone(self, tz: tzinfo) -> datetime: return result return self._new(result) - def _new(self, result) -> DateTime: + def _new(self, result) -> "DateTime": raw = result.isoformat() return DateTime( @@ -1008,7 +1013,7 @@ def __new__( minute: int, second: int, microsecond: int, - tzinfo: tzinfo | None, + tzinfo: Optional[tzinfo], *_: Any, ) -> time: return time.__new__(cls, hour, minute, second, microsecond, tzinfo) @@ -1019,7 +1024,7 @@ def __init__( minute: int, second: int, microsecond: int, - tzinfo: tzinfo | None, + tzinfo: Optional[tzinfo], trivia: Trivia, raw: str, ) -> None: @@ -1077,7 +1082,7 @@ class Array(Item, _CustomList): def __init__(self, value: list, trivia: Trivia, multiline: bool = False) -> None: super().__init__(trivia) - self._index_map: dict[int, int] = {} + self._index_map: Dict[int, int] = {} list.__init__( self, [v.value for v in value if not isinstance(v, (Whitespace, Comment))] ) @@ -1103,7 +1108,7 @@ def discriminant(self) -> int: def value(self) -> list: return self - def multiline(self, multiline: bool) -> Array: + def multiline(self, multiline: bool) -> "Array": """Change the array to display in multiline or not. :Example: @@ -1152,7 +1157,7 @@ def add_line( self, *items: Any, indent: str = " ", - comment: str | None = None, + comment: Optional[str] = None, add_comma: bool = True, newline: bool = True, ) -> None: @@ -1224,10 +1229,10 @@ def clear(self) -> None: def __len__(self) -> int: return list.__len__(self) - def __getitem__(self, key: int | slice) -> Any: + def __getitem__(self, key: Union[int, slice]) -> Any: return list.__getitem__(self, key) - def __setitem__(self, key: int | slice, value: Any) -> Any: + def __setitem__(self, key: Union[int, slice], value: Any) -> Any: it = item(value, _parent=self) list.__setitem__(self, key, it.value) if isinstance(key, slice): @@ -1287,7 +1292,7 @@ def insert(self, pos: int, value: Any) -> None: self._reindex() - def __delitem__(self, key: int | slice): + def __delitem__(self, key: Union[int, slice]): length = len(self) list.__delitem__(self, key) @@ -1330,7 +1335,7 @@ def _getstate(self, protocol=3): class AbstractTable(Item, _CustomDict): """Common behaviour of both :class:`Table` and :class:`InlineTable`""" - def __init__(self, value: container.Container, trivia: Trivia): + def __init__(self, value: "container.Container", trivia: Trivia): Item.__init__(self, trivia) self._value = value @@ -1351,26 +1356,26 @@ def unwrap(self): return unwrapped @property - def value(self) -> container.Container: + def value(self) -> "container.Container": return self._value @overload - def append(self: AT, key: None, value: Comment | Whitespace) -> AT: + def append(self: AT, key: None, value: Union[Comment, Whitespace]) -> AT: ... @overload - def append(self: AT, key: Key | str, value: Any) -> AT: + def append(self: AT, key: Union[Key, str], value: Any) -> AT: ... def append(self, key, value): raise NotImplementedError @overload - def add(self: AT, value: Comment | Whitespace) -> AT: + def add(self: AT, value: Union[Comment, Whitespace]) -> AT: ... @overload - def add(self: AT, key: Key | str, value: Any) -> AT: + def add(self: AT, key: Union[Key, str], value: Any) -> AT: ... def add(self, key, value=None): @@ -1383,7 +1388,7 @@ def add(self, key, value=None): return self.append(key, value) - def remove(self: AT, key: Key | str) -> AT: + def remove(self: AT, key: Union[Key, str]) -> AT: self._value.remove(key) if isinstance(key, Key): @@ -1394,7 +1399,7 @@ def remove(self: AT, key: Key | str) -> AT: return self - def setdefault(self, key: Key | str, default: Any) -> Any: + def setdefault(self, key: Union[Key, str], default: Any) -> Any: super().setdefault(key, default) return self[key] @@ -1413,13 +1418,13 @@ def __iter__(self) -> Iterator[str]: def __len__(self) -> int: return len(self._value) - def __delitem__(self, key: Key | str) -> None: + def __delitem__(self, key: Union[Key, str]) -> None: self.remove(key) - def __getitem__(self, key: Key | str) -> Item: + def __getitem__(self, key: Union[Key, str]) -> Item: return cast(Item, self._value[key]) - def __setitem__(self, key: Key | str, value: Any) -> None: + def __setitem__(self, key: Union[Key, str], value: Any) -> None: if not isinstance(value, Item): value = item(value) @@ -1452,12 +1457,12 @@ class Table(AbstractTable): def __init__( self, - value: container.Container, + value: "container.Container", trivia: Trivia, is_aot_element: bool, is_super_table: bool = False, - name: str | None = None, - display_name: str | None = None, + name: Optional[str] = None, + display_name: Optional[str] = None, ) -> None: super().__init__(value, trivia) @@ -1470,7 +1475,7 @@ def __init__( def discriminant(self) -> int: return 9 - def __copy__(self) -> Table: + def __copy__(self) -> "Table": return type(self)( self._value.copy(), self._trivia.copy(), @@ -1511,7 +1516,7 @@ def append(self, key, _item): return self - def raw_append(self, key: Key | str, _item: Any) -> Table: + def raw_append(self, key: Union[Key, str], _item: Any) -> "Table": """Similar to :meth:`append` but does not copy indentation.""" if not isinstance(_item, Item): _item = item(_item) @@ -1541,7 +1546,7 @@ def as_string(self) -> str: # Helpers - def indent(self, indent: int) -> Table: + def indent(self, indent: int) -> "Table": """Indent the table with given number of spaces.""" super().indent(indent) @@ -1581,7 +1586,7 @@ class InlineTable(AbstractTable): """ def __init__( - self, value: container.Container, trivia: Trivia, new: bool = False + self, value: "container.Container", trivia: Trivia, new: bool = False ) -> None: super().__init__(value, trivia) @@ -1647,12 +1652,12 @@ def as_string(self) -> str: return buf - def __setitem__(self, key: Key | str, value: Any) -> None: + def __setitem__(self, key: Union[Key, str], value: Any) -> None: if hasattr(value, "trivia") and value.trivia.comment: value.trivia.comment = "" super().__setitem__(key, value) - def __copy__(self) -> InlineTable: + def __copy__(self) -> "InlineTable": return type(self)(self._value.copy(), self._trivia.copy(), self._new) def _getstate(self, protocol: int = 3) -> tuple: @@ -1704,7 +1709,7 @@ def _getstate(self, protocol=3): return self._t, str(self), self._original, self._trivia @classmethod - def from_raw(cls, value: str, type_=StringType.SLB, escape=True) -> String: + def from_raw(cls, value: str, type_=StringType.SLB, escape=True) -> "String": value = decode(value) invalid = type_.invalid_sequences @@ -1723,10 +1728,10 @@ class AoT(Item, _CustomList): """ def __init__( - self, body: list[Table], name: str | None = None, parsed: bool = False + self, body: List[Table], name: Optional[str] = None, parsed: bool = False ) -> None: self.name = name - self._body: list[Table] = [] + self._body: List[Table] = [] self._parsed = parsed super().__init__(Trivia(trail="")) @@ -1744,7 +1749,7 @@ def unwrap(self) -> str: return unwrapped @property - def body(self) -> list[Table]: + def body(self) -> List[Table]: return self._body @property @@ -1752,14 +1757,14 @@ def discriminant(self) -> int: return 12 @property - def value(self) -> list[dict[Any, Any]]: + def value(self) -> List[Dict[Any, Any]]: return [v.value for v in self._body] def __len__(self) -> int: return len(self._body) @overload - def __getitem__(self, key: slice) -> list[Table]: + def __getitem__(self, key: slice) -> List[Table]: ... @overload @@ -1769,10 +1774,10 @@ def __getitem__(self, key: int) -> Table: def __getitem__(self, key): return self._body[key] - def __setitem__(self, key: slice | int, value: Any) -> None: + def __setitem__(self, key: Union[slice, int], value: Any) -> None: raise NotImplementedError - def __delitem__(self, key: slice | int) -> None: + def __delitem__(self, key: Union[slice, int]) -> None: del self._body[key] list.__delitem__(self, key) diff --git a/tomlkit/parser.py b/tomlkit/parser.py index fd0e66b2..e7b09ca8 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import re import string @@ -48,6 +46,12 @@ if TYPE_CHECKING: + from typing import List + from typing import Optional + from typing import Tuple + from typing import Type + from typing import Union + from tomlkit.items import Item from tomlkit.items import Key @@ -68,7 +72,7 @@ def __init__(self, string: str) -> None: # Input to parse self._src = Source(decode(string)) - self._aot_stack: list[Key] = [] + self._aot_stack: List[Key] = [] @property def _state(self): @@ -92,14 +96,14 @@ def extract(self) -> str: """ return self._src.extract() - def inc(self, exception: type[ParseError] | None = None) -> bool: + def inc(self, exception: Optional[Type[ParseError]] = None) -> bool: """ Increments the parser if the end of the input has not been reached. Returns whether or not it was able to advance. """ return self._src.inc(exception=exception) - def inc_n(self, n: int, exception: type[ParseError] | None = None) -> bool: + def inc_n(self, n: int, exception: Optional[Type[ParseError]] = None) -> bool: """ Increments the parser by n characters if the end of the input has not been reached. @@ -205,7 +209,7 @@ def _is_child(self, parent: Key, child: Key) -> bool: return parent_parts == child_parts[: len(parent_parts)] - def _parse_item(self) -> tuple[Key | None, Item] | None: + def _parse_item(self) -> Optional[Tuple[Optional[Key], Item]]: """ Attempts to parse the next item and returns it, along with its key if the item is value-like. @@ -241,7 +245,7 @@ def _parse_item(self) -> tuple[Key | None, Item] | None: return self._parse_key_value(True) - def _parse_comment_trail(self, parse_trail: bool = True) -> tuple[str, str, str]: + def _parse_comment_trail(self, parse_trail: bool = True) -> Tuple[str, str, str]: """ Returns (comment_ws, comment, trail) If there is no comment, comment_ws and comment will @@ -302,7 +306,7 @@ def _parse_comment_trail(self, parse_trail: bool = True) -> tuple[str, str, str] return comment_ws, comment, trail - def _parse_key_value(self, parse_comment: bool = False) -> tuple[Key, Item]: + def _parse_key_value(self, parse_comment: bool = False) -> Tuple[Key, Item]: # Leading indent self.mark() @@ -559,7 +563,7 @@ def _parse_array(self) -> Array: # Consume opening bracket, EOF here is an issue (middle of array) self.inc(exception=UnexpectedEofError) - elems: list[Item] = [] + elems: List[Item] = [] prev_value = None while True: # consume whitespace @@ -666,7 +670,7 @@ def _parse_inline_table(self) -> InlineTable: return InlineTable(elems, Trivia()) - def _parse_number(self, raw: str, trivia: Trivia) -> Item | None: + def _parse_number(self, raw: str, trivia: Trivia) -> Optional[Item]: # Leading zeros are not allowed sign = "" if raw.startswith(("+", "-")): @@ -876,8 +880,8 @@ def _parse_string(self, delim: StringType) -> String: self.inc(exception=UnexpectedEofError) def _parse_table( - self, parent_name: Key | None = None, parent: Table | None = None - ) -> tuple[Key, Table | AoT]: + self, parent_name: Optional[Key] = None, parent: Optional[Table] = None + ) -> Tuple[Key, Union[Table, AoT]]: """ Parses a table element. """ @@ -1031,7 +1035,7 @@ def _parse_table( return key, result - def _peek_table(self) -> tuple[bool, Key]: + def _peek_table(self) -> Tuple[bool, Key]: """ Peeks ahead non-intrusively by cloning then restoring the initial state of the parser. @@ -1095,7 +1099,7 @@ def _peek(self, n: int) -> str: break return buf - def _peek_unicode(self, is_long: bool) -> tuple[str | None, str | None]: + def _peek_unicode(self, is_long: bool) -> Tuple[Optional[str], Optional[str]]: """ Peeks ahead non-intrusively by cloning then restoring the initial state of the parser. diff --git a/tomlkit/source.py b/tomlkit/source.py index ab29854a..d1a53cdd 100644 --- a/tomlkit/source.py +++ b/tomlkit/source.py @@ -1,7 +1,8 @@ -from __future__ import annotations - from copy import copy from typing import Any +from typing import Optional +from typing import Tuple +from typing import Type from tomlkit.exceptions import ParseError from tomlkit.exceptions import UnexpectedCharError @@ -11,15 +12,15 @@ class _State: def __init__( self, - source: Source, - save_marker: bool | None = False, - restore: bool | None = False, + source: "Source", + save_marker: Optional[bool] = False, + restore: Optional[bool] = False, ) -> None: self._source = source self._save_marker = save_marker self.restore = restore - def __enter__(self) -> _State: + def __enter__(self) -> "_State": # Entering this context manager - save the state self._chars = copy(self._source._chars) self._idx = self._source._idx @@ -43,7 +44,7 @@ class _StateHandler: State preserver for the Parser. """ - def __init__(self, source: Source) -> None: + def __init__(self, source: "Source") -> None: self._source = source self._states = [] @@ -106,7 +107,7 @@ def extract(self) -> str: """ return self[self._marker : self._idx] - def inc(self, exception: type[ParseError] | None = None) -> bool: + def inc(self, exception: Optional[Type[ParseError]] = None) -> bool: """ Increments the parser if the end of the input has not been reached. Returns whether or not it was able to advance. @@ -123,7 +124,7 @@ def inc(self, exception: type[ParseError] | None = None) -> bool: return False - def inc_n(self, n: int, exception: type[ParseError] | None = None) -> bool: + def inc_n(self, n: int, exception: Optional[Type[ParseError]] = None) -> bool: """ Increments the parser by n characters if the end of the input has not been reached. @@ -158,7 +159,7 @@ def mark(self) -> None: def parse_error( self, - exception: type[ParseError] = ParseError, + exception: Type[ParseError] = ParseError, *args: Any, **kwargs: Any, ) -> ParseError: @@ -169,7 +170,7 @@ def parse_error( return exception(line, col, *args, **kwargs) - def _to_linecol(self) -> tuple[int, int]: + def _to_linecol(self) -> Tuple[int, int]: cur = 0 for i, line in enumerate(self.splitlines()): if cur + len(line) + 1 > self.idx: diff --git a/tomlkit/toml_char.py b/tomlkit/toml_char.py index d85b62cc..b6260e9e 100644 --- a/tomlkit/toml_char.py +++ b/tomlkit/toml_char.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import string from functools import lru_cache diff --git a/tomlkit/toml_document.py b/tomlkit/toml_document.py index 7e3b4024..71fac2e1 100644 --- a/tomlkit/toml_document.py +++ b/tomlkit/toml_document.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from tomlkit.container import Container diff --git a/tomlkit/toml_file.py b/tomlkit/toml_file.py index b8b5b998..34880c15 100644 --- a/tomlkit/toml_file.py +++ b/tomlkit/toml_file.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os import re From ace28ebd13a43b49d2c83478342b92290621c71f Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Mon, 25 Jul 2022 14:36:47 +0200 Subject: [PATCH 13/15] Remove unnecessary option --- .flake8 | 1 - 1 file changed, 1 deletion(-) diff --git a/.flake8 b/.flake8 index 0ec0d0a7..a6b4d120 100644 --- a/.flake8 +++ b/.flake8 @@ -3,7 +3,6 @@ min_python_version = 3.6.0 max-line-length = 88 ignore = E501, E203, W503 per-file-ignores = - __init__.py:F401 # N818: error suffix in exception names (API-breaking change) tomlkit/exceptions.py: N818, # FS003: f-string missing prefix From f96fa94534f116da630be85c44dba80dbd65c9d7 Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Mon, 25 Jul 2022 14:40:01 +0200 Subject: [PATCH 14/15] Remove unnecessary isort config option --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 66c7de8d..7d253362 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: rev: 5.10.1 hooks: - id: isort - exclude: docs/.*|^.*__init__\.py$ + exclude: docs/.* - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 From d74bbb84d2310105d4a9ad28ff194ebf4150ccba Mon Sep 17 00:00:00 2001 From: Bartek Sokorski Date: Mon, 25 Jul 2022 16:17:26 +0200 Subject: [PATCH 15/15] Fix tests --- tomlkit/api.py | 22 +++++++++------------- tomlkit/items.py | 19 +++++++++++-------- tomlkit/parser.py | 19 +++++++------------ tomlkit/toml_file.py | 7 +------ 4 files changed, 28 insertions(+), 39 deletions(-) diff --git a/tomlkit/api.py b/tomlkit/api.py index 6b5a2ca2..49573b5e 100644 --- a/tomlkit/api.py +++ b/tomlkit/api.py @@ -2,7 +2,6 @@ from collections.abc import Mapping from typing import IO -from typing import TYPE_CHECKING from typing import Iterable from typing import Tuple from typing import Union @@ -11,13 +10,22 @@ from tomlkit.container import Container from tomlkit.exceptions import UnexpectedCharError from tomlkit.items import AoT +from tomlkit.items import Array +from tomlkit.items import Bool from tomlkit.items import Comment +from tomlkit.items import Date +from tomlkit.items import DateTime from tomlkit.items import DottedKey +from tomlkit.items import Float from tomlkit.items import InlineTable +from tomlkit.items import Integer +from tomlkit.items import Item as _Item +from tomlkit.items import Key from tomlkit.items import SingleKey from tomlkit.items import String from tomlkit.items import StringType as _StringType from tomlkit.items import Table +from tomlkit.items import Time from tomlkit.items import Trivia from tomlkit.items import Whitespace from tomlkit.items import item @@ -25,18 +33,6 @@ from tomlkit.toml_document import TOMLDocument -if TYPE_CHECKING: - from tomlkit.items import Array - from tomlkit.items import Bool - from tomlkit.items import Date - from tomlkit.items import DateTime - from tomlkit.items import Float - from tomlkit.items import Integer - from tomlkit.items import Item as _Item - from tomlkit.items import Key - from tomlkit.items import Time - - def loads(string: Union[str, bytes]) -> TOMLDocument: """ Parses a string into a TOMLDocument. diff --git a/tomlkit/items.py b/tomlkit/items.py index 71844825..c9564078 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -223,6 +223,12 @@ def item( raise ValueError(f"Invalid type {type(value)}") +# This code is only valid for Python < 3.8, when @cached_property was introduced +# it replaces chained @property and @lru_cache decorators +def lazy_property(f): + return property(lru_cache(maxsize=None)(f)) + + class StringType(Enum): # Single Line Basic SLB = '"' @@ -233,14 +239,12 @@ class StringType(Enum): # Multi Line Literal MLL = "'''" - def __init__(self, *args, **kwargs): - self.unit = property(lru_cache(maxsize=None)(self._unit)) + def __init__(self, value): self.is_basic = lru_cache(maxsize=None)(self._is_basic) self.is_literal = lru_cache(maxsize=None)(self._is_literal) self.is_singleline = lru_cache(maxsize=None)(self._is_singleline) self.is_multiline = lru_cache(maxsize=None)(self._is_multiline) self.toggle = lru_cache(maxsize=None)(self._toggle) - super().__init__(*args, **kwargs) @classmethod def select(cls, literal=False, multiline=False) -> "StringType": @@ -275,7 +279,8 @@ def invalid_sequences(self) -> Collection[str]: StringType.MLL: (forbidden_in_literal | {"'''"}) - allowed_in_multiline, }[self] - def _unit(self) -> str: + @lazy_property + def unit(self) -> str: return self.value[0] def _is_basic(self) -> bool: @@ -303,10 +308,8 @@ class BoolType(Enum): TRUE = "true" FALSE = "false" - def __init__(self, *args, **kwargs): - self.__bool__ = lru_cache(maxsize=None)(self._bool) - - def _bool(self): + @lru_cache(maxsize=None) # noqa: B019 + def __bool__(self): return {BoolType.TRUE: True, BoolType.FALSE: False}[self] def __iter__(self): diff --git a/tomlkit/parser.py b/tomlkit/parser.py index e7b09ca8..e3905790 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -1,7 +1,11 @@ import re import string -from typing import TYPE_CHECKING +from typing import List +from typing import Optional +from typing import Tuple +from typing import Type +from typing import Union from tomlkit._compat import decode from tomlkit._utils import RFC_3339_LOOSE @@ -31,6 +35,8 @@ from tomlkit.items import Float from tomlkit.items import InlineTable from tomlkit.items import Integer +from tomlkit.items import Item +from tomlkit.items import Key from tomlkit.items import KeyType from tomlkit.items import Null from tomlkit.items import SingleKey @@ -45,17 +51,6 @@ from tomlkit.toml_document import TOMLDocument -if TYPE_CHECKING: - from typing import List - from typing import Optional - from typing import Tuple - from typing import Type - from typing import Union - - from tomlkit.items import Item - from tomlkit.items import Key - - CTRL_I = 0x09 # Tab CTRL_J = 0x0A # Line feed CTRL_M = 0x0D # Carriage return diff --git a/tomlkit/toml_file.py b/tomlkit/toml_file.py index 34880c15..d05a62f1 100644 --- a/tomlkit/toml_file.py +++ b/tomlkit/toml_file.py @@ -1,13 +1,8 @@ import os import re -from typing import TYPE_CHECKING - from tomlkit.api import loads - - -if TYPE_CHECKING: - from tomlkit.toml_document import TOMLDocument +from tomlkit.toml_document import TOMLDocument class TOMLFile: