Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 0.5.0-dev

- Move from `toml` to `tomli` for TOML parsing to allow using `tomllib` in standard library for Python > 3.11.

### Breaking changes

- Remove custom prefix or suffixes for special operators. Everything should start with "$" to reduce code and unnecessary complications.

## 0.4.0

- Add `$value` operator.
Expand Down
9 changes: 0 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,6 @@ ALLOWED_HOSTS = ["example.com"]

By default, special operations are denoted by an [`inline table`](https://toml.io/en/v1.0.0#inline-table), (aka a `dictionary`) with a key that starts with a `$`, e.g. `{ "$value" = "1" }`.

The prefix and suffix that denotes a special operation can be configured with `TOML_SETTINGS_SPECIAL_PREFIX` or `TOML_SETTINGS_SPECIAL_SUFFIX` in `[tool.django]`.

```toml
[tool.django]
TOML_SETTINGS_SPECIAL_PREFIX = "&"
TOML_SETTINGS_SPECIAL_SUFFIX = "*"
BASE_DIR = { "&path*" = "." }
```

### Path

Converts a string to a `Path` object by using a `$path` key. Handles relative paths based on the location of the parsed TOML file.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ classifiers = [
]
dependencies = [
"python-dateutil>=2.9.0.post0",
"toml>=0.10.2",
"tomli>=1.1.0; python_version < '3.11'",
"typeguard>=2",
]

Expand Down
12 changes: 9 additions & 3 deletions src/dj_toml_settings/toml_parser.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import logging
import os
import sys
from datetime import datetime
from pathlib import Path
from typing import Any

import toml
from dateutil import parser as dateparser
from typeguard import typechecked

if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib

from dj_toml_settings.value_parsers.dict_parsers import (
EnvParser,
InsertParser,
Expand Down Expand Up @@ -76,10 +81,11 @@ def get_data(self) -> dict:
data = {}

try:
data = toml.load(self.path)
with open(self.path, "rb") as f:
data = tomllib.load(f)
except FileNotFoundError:
logger.warning(f"Cannot find file at: {self.path}")
except toml.TomlDecodeError:
except tomllib.TOMLDecodeError:
logger.error(f"Cannot parse TOML at: {self.path}")

return data.get("tool", {}).get("django", {}) or {}
Expand Down
22 changes: 6 additions & 16 deletions src/dj_toml_settings/value_parsers/dict_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,16 @@ def __init__(self, data: dict, value: dict):
if not hasattr(self, "key"):
raise NotImplementedError("Missing key attribute")

self.key = self.add_prefix_and_suffix_to_key(self.key)
self.key = self.add_prefix_to_key(self.key)

def match(self) -> bool:
return self.key in self.value

@typechecked
def add_prefix_and_suffix_to_key(self, key: str) -> str:
"""Gets the key for the special operator. Defaults to "$" as the prefix, and "" as the suffix.

To change in the included TOML settings, set:
```
TOML_SETTINGS_SPECIAL_PREFIX = ""
TOML_SETTINGS_SPECIAL_SUFFIX = ""
```
"""

prefix = self.data.get("TOML_SETTINGS_SPECIAL_PREFIX", "$")
suffix = self.data.get("TOML_SETTINGS_SPECIAL_SUFFIX", "")
def add_prefix_to_key(self, key: str) -> str:
"""Gets the key for the special operator."""

return f"{prefix}{key}{suffix}"
return f"${key}"

def parse(self, *args, **kwargs):
raise NotImplementedError("parse() not implemented")
Expand All @@ -56,7 +46,7 @@ class EnvParser(DictParser):
key: str = "env"

def parse(self) -> Any:
default_special_key = self.add_prefix_and_suffix_to_key("default")
default_special_key = self.add_prefix_to_key("default")
default_value = self.value.get(default_special_key)

env_value = self.value[self.key]
Expand Down Expand Up @@ -114,7 +104,7 @@ def parse(self) -> Any:
raise InvalidActionError(f"`insert` cannot be used for value of type: {type(self.data[self.data_key])}")

# Insert the data
index_key = self.add_prefix_and_suffix_to_key("index")
index_key = self.add_prefix_to_key("index")
index = self.value.get(index_key, len(insert_data))

insert_data.insert(index, self.value[self.key])
Expand Down
92 changes: 17 additions & 75 deletions tests/test_toml_parser/test_parse_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_type_bool_true(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
DEBUG = { $value = "True", $type = "bool" }
DEBUG = { "$value" = "True", "$type" = "bool" }
""")

actual = Parser(path).parse_file()
Expand All @@ -45,7 +45,7 @@ def test_type_float(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
FLOAT = { $value = "1.5", $type = "float" }
FLOAT = { "$value" = "1.5", "$type" = "float" }
""")

actual = Parser(path).parse_file()
Expand Down Expand Up @@ -188,7 +188,7 @@ def test_env(tmp_path, monkeypatch):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { $env = "SOME_VAR" }
SOMETHING = { "$env" = "SOME_VAR" }
""")

actual = Parser(path).parse_file()
Expand All @@ -204,7 +204,7 @@ def test_env_in_nested_dict(tmp_path, monkeypatch):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { MORE = { $env = "SOME_VAR" } }
SOMETHING = { MORE = { "$env" = "SOME_VAR" } }
""")

actual = Parser(path).parse_file()
Expand Down Expand Up @@ -265,7 +265,7 @@ def test_env_missing(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { $env = "SOME_VAR" }
SOMETHING = { "$env" = "SOME_VAR" }
""")

actual = Parser(path).parse_file()
Expand All @@ -279,7 +279,7 @@ def test_env_default(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { $env = "SOME_VAR", $default = "default" }
SOMETHING = { "$env" = "SOME_VAR", "$default" = "default" }
""")

actual = Parser(path).parse_file()
Expand All @@ -293,7 +293,7 @@ def test_path(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { $path = "test-file" }
SOMETHING = { "$path" = "test-file" }
""")

actual = Parser(path).parse_file()
Expand All @@ -307,7 +307,7 @@ def test_relative_path(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { $path = "./test-file" }
SOMETHING = { "$path" = "./test-file" }
""")

actual = Parser(path).parse_file()
Expand All @@ -321,7 +321,7 @@ def test_parent_path(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { $path = "../test-file" }
SOMETHING = { "$path" = "../test-file" }
""")

actual = Parser(path).parse_file()
Expand All @@ -335,7 +335,7 @@ def test_parent_path_2(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { $path = "./../test-file" }
SOMETHING = { "$path" = "./../test-file" }
""")

actual = Parser(path).parse_file()
Expand All @@ -352,7 +352,7 @@ def test_insert(tmp_path):
SOMETHING = [1]

[tool.django.apps.something]
SOMETHING = { $insert = 2 }
SOMETHING = { "$insert" = 2 }
""")

actual = Parser(path).parse_file()
Expand All @@ -366,7 +366,7 @@ def test_insert_missing(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
SOMETHING = { $insert = 1 }
SOMETHING = { "$insert" = 1 }
""")

