Files
AstrBot/astrbot/core/utils/toml_parser.py
Weilong Liao 272242e407 chore: add release preparation workflow (#8891)
* chore: add release preparation workflow

* fix: address release workflow review feedback
2026-06-19 17:41:13 +08:00

185 lines
6.0 KiB
Python

"""Small TOML readers for bootstrapping paths without parser dependencies."""
from pathlib import Path
def _read_quoted_value(value: str, field_name: str) -> tuple[str, str]:
"""Read one quoted TOML string value and return its tail.
Args:
value: Raw value text that starts with a quoted string.
field_name: Field name used in error messages.
Returns:
A tuple containing the unquoted string and the remaining text.
Raises:
ValueError: The value is not a supported quoted string.
"""
value = value.strip()
if len(value) < 2 or value[0] not in ("'", '"'):
raise ValueError(f"Unsupported {field_name} value")
quote = value[0]
end_index = value.find(quote, 1)
if end_index == -1:
raise ValueError(f"Unterminated {field_name} string")
result = value[1:end_index]
if not result:
raise ValueError(f"Empty {field_name} value")
return result, value[end_index + 1 :].strip()
def _read_dependency_array(raw_value: str) -> list[str]:
"""Read a simple inline TOML string array.
Args:
raw_value: Raw dependency array text, including the surrounding brackets.
Returns:
Parsed dependency strings.
Raises:
ValueError: The array is missing brackets or contains unsupported entries.
"""
value = raw_value.strip()
if not value.startswith("["):
raise ValueError("Unsupported project.dependencies value")
dependencies = []
value = value[1:].strip()
while value:
if value.startswith("]"):
tail = value[1:].strip()
if tail and not tail.startswith("#"):
raise ValueError("Unsupported content after project.dependencies")
return dependencies
dependency, tail = _read_quoted_value(value, "project.dependencies entry")
dependencies.append(dependency)
if tail.startswith(","):
value = tail[1:].strip()
continue
if tail.startswith("]"):
value = tail
continue
if tail:
raise ValueError("Unsupported content after project.dependencies entry")
raise ValueError("Unterminated project.dependencies array")
raise ValueError("Unterminated project.dependencies array")
def read_pyproject_project_version(pyproject_path: Path) -> str:
"""Read the project version from a pyproject.toml file.
Args:
pyproject_path: Path to the pyproject.toml file.
Returns:
The value of the project.version field.
Raises:
FileNotFoundError: The pyproject.toml file does not exist.
ValueError: The project.version field is missing or unsupported.
"""
in_project_section = False
for raw_line in pyproject_path.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#"):
continue
if line.startswith("[") and line.endswith("]"):
in_project_section = line == "[project]"
continue
if not in_project_section:
continue
key, separator, raw_value = line.partition("=")
if key.strip() != "version":
continue
if not separator:
raise ValueError("Missing value separator for project.version")
version, tail = _read_quoted_value(raw_value, "project.version")
if tail and not tail.startswith("#"):
raise ValueError("Unsupported content after project.version")
return version
raise ValueError("Missing project.version")
def read_pyproject_project_dependencies(pyproject_path: Path) -> list[str]:
"""Read project dependencies from a pyproject.toml file.
Args:
pyproject_path: Path to the pyproject.toml file.
Returns:
The values in the project.dependencies array.
Raises:
FileNotFoundError: The pyproject.toml file does not exist.
ValueError: The project.dependencies field is missing or unsupported.
"""
dependencies = []
in_project_section = False
in_dependencies_array = False
for raw_line in pyproject_path.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#"):
continue
if in_dependencies_array:
if line.startswith("]"):
tail = line[1:].strip()
if tail and not tail.startswith("#"):
raise ValueError("Unsupported content after project.dependencies")
return dependencies
dependency, tail = _read_quoted_value(
line,
"project.dependencies entry",
)
if tail.startswith(","):
tail = tail[1:].strip()
if tail.startswith("]"):
tail = tail[1:].strip()
dependencies.append(dependency)
if tail and not tail.startswith("#"):
raise ValueError("Unsupported content after project.dependencies")
return dependencies
if tail and not tail.startswith("#"):
raise ValueError("Unsupported content after project.dependencies entry")
dependencies.append(dependency)
continue
if line.startswith("[") and line.endswith("]"):
in_project_section = line == "[project]"
continue
if not in_project_section:
continue
key, separator, raw_value = line.partition("=")
if key.strip() != "dependencies":
continue
if not separator:
raise ValueError("Unsupported project.dependencies value")
raw_value = raw_value.strip()
if raw_value == "[" or raw_value.startswith("[ #"):
in_dependencies_array = True
continue
if raw_value.startswith("["):
return _read_dependency_array(raw_value)
raise ValueError("Unsupported project.dependencies value")
if in_dependencies_array:
raise ValueError("Unterminated project.dependencies array")
raise ValueError("Missing project.dependencies")