Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-tomli for openSUSE:Factory checked in at 2022-02-03 23:15:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-tomli (Old) and /work/SRC/openSUSE:Factory/.python-tomli.new.1898 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-tomli" Thu Feb 3 23:15:58 2022 rev:3 rq:948285 version:2.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-tomli/python-tomli.changes 2021-10-25 15:17:33.917680580 +0200 +++ /work/SRC/openSUSE:Factory/.python-tomli.new.1898/python-tomli.changes 2022-02-03 23:16:23.460651870 +0100 @@ -1,0 +2,23 @@ +Thu Jan 20 20:10:07 UTC 2022 - Matej Cepl <mc...@suse.com> + +- Upgrade to 2.0.0: + - Removed Python 3.6 support + - Support for text file objects as load input. Use binary file + objects instead. + - First argument of load and loads can no longer be passed by + keyword. + - Allow lower case "t" and "z" in datetimes + - Raise an error when dotted keys define values outside the + "current table". Technically speaking TOML v1.0.0 does allow + such assignments but that isn't intended by specification + writers, and will change in a future specification version + (see the pull request). + - Illegal characters in error messages were surrounded by two + pairs of quotation marks + - TOMLDecodeError.__module__ is now the public import path + (tomli) instead of private import path (tomli._parser) + - Eliminated an import cycle when typing.TYPE_CHECKING is + True. This allows sphinx-autodoc-typehints to resolve type + annotations. + +------------------------------------------------------------------- Old: ---- tomli-1.2.1-gh.tar.gz New: ---- tomli-2.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-tomli.spec ++++++ --- /var/tmp/diff_new_pack.0SicVN/_old 2022-02-03 23:16:24.360645726 +0100 +++ /var/tmp/diff_new_pack.0SicVN/_new 2022-02-03 23:16:24.364645699 +0100 @@ -1,7 +1,7 @@ # # spec file # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -27,13 +27,13 @@ %{?!python_module:%define python_module() python3-%{**}} %define skip_python2 1 Name: python-tomli%{psuffix} -Version: 1.2.1 +Version: 2.0.0 Release: 0 Summary: A lil' TOML parser License: MIT URL: https://github.com/hukkin/tomli # prefer github archive over pypi sdist for pacakged tests -Source: https://github.com/hukkin/tomli/archive/refs/tags/%{version}.tar.gz#/tomli-%{version}-gh.tar.gz +Source: https://github.com/hukkin/tomli/archive/refs/tags/%{version}.tar.gz#/tomli-%{version}.tar.gz BuildRequires: %{python_module flit-core} BuildRequires: %{python_module pip} # Avoid build cycles @@ -58,7 +58,7 @@ Tomli is a Python library for parsing TOML %prep -%setup -q -n tomli-%{version} +%autosetup -p1 -n tomli-%{version} %build export PYTHONPATH=$PWD ++++++ tomli-1.2.1-gh.tar.gz -> tomli-2.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/.bumpversion.cfg new/tomli-2.0.0/.bumpversion.cfg --- old/tomli-1.2.1/.bumpversion.cfg 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/.bumpversion.cfg 2021-12-13 22:09:35.000000000 +0100 @@ -2,7 +2,7 @@ commit = True tag = True tag_name = {new_version} -current_version = 1.2.1 +current_version = 2.0.0 [bumpversion:file:pyproject.toml] search = version = "{current_version}" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/.github/workflows/tests.yaml new/tomli-2.0.0/.github/workflows/tests.yaml --- old/tomli-1.2.1/.github/workflows/tests.yaml 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/.github/workflows/tests.yaml 2021-12-13 22:09:35.000000000 +0100 @@ -32,9 +32,9 @@ runs-on: ${{ matrix.os }} strategy: matrix: - python-version: ['pypy-3.6', 'pypy-3.7', '3.6', '3.7', '3.8', '3.9', '3.10-dev'] + python-version: ['pypy-3.7', '3.7', '3.8', '3.9', '3.10', '3.11-dev'] os: [ubuntu-latest, macos-latest, windows-latest] - continue-on-error: ${{ matrix.python-version == '3.10-dev' }} + continue-on-error: ${{ matrix.python-version == '3.11-dev' }} steps: - uses: actions/checkout@v2 @@ -50,11 +50,12 @@ - name: Test with pytest run: | - pytest --cov --cov-fail-under=100 + # Use 'python -m pytest' to add CWD to sys.path + python -m pytest --cov --cov-fail-under=100 - name: Report coverage - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.6' - uses: codecov/codecov-action@v1 + if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10' + uses: codecov/codecov-action@v2 test-built-package: runs-on: ubuntu-latest @@ -96,12 +97,16 @@ - uses: actions/setup-python@v2 with: python-version: '3.7' - - name: Install Flit + - name: Install build and publish tools run: | - pip install "flit==3.3.0" - - name: Build and publish + pip install build twine + - name: Build and check run: | - flit publish --no-setup-py + rm -rf dist/ && python -m build + twine check --strict dist/* + - name: Publish + run: | + twine upload dist/* env: - FLIT_USERNAME: __token__ - FLIT_PASSWORD: ${{ secrets.PYPI_TOKEN }} + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/.pre-commit-config.yaml new/tomli-2.0.0/.pre-commit-config.yaml --- old/tomli-1.2.1/.pre-commit-config.yaml 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/.pre-commit-config.yaml 2021-12-13 22:09:35.000000000 +0100 @@ -1,6 +1,6 @@ repos: - repo: https://github.com/executablebooks/mdformat - rev: b9b885e183ca16670b6d4a5ef8058664395dec58 # frozen: 0.7.7 + rev: 427df9181bd4d8e65c1108b912ad47a81628f03b # frozen: 0.7.10 hooks: - id: mdformat additional_dependencies: @@ -16,11 +16,11 @@ - flake8-builtins - flake8-comprehensions - repo: https://github.com/PyCQA/isort - rev: 6e4281f018ff848226d8993596765b2285e1624f # frozen: 5.9.2 + rev: fd5ba70665a37ec301a1f714ed09336048b3be63 # frozen: 5.9.3 hooks: - id: isort - repo: https://github.com/psf/black - rev: e3000ace2fd1fcb1c181bb7a8285f1f976bcbdc7 # frozen: 21.7b0 + rev: 911470a610e47d9da5ea938b0887c3df62819b85 # frozen: 21.9b0 hooks: - id: black - repo: https://github.com/myint/docformatter @@ -38,7 +38,7 @@ - id: python-check-blanket-noqa - id: python-check-blanket-type-ignore - repo: https://github.com/PyCQA/flake8 - rev: dcd740bc0ebaf2b3d43e59a0060d157c97de13f3 # frozen: 3.9.2 + rev: cbeb4c9c4137cff1568659fcc48e8b85cddd0c8d # frozen: 4.0.1 hooks: - id: flake8 additional_dependencies: @@ -46,7 +46,7 @@ - flake8-builtins - flake8-comprehensions - repo: https://github.com/pre-commit/mirrors-mypy - rev: 44afb68a9695d04030edc5cdc5a4fc4f17e4f9e2 # frozen: v0.910 + rev: 5cf22ccb774a8be8f47dfe4c1e8c4f177c608cbf # frozen: v0.910-1 hooks: - id: mypy args: ["--scripts-are-modules"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/CHANGELOG.md new/tomli-2.0.0/CHANGELOG.md --- old/tomli-1.2.1/CHANGELOG.md 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/CHANGELOG.md 2021-12-13 22:09:35.000000000 +0100 @@ -1,5 +1,28 @@ # Changelog +## 2.0.0 + +- Removed + - Python 3.6 support + - Support for text file objects as `load` input. Use binary file objects instead. + - First argument of `load` and `loads` can no longer be passed by keyword. +- Fixed + - Allow lower case "t" and "z" in datetimes +- Improved + - Raise an error when dotted keys define values outside the "current table". + Technically speaking TOML v1.0.0 does allow such assignments + but that isn't intended by specification writers, + and will change in a future specification version (see the [pull request](https://github.com/toml-lang/toml/pull/848)). + +## 1.2.2 + +- Fixed + - Illegal characters in error messages were surrounded by two pairs of quotation marks +- Improved + - `TOMLDecodeError.__module__` is now the public import path (`tomli`) instead of private import path (`tomli._parser`) + - Eliminated an import cycle when `typing.TYPE_CHECKING` is `True`. + This allows `sphinx-autodoc-typehints` to resolve type annotations. + ## 1.2.1 - Fixed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/README.md new/tomli-2.0.0/README.md --- old/tomli-1.2.1/README.md 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/README.md 2021-12-13 22:09:35.000000000 +0100 @@ -67,7 +67,6 @@ The file must be opened in binary mode (with the `"rb"` flag). Binary mode will enforce decoding the file as UTF-8 with universal newlines disabled, both of which are required to correctly parse TOML. -Support for text file objects is deprecated for removal in the next major release. ### Handle invalid TOML<a name="handle-invalid-toml"></a> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/pyproject.toml new/tomli-2.0.0/pyproject.toml --- old/tomli-1.2.1/pyproject.toml 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/pyproject.toml 2021-12-13 22:09:35.000000000 +0100 @@ -4,13 +4,13 @@ [project] name = "tomli" -version = "1.2.1" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT +version = "2.0.0" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT description = "A lil' TOML parser" authors = [ { name = "Taneli Hukkinen", email = "huk...@users.noreply.github.com" }, ] license = { file = "LICENSE" } -requires-python = ">=3.6" +requires-python = ">=3.7" readme = "README.md" classifiers = [ "License :: OSI Approved :: MIT License", @@ -18,7 +18,6 @@ "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", @@ -35,16 +34,6 @@ "Changelog" = "https://github.com/hukkin/tomli/blob/master/CHANGELOG.md" -[tool.flit.sdist] -exclude = [ - "tests/", - "benchmark/", - "fuzzer/", - ".*", - "CHANGELOG.md", -] - - [tool.isort] # Force imports to be sorted by module, independent of import type force_sort_within_sections = true @@ -67,17 +56,18 @@ legacy_tox_ini = ''' [tox] # Only run pytest envs when no args given to tox -envlist = py{36,37,38,39} +envlist = py{37,38,39,310} isolated_build = True -[testenv:py{36,37,38,39}] +[testenv:py{37,38,39,310}] description = run tests against unpackaged source skip_install = True deps = -r tests/requirements.txt commands = - pytest {posargs} + # Use 'python -m pytest' to add CWD to sys.path + python -m pytest {posargs} -[testenv:py{36,37,38,39}-package] +[testenv:py{37,38,39,310}-package] description = run tests against a built package (can fail, in theory, if test dependencies need a tomli version incompatible with local state) deps = -r tests/requirements.txt commands = @@ -139,15 +129,15 @@ [tool.coverage.run] branch = true -omit = ["tests/*", "*/site-packages/*"] +source = ['tomli'] [tool.coverage.report] # Regexes for lines to exclude from consideration exclude_lines = [ - # Have to re-enable the standard pragma - "pragma: no cover", + # Re-enable the standard pragma (with extra strictness) + '# pragma: no cover\b', # Code for static type checkers - "if TYPE_CHECKING:", + 'if TYPE_CHECKING:', # Scripts 'if __name__ == .__main__.:', ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tests/data/extras/invalid/dotted-keys/extend-defined-aot.toml new/tomli-2.0.0/tests/data/extras/invalid/dotted-keys/extend-defined-aot.toml --- old/tomli-1.2.1/tests/data/extras/invalid/dotted-keys/extend-defined-aot.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/tomli-2.0.0/tests/data/extras/invalid/dotted-keys/extend-defined-aot.toml 2021-12-13 22:09:35.000000000 +0100 @@ -0,0 +1,3 @@ +[[tab.arr]] +[tab] +arr.val1=1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tests/data/extras/invalid/dotted-keys/extend-defined-table-with-subtable.toml new/tomli-2.0.0/tests/data/extras/invalid/dotted-keys/extend-defined-table-with-subtable.toml --- old/tomli-1.2.1/tests/data/extras/invalid/dotted-keys/extend-defined-table-with-subtable.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/tomli-2.0.0/tests/data/extras/invalid/dotted-keys/extend-defined-table-with-subtable.toml 2021-12-13 22:09:35.000000000 +0100 @@ -0,0 +1,4 @@ +[a.b.c.d] + z = 9 +[a] + b.c.d.k.t = 8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tests/data/extras/invalid/dotted-keys/extend-defined-table.toml new/tomli-2.0.0/tests/data/extras/invalid/dotted-keys/extend-defined-table.toml --- old/tomli-1.2.1/tests/data/extras/invalid/dotted-keys/extend-defined-table.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/tomli-2.0.0/tests/data/extras/invalid/dotted-keys/extend-defined-table.toml 2021-12-13 22:09:35.000000000 +0100 @@ -0,0 +1,4 @@ +[a.b.c] + z = 9 +[a] + b.c.t = 9 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tests/requirements.txt new/tomli-2.0.0/tests/requirements.txt --- old/tomli-1.2.1/tests/requirements.txt 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/tests/requirements.txt 2021-12-13 22:09:35.000000000 +0100 @@ -2,3 +2,4 @@ pytest-randomly pytest-cov >=2.12.1 python-dateutil +coverage !=6.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tests/test_error.py new/tomli-2.0.0/tests/test_error.py --- old/tomli-1.2.1/tests/test_error.py 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/tests/test_error.py 2021-12-13 22:09:35.000000000 +0100 @@ -25,3 +25,13 @@ with pytest.raises(tomli.TOMLDecodeError) as exc_info: tomli.loads("\n\nfwfw=") assert str(exc_info.value) == "Invalid value (at end of document)" + + +def test_invalid_char_quotes(): + with pytest.raises(tomli.TOMLDecodeError) as exc_info: + tomli.loads("v = '\n'") + assert " '\\n' " in str(exc_info.value) + + +def test_module_name(): + assert tomli.TOMLDecodeError().__module__ == "tomli" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tests/test_flags.py new/tomli-2.0.0/tests/test_flags.py --- old/tomli-1.2.1/tests/test_flags.py 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/tests/test_flags.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,13 +0,0 @@ -from tomli._parser import Flags - - -def test_set_for_relative_key(): - flags = Flags() - head_key = ("a", "b") - rel_key = ("c", "d") - flags.set_for_relative_key(head_key, rel_key, Flags.EXPLICIT_NEST) - assert not flags.is_(("a",), flags.EXPLICIT_NEST) - assert not flags.is_(("a", "b"), flags.EXPLICIT_NEST) - assert flags.is_(("a", "b", "c"), flags.EXPLICIT_NEST) - assert flags.is_(("a", "b", "c", "d"), flags.EXPLICIT_NEST) - assert not flags.is_(("a", "b", "c", "d", "e"), flags.EXPLICIT_NEST) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tests/test_misc.py new/tomli-2.0.0/tests/test_misc.py --- old/tomli-1.2.1/tests/test_misc.py 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/tests/test_misc.py 2021-12-13 22:09:35.000000000 +0100 @@ -12,12 +12,6 @@ file_path = tmp_path / "test.toml" file_path.write_text(content) - # Test text mode - with open(file_path, encoding="utf-8", newline="") as f: - actual = tomli.load(f) # type: ignore[arg-type] - assert actual == expected - - # Test binary mode with open(file_path, "rb") as bin_f: actual = tomli.load(bin_f) assert actual == expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tomli/__init__.py new/tomli-2.0.0/tomli/__init__.py --- old/tomli-1.2.1/tomli/__init__.py 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/tomli/__init__.py 2021-12-13 22:09:35.000000000 +0100 @@ -1,6 +1,9 @@ """A lil' TOML parser.""" __all__ = ("loads", "load", "TOMLDecodeError") -__version__ = "1.2.1" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT +__version__ = "2.0.0" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT from tomli._parser import TOMLDecodeError, load, loads + +# Pretend this exception was created here. +TOMLDecodeError.__module__ = __name__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tomli/_parser.py new/tomli-2.0.0/tomli/_parser.py --- old/tomli-1.2.1/tomli/_parser.py 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/tomli/_parser.py 2021-12-13 22:09:35.000000000 +0100 @@ -1,17 +1,9 @@ +from __future__ import annotations + +from collections.abc import Iterable import string from types import MappingProxyType -from typing import ( - Any, - BinaryIO, - Callable, - Dict, - FrozenSet, - Iterable, - NamedTuple, - Optional, - Tuple, -) -import warnings +from typing import Any, BinaryIO, NamedTuple from tomli._re import ( RE_DATETIME, @@ -21,6 +13,7 @@ match_to_localtime, match_to_number, ) +from tomli._types import Key, ParseFloat, Pos ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) @@ -52,37 +45,23 @@ } ) -# Type annotations -ParseFloat = Callable[[str], Any] -Key = Tuple[str, ...] -Pos = int - class TOMLDecodeError(ValueError): """An error raised if a document is not valid TOML.""" -def load(fp: BinaryIO, *, parse_float: ParseFloat = float) -> Dict[str, Any]: +def load(__fp: BinaryIO, *, parse_float: ParseFloat = float) -> dict[str, Any]: """Parse TOML from a binary file object.""" - s_bytes = fp.read() - try: - s = s_bytes.decode() - except AttributeError: - warnings.warn( - "Text file object support is deprecated in favor of binary file objects." - ' Use `open("foo.toml", "rb")` to open the file in binary mode.', - DeprecationWarning, - ) - s = s_bytes # type: ignore[assignment] + s = __fp.read().decode() return loads(s, parse_float=parse_float) -def loads(s: str, *, parse_float: ParseFloat = float) -> Dict[str, Any]: # noqa: C901 +def loads(__s: str, *, parse_float: ParseFloat = float) -> dict[str, Any]: # noqa: C901 """Parse TOML from a string.""" # The spec allows converting "\r\n" to "\n", even in string # literals. Let's do so to simplify parsing. - src = s.replace("\r\n", "\n") + src = __s.replace("\r\n", "\n") pos = 0 out = Output(NestedDict(), Flags()) header: Key = () @@ -113,9 +92,10 @@ pos = skip_chars(src, pos, TOML_WS) elif char == "[": try: - second_char: Optional[str] = src[pos + 1] + second_char: str | None = src[pos + 1] except IndexError: second_char = None + out.flags.finalize_pending() if second_char == "[": pos, header = create_list_rule(src, pos, out) else: @@ -151,7 +131,16 @@ EXPLICIT_NEST = 1 def __init__(self) -> None: - self._flags: Dict[str, dict] = {} + self._flags: dict[str, dict] = {} + self._pending_flags: set[tuple[Key, int]] = set() + + def add_pending(self, key: Key, flag: int) -> None: + self._pending_flags.add((key, flag)) + + def finalize_pending(self) -> None: + for key, flag in self._pending_flags: + self.set(key, flag, recursive=False) + self._pending_flags.clear() def unset_all(self, key: Key) -> None: cont = self._flags @@ -161,19 +150,6 @@ cont = cont[k]["nested"] cont.pop(key[-1], None) - def set_for_relative_key(self, head_key: Key, rel_key: Key, flag: int) -> None: - cont = self._flags - for k in head_key: - if k not in cont: - cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}} - cont = cont[k]["nested"] - for k in rel_key: - if k in cont: - cont[k]["flags"].add(flag) - else: - cont[k] = {"flags": {flag}, "recursive_flags": set(), "nested": {}} - cont = cont[k]["nested"] - def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003 cont = self._flags key_parent, key_stem = key[:-1], key[-1] @@ -206,7 +182,7 @@ class NestedDict: def __init__(self) -> None: # The parsed content of the TOML document - self.dict: Dict[str, Any] = {} + self.dict: dict[str, Any] = {} def get_or_create_nest( self, @@ -257,7 +233,7 @@ pos: Pos, expect: str, *, - error_on: FrozenSet[str], + error_on: frozenset[str], error_on_eof: bool, ) -> Pos: try: @@ -265,18 +241,18 @@ except ValueError: new_pos = len(src) if error_on_eof: - raise suffixed_err(src, new_pos, f'Expected "{expect!r}"') + raise suffixed_err(src, new_pos, f"Expected {expect!r}") from None if not error_on.isdisjoint(src[pos:new_pos]): while src[pos] not in error_on: pos += 1 - raise suffixed_err(src, pos, f'Found invalid character "{src[pos]!r}"') + raise suffixed_err(src, pos, f"Found invalid character {src[pos]!r}") return new_pos def skip_comment(src: str, pos: Pos) -> Pos: try: - char: Optional[str] = src[pos] + char: str | None = src[pos] except IndexError: char = None if char == "#": @@ -295,7 +271,7 @@ return pos -def create_dict_rule(src: str, pos: Pos, out: Output) -> Tuple[Pos, Key]: +def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: pos += 1 # Skip "[" pos = skip_chars(src, pos, TOML_WS) pos, key = parse_key(src, pos) @@ -306,14 +282,14 @@ try: out.data.get_or_create_nest(key) except KeyError: - raise suffixed_err(src, pos, "Can not overwrite a value") + raise suffixed_err(src, pos, "Can not overwrite a value") from None if not src.startswith("]", pos): raise suffixed_err(src, pos, 'Expected "]" at the end of a table declaration') return pos + 1, key -def create_list_rule(src: str, pos: Pos, out: Output) -> Tuple[Pos, Key]: +def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]: pos += 2 # Skip "[[" pos = skip_chars(src, pos, TOML_WS) pos, key = parse_key(src, pos) @@ -327,7 +303,7 @@ try: out.data.append_nest_to_list(key) except KeyError: - raise suffixed_err(src, pos, "Can not overwrite a value") + raise suffixed_err(src, pos, "Can not overwrite a value") from None if not src.startswith("]]", pos): raise suffixed_err(src, pos, 'Expected "]]" at the end of an array declaration') @@ -341,16 +317,24 @@ key_parent, key_stem = key[:-1], key[-1] abs_key_parent = header + key_parent + relative_path_cont_keys = (header + key[:i] for i in range(1, len(key))) + for cont_key in relative_path_cont_keys: + # Check that dotted key syntax does not redefine an existing table + if out.flags.is_(cont_key, Flags.EXPLICIT_NEST): + raise suffixed_err(src, pos, f"Cannot redefine namespace {cont_key}") + # Containers in the relative path can't be opened with the table syntax or + # dotted key/value syntax in following table sections. + out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST) + if out.flags.is_(abs_key_parent, Flags.FROZEN): raise suffixed_err( - src, pos, f"Can not mutate immutable namespace {abs_key_parent}" + src, pos, f"Cannot mutate immutable namespace {abs_key_parent}" ) - # Containers in the relative path can't be opened with the table syntax after this - out.flags.set_for_relative_key(header, key, Flags.EXPLICIT_NEST) + try: nest = out.data.get_or_create_nest(abs_key_parent) except KeyError: - raise suffixed_err(src, pos, "Can not overwrite a value") + raise suffixed_err(src, pos, "Can not overwrite a value") from None if key_stem in nest: raise suffixed_err(src, pos, "Can not overwrite a value") # Mark inline table and array namespaces recursively immutable @@ -362,10 +346,10 @@ def parse_key_value_pair( src: str, pos: Pos, parse_float: ParseFloat -) -> Tuple[Pos, Key, Any]: +) -> tuple[Pos, Key, Any]: pos, key = parse_key(src, pos) try: - char: Optional[str] = src[pos] + char: str | None = src[pos] except IndexError: char = None if char != "=": @@ -376,13 +360,13 @@ return pos, key, value -def parse_key(src: str, pos: Pos) -> Tuple[Pos, Key]: +def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]: pos, key_part = parse_key_part(src, pos) key: Key = (key_part,) pos = skip_chars(src, pos, TOML_WS) while True: try: - char: Optional[str] = src[pos] + char: str | None = src[pos] except IndexError: char = None if char != ".": @@ -394,9 +378,9 @@ pos = skip_chars(src, pos, TOML_WS) -def parse_key_part(src: str, pos: Pos) -> Tuple[Pos, str]: +def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]: try: - char: Optional[str] = src[pos] + char: str | None = src[pos] except IndexError: char = None if char in BARE_KEY_CHARS: @@ -410,12 +394,12 @@ raise suffixed_err(src, pos, "Invalid initial character for a key part") -def parse_one_line_basic_str(src: str, pos: Pos) -> Tuple[Pos, str]: +def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]: pos += 1 return parse_basic_str(src, pos, multiline=False) -def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, list]: +def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list]: pos += 1 array: list = [] @@ -439,7 +423,7 @@ return pos + 1, array -def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, dict]: +def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, dict]: pos += 1 nested_dict = NestedDict() flags = Flags() @@ -455,9 +439,9 @@ try: nest = nested_dict.get_or_create_nest(key_parent, access_lists=False) except KeyError: - raise suffixed_err(src, pos, "Can not overwrite a value") + raise suffixed_err(src, pos, "Can not overwrite a value") from None if key_stem in nest: - raise suffixed_err(src, pos, f'Duplicate inline table key "{key_stem}"') + raise suffixed_err(src, pos, f"Duplicate inline table key {key_stem!r}") nest[key_stem] = value pos = skip_chars(src, pos, TOML_WS) c = src[pos : pos + 1] @@ -473,7 +457,7 @@ def parse_basic_str_escape( # noqa: C901 src: str, pos: Pos, *, multiline: bool = False -) -> Tuple[Pos, str]: +) -> tuple[Pos, str]: escape_id = src[pos : pos + 2] pos += 2 if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}: @@ -498,15 +482,15 @@ return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id] except KeyError: if len(escape_id) != 2: - raise suffixed_err(src, pos, "Unterminated string") - raise suffixed_err(src, pos, 'Unescaped "\\" in a string') + raise suffixed_err(src, pos, "Unterminated string") from None + raise suffixed_err(src, pos, 'Unescaped "\\" in a string') from None -def parse_basic_str_escape_multiline(src: str, pos: Pos) -> Tuple[Pos, str]: +def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]: return parse_basic_str_escape(src, pos, multiline=True) -def parse_hex_char(src: str, pos: Pos, hex_len: int) -> Tuple[Pos, str]: +def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]: hex_str = src[pos : pos + hex_len] if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str): raise suffixed_err(src, pos, "Invalid hex value") @@ -517,7 +501,7 @@ return pos, chr(hex_int) -def parse_literal_str(src: str, pos: Pos) -> Tuple[Pos, str]: +def parse_literal_str(src: str, pos: Pos) -> tuple[Pos, str]: pos += 1 # Skip starting apostrophe start_pos = pos pos = skip_until( @@ -526,7 +510,7 @@ return pos + 1, src[start_pos:pos] # Skip ending apostrophe -def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> Tuple[Pos, str]: +def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> tuple[Pos, str]: pos += 3 if src.startswith("\n", pos): pos += 1 @@ -557,7 +541,7 @@ return pos, result + (delim * 2) -def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> Tuple[Pos, str]: +def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]: if multiline: error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS parse_escapes = parse_basic_str_escape_multiline @@ -570,7 +554,7 @@ try: char = src[pos] except IndexError: - raise suffixed_err(src, pos, "Unterminated string") + raise suffixed_err(src, pos, "Unterminated string") from None if char == '"': if not multiline: return pos + 1, result + src[start_pos:pos] @@ -585,18 +569,20 @@ start_pos = pos continue if char in error_on: - raise suffixed_err(src, pos, f'Illegal character "{char!r}"') + raise suffixed_err(src, pos, f"Illegal character {char!r}") pos += 1 def parse_value( # noqa: C901 src: str, pos: Pos, parse_float: ParseFloat -) -> Tuple[Pos, Any]: +) -> tuple[Pos, Any]: try: - char: Optional[str] = src[pos] + char: str | None = src[pos] except IndexError: char = None + # IMPORTANT: order conditions based on speed of checking and likelihood + # Basic strings if char == '"': if src.startswith('"""', pos): @@ -617,13 +603,21 @@ if src.startswith("false", pos): return pos + 5, False + # Arrays + if char == "[": + return parse_array(src, pos, parse_float) + + # Inline tables + if char == "{": + return parse_inline_table(src, pos, parse_float) + # Dates and times datetime_match = RE_DATETIME.match(src, pos) if datetime_match: try: datetime_obj = match_to_datetime(datetime_match) - except ValueError: - raise suffixed_err(src, pos, "Invalid date or datetime") + except ValueError as e: + raise suffixed_err(src, pos, "Invalid date or datetime") from e return datetime_match.end(), datetime_obj localtime_match = RE_LOCALTIME.match(src, pos) if localtime_match: @@ -636,14 +630,6 @@ if number_match: return number_match.end(), match_to_number(number_match, parse_float) - # Arrays - if char == "[": - return parse_array(src, pos, parse_float) - - # Inline tables - if char == "{": - return parse_inline_table(src, pos, parse_float) - # Special floats first_three = src[pos : pos + 3] if first_three in {"inf", "nan"}: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tomli/_re.py new/tomli-2.0.0/tomli/_re.py --- old/tomli-1.2.1/tomli/_re.py 2021-08-05 22:07:21.000000000 +0200 +++ new/tomli-2.0.0/tomli/_re.py 2021-12-13 22:09:35.000000000 +0100 @@ -1,10 +1,11 @@ +from __future__ import annotations + from datetime import date, datetime, time, timedelta, timezone, tzinfo from functools import lru_cache import re -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import Any -if TYPE_CHECKING: - from tomli._parser import ParseFloat +from tomli._types import ParseFloat # E.g. # - 00:32:00.999999 @@ -35,16 +36,16 @@ fr""" ([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 (?: - [T ] + [Tt ] {_TIME_RE_STR} - (?:(Z)|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset + (?:([Zz])|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset )? """, flags=re.VERBOSE, ) -def match_to_datetime(match: "re.Match") -> Union[datetime, date]: +def match_to_datetime(match: re.Match) -> datetime | date: """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`. Raises ValueError if the match does not correspond to a valid date @@ -69,7 +70,7 @@ hour, minute, sec = int(hour_str), int(minute_str), int(sec_str) micros = int(micros_str.ljust(6, "0")) if micros_str else 0 if offset_sign_str: - tz: Optional[tzinfo] = cached_tz( + tz: tzinfo | None = cached_tz( offset_hour_str, offset_minute_str, offset_sign_str ) elif zulu_time: @@ -90,13 +91,13 @@ ) -def match_to_localtime(match: "re.Match") -> time: +def match_to_localtime(match: re.Match) -> time: hour_str, minute_str, sec_str, micros_str = match.groups() micros = int(micros_str.ljust(6, "0")) if micros_str else 0 return time(int(hour_str), int(minute_str), int(sec_str), micros) -def match_to_number(match: "re.Match", parse_float: "ParseFloat") -> Any: +def match_to_number(match: re.Match, parse_float: ParseFloat) -> Any: if match.group("floatpart"): return parse_float(match.group()) return int(match.group(), 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tomli-1.2.1/tomli/_types.py new/tomli-2.0.0/tomli/_types.py --- old/tomli-1.2.1/tomli/_types.py 1970-01-01 01:00:00.000000000 +0100 +++ new/tomli-2.0.0/tomli/_types.py 2021-12-13 22:09:35.000000000 +0100 @@ -0,0 +1,6 @@ +from typing import Any, Callable, Tuple + +# Type annotations +ParseFloat = Callable[[str], Any] +Key = Tuple[str, ...] +Pos = int