109 lines
3.7 KiB
Python
109 lines
3.7 KiB
Python
"""Source for pyproject.toml files or more generally toml files."""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from pytoolconfig.sources.source import Source
|
|
from pytoolconfig.types import Key
|
|
from pytoolconfig.universal_config import UniversalConfig
|
|
from pytoolconfig.utils import (
|
|
_dict_to_dataclass,
|
|
find_config_file,
|
|
max_py_version,
|
|
min_py_version,
|
|
parse_dependencies,
|
|
)
|
|
|
|
if sys.version_info < (3, 11, 0):
|
|
import tomli as tomllib
|
|
else:
|
|
import tomllib
|
|
|
|
|
|
class PyProject(Source):
|
|
|
|
"""Source for pyproject.toml and pytool.toml files.
|
|
|
|
Can be extended to other toml files.
|
|
"""
|
|
|
|
tool: str
|
|
toml_dict: dict | None = None
|
|
name: str = "pyproject.toml"
|
|
description: str = """
|
|
PEP 518 defines pyproject.toml as a configuration file to store build system
|
|
requirements for Python projects. With the help of tools like Poetry or Flit
|
|
it can fully replace the need for setup.py and setup.cfg files.
|
|
""" # taken from black.
|
|
file: Path | None
|
|
|
|
def __init__(
|
|
self,
|
|
working_directory: Path,
|
|
tool: str,
|
|
bases: list[str] | None = None,
|
|
recursive: bool = True,
|
|
) -> None:
|
|
"""Initialize the TOML configuration.
|
|
|
|
:param working_directory: Working Directory
|
|
:param tool: name of your tool. Will read configuration from [tool.yourtool]
|
|
:param bases: Base files/folders to look for (besides pyproject.toml)
|
|
:param recursive: search recursively up the directory tree for the file.
|
|
"""
|
|
if recursive:
|
|
self.file = find_config_file(working_directory, "pyproject.toml", bases)
|
|
else:
|
|
self.file = working_directory / "pyproject.toml"
|
|
self.tool = tool
|
|
|
|
def _read(self) -> bool:
|
|
if not self.file or not self.file.exists():
|
|
return False
|
|
self.toml_dict = tomllib.loads(self.file.read_text())
|
|
if self.toml_dict is None:
|
|
return False
|
|
if "tool" not in self.toml_dict:
|
|
return False
|
|
return self.tool in self.toml_dict["tool"]
|
|
|
|
def parse(self) -> dict[str, Key] | None:
|
|
"""Parse the TOML file."""
|
|
if not self._read():
|
|
return None
|
|
assert self.toml_dict
|
|
return self.toml_dict["tool"][self.tool]
|
|
|
|
def universalconfig(self) -> UniversalConfig:
|
|
"""Parse the file for the universal config object's fields.
|
|
|
|
Only implement the relevant fields such as minimum python version.
|
|
|
|
Pre: file was read but tool isn't necessarily in file.
|
|
"""
|
|
if not self.toml_dict:
|
|
return UniversalConfig()
|
|
config: UniversalConfig
|
|
config = (
|
|
_dict_to_dataclass(UniversalConfig, self.toml_dict["tool"]["pytoolconfig"])
|
|
if "pytoolconfig" in self.toml_dict.get("tool", {})
|
|
else UniversalConfig()
|
|
)
|
|
if "project" in self.toml_dict:
|
|
project = self.toml_dict["project"]
|
|
if "requires-python" in project:
|
|
raw_python_ver = project["requires-python"]
|
|
config.min_py_version = min_py_version(raw_python_ver)
|
|
config.max_py_version = max_py_version(raw_python_ver)
|
|
if "dependencies" in project:
|
|
dependencies = parse_dependencies(project["dependencies"])
|
|
config.dependencies = list(dependencies)
|
|
if "optional-dependencies" in project:
|
|
optional_deps = {}
|
|
for group, deps in project["optional-dependencies"].items():
|
|
optional_deps[group] = list(parse_dependencies(deps))
|
|
config.optional_dependencies = optional_deps
|
|
if "version" in project:
|
|
config.version = project["version"]
|
|
return config
|