actual = Parser(path).parse_file()
Expand All @@ -383,7 +383,7 @@ def test_insert_invalid(tmp_path):
SOMETHING = "hello"

[tool.django.apps.something]
SOMETHING = { $insert = 1 }
SOMETHING = { "$insert" = 1 }
""")

with pytest.raises(InvalidActionError) as e:
Expand All @@ -406,7 +406,7 @@ def test_insert_index(tmp_path):
SOMETHING = [1]

[tool.django.apps.something]
SOMETHING = { $insert = 2, $index = 0 }
SOMETHING = { "$insert" = 2, "$index" = 0 }
""")

actual = Parser(path).parse_file()
Expand Down Expand Up @@ -665,65 +665,7 @@ def test_none(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
TEST = { $none = 1 }
""")

actual = Parser(path).parse_file()

assert expected == actual


def test_special_prefix(tmp_path):
expected = {
"TOML_SETTINGS_SPECIAL_PREFIX": "&",
"TEST": None,
}

path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
TOML_SETTINGS_SPECIAL_PREFIX = "&"
TEST = { &none = 1 }
""")

actual = Parser(path).parse_file()

assert expected == actual


def test_special_suffix(tmp_path):
expected = {
"TOML_SETTINGS_SPECIAL_PREFIX": "",
"TOML_SETTINGS_SPECIAL_SUFFIX": "*",
"TEST": None,
}

path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
TOML_SETTINGS_SPECIAL_PREFIX = ""
TOML_SETTINGS_SPECIAL_SUFFIX = "*"
TEST = { none* = 1 }
""")

actual = Parser(path).parse_file()

assert expected == actual


def test_special_prefix_and_suffix(tmp_path):
expected = {
"TOML_SETTINGS_SPECIAL_PREFIX": "&",
"TOML_SETTINGS_SPECIAL_SUFFIX": "*",
"TEST": None,
}

path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
TOML_SETTINGS_SPECIAL_PREFIX = "&"
TOML_SETTINGS_SPECIAL_SUFFIX = "*"
TEST = { &none* = 1 }
TEST = { "$none" = 1 }
""")

actual = Parser(path).parse_file()
Expand Down Expand Up @@ -785,7 +727,7 @@ def test_variable_start_path(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
BASE_DIR = { $path = "." }
BASE_DIR = { "$path" = "." }
STATIC_ROOT = "${BASE_DIR}/staticfiles"
""")

Expand All @@ -800,7 +742,7 @@ def test_variable_end_path(tmp_path):
path = tmp_path / "pyproject.toml"
path.write_text("""
[tool.django]
BASE_DIR = { $path = "/something" }
BASE_DIR = { "$path" = "/something" }
STATIC_ROOT = "/blob${BASE_DIR}"
""")

Expand Down
13 changes: 2 additions & 11 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading