Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when dotted keys define values outside current table #125

Merged
merged 6 commits into from Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,11 @@
- Removed
- Python 3.6 support
- Support for text file objects as `load` input. Use binary file objects instead.
- 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

Expand Down
3 changes: 3 additions & 0 deletions tests/data/extras/invalid/dotted-keys/extend-defined-aot.toml
@@ -0,0 +1,3 @@
[[tab.arr]]
[tab]
arr.val1=1
@@ -0,0 +1,4 @@
[a.b.c.d]
z = 9
[a]
b.c.d.k.t = 8
@@ -0,0 +1,4 @@
[a.b.c]
z = 9
[a]
b.c.t = 9
13 changes: 0 additions & 13 deletions tests/test_flags.py

This file was deleted.

37 changes: 21 additions & 16 deletions tomli/_parser.py
Expand Up @@ -95,6 +95,7 @@ def loads(s: str, *, parse_float: ParseFloat = float) -> dict[str, Any]: # noqa
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:
Expand Down Expand Up @@ -131,6 +132,15 @@ class Flags:

def __init__(self) -> None:
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
Expand All @@ -140,19 +150,6 @@ def unset_all(self, key: Key) -> None:
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]
Expand Down Expand Up @@ -320,12 +317,20 @@ def key_value_rule(
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:
Expand Down