Coverage for pymend\files.py: 0%
41 statements
« prev ^ index » next coverage.py v7.3.2, created at 2024-04-20 19:09 +0200
« prev ^ index » next coverage.py v7.3.2, created at 2024-04-20 19:09 +0200
1"""File handling for pymend."""
3import sys
4from collections.abc import Sequence
5from functools import lru_cache
6from pathlib import Path
7from typing import TYPE_CHECKING, Any, Optional
9if sys.version_info >= (3, 11):
10 try:
11 import tomllib
12 except ImportError:
13 # Help users on older alphas
14 if not TYPE_CHECKING:
15 import tomli as tomllib
16 else:
17 raise
18else:
19 import tomli as tomllib
22@lru_cache
23def find_project_root(srcs: Sequence[str]) -> tuple[Path, str]:
24 """Return a directory containing .git, .hg, or pyproject.toml.
26 That directory will be a common parent of all files and directories
27 passed in `srcs`.
29 If no directory in the tree contains a marker that would specify it's the
30 project root, the root of the file system is returned.
32 Returns a two-tuple with the first element as the project root path and
33 the second element as a string describing the method by which the
34 project root was discovered.
36 Parameters
37 ----------
38 srcs : Sequence[str]
39 Source files that will be considered by pymend.
41 Returns
42 -------
43 directory : Path
44 Projects root path
45 method : str
46 Method by which the root path was determined.
47 """
48 if not srcs:
49 srcs = [str(Path.cwd().resolve())]
51 path_srcs = [Path(Path.cwd(), src).resolve() for src in srcs]
53 # A list of lists of parents for each 'src'. 'src' is included as a
54 # "parent" of itself if it is a directory
55 src_parents = [
56 list(path.parents) + ([path] if path.is_dir() else []) for path in path_srcs
57 ]
59 intersection: set[Path] = set[Path].intersection(
60 *(set(parents) for parents in src_parents)
61 )
63 common_base = max(
64 intersection,
65 key=lambda path: path.parts,
66 )
68 # Directory will always be set in the loop.
69 # This is just for pylint.
70 directory = Path()
71 for directory in (common_base, *common_base.parents):
72 if (directory / ".git").exists():
73 return directory, ".git directory"
75 if (directory / ".hg").is_dir():
76 return directory, ".hg directory"
78 if (directory / "pyproject.toml").is_file():
79 return directory, "pyproject.toml"
81 return directory, "file system root"
84def find_pyproject_toml(path_search_start: tuple[str, ...]) -> Optional[str]:
85 """Find the absolute filepath to a pyproject.toml if it exists.
87 Parameters
88 ----------
89 path_search_start : tuple[str, ...]
90 Tuple of paths to consider in the search for pyproject.toml
92 Returns
93 -------
94 Optional[str]
95 Path to pypyproject.toml or None if it could not be found.
96 """
97 path_project_root, _ = find_project_root(path_search_start)
98 path_pyproject_toml = path_project_root / "pyproject.toml"
99 if path_pyproject_toml.is_file():
100 return str(path_pyproject_toml)
102 return None
105def parse_pyproject_toml(path_config: str) -> dict[str, Any]:
106 """Parse a pyproject toml file, pulling out relevant parts for pymend.
108 If parsing fails, will raise a tomllib.TOMLDecodeError.
110 Parameters
111 ----------
112 path_config : str
113 Path to the pyproject.toml file.
115 Returns
116 -------
117 dict[str, Any]
118 Configuration dictionary parsed from pyproject.toml
119 """
120 with Path(path_config).open("rb") as f:
121 pyproject_toml: dict[str, Any] = tomllib.load(f)
122 config: dict[str, Any] = pyproject_toml.get("tool", {}).get("pymend", {})
123 return {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}