+ """Parse a pyproject toml file, pulling out relevant parts for Black.
+
+ If parsing fails, will raise a tomllib.TOMLDecodeError.
+ """
+ with open(path_config, "rb") as f:
+ pyproject_toml = tomllib.load(f)
+ config: Dict[str, Any] = pyproject_toml.get("tool", {}).get("black", {})
+ config = {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}
+
+ if "target_version" not in config:
+ inferred_target_version = infer_target_version(pyproject_toml)
+ if inferred_target_version is not None:
+ config["target_version"] = [v.name.lower() for v in inferred_target_version]
+
+ return config
+
+
+def infer_target_version(
+ pyproject_toml: Dict[str, Any]
+) -> Optional[List[TargetVersion]]:
+ """Infer Black's target version from the project metadata in pyproject.toml.
+
+ Supports the PyPA standard format (PEP 621):
+ https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#requires-python
+
+ If the target version cannot be inferred, returns None.
+ """
+ project_metadata = pyproject_toml.get("project", {})
+ requires_python = project_metadata.get("requires-python", None)
+ if requires_python is not None:
+ try:
+ return parse_req_python_version(requires_python)
+ except InvalidVersion:
+ pass
+ try:
+ return parse_req_python_specifier(requires_python)
+ except (InvalidSpecifier, InvalidVersion):
+ pass
+
+ return None
+
+
+def parse_req_python_version(requires_python: str) -> Optional[List[TargetVersion]]:
+ """Parse a version string (i.e. ``"3.7"``) to a list of TargetVersion.
+
+ If parsing fails, will raise a packaging.version.InvalidVersion error.
+ If the parsed version cannot be mapped to a valid TargetVersion, returns None.
+ """
+ version = Version(requires_python)
+ if version.release[0] != 3:
+ return None
+ try:
+ return [TargetVersion(version.release[1])]
+ except (IndexError, ValueError):
+ return None
+