Initial upload: Doc2Man CLI tool with parsers, generators, and tests
This commit is contained in:
121
doc2man/config.py
Normal file
121
doc2man/config.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
"""Configuration file handling for Doc2Man."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def find_config_file(start_path: Path = Path.cwd()) -> Optional[Path]:
|
||||||
|
"""Find configuration file starting from the given path."""
|
||||||
|
search_paths = [
|
||||||
|
".doc2man.yaml",
|
||||||
|
".doc2man.yml",
|
||||||
|
"doc2man.yaml",
|
||||||
|
"doc2man.yml",
|
||||||
|
"pyproject.toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
current = start_path
|
||||||
|
while current != current.parent:
|
||||||
|
for config_name in search_paths:
|
||||||
|
config_path = current / config_name
|
||||||
|
if config_path.exists():
|
||||||
|
return config_path
|
||||||
|
current = current.parent
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(config_path: Optional[str] = None) -> Dict[str, Any]:
|
||||||
|
"""Load configuration from file."""
|
||||||
|
if config_path is None:
|
||||||
|
config_path = find_config_file()
|
||||||
|
if config_path is None:
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
config_path = Path(config_path)
|
||||||
|
|
||||||
|
config_path = config_path.resolve()
|
||||||
|
|
||||||
|
if not config_path.exists():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if config_path.name == "pyproject.toml":
|
||||||
|
return load_pyproject_config(config_path)
|
||||||
|
else:
|
||||||
|
return load_yaml_config(config_path)
|
||||||
|
|
||||||
|
|
||||||
|
def load_yaml_config(config_path: Path) -> Dict[str, Any]:
|
||||||
|
"""Load configuration from YAML file."""
|
||||||
|
try:
|
||||||
|
with open(config_path, "r", encoding="utf-8") as f:
|
||||||
|
config = yaml.safe_load(f) or {}
|
||||||
|
return config.get("doc2man", config)
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
raise ValueError(f"Error parsing configuration file {config_path}: {e}")
|
||||||
|
except IOError as e:
|
||||||
|
raise ValueError(f"Error reading configuration file {config_path}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def load_pyproject_config(config_path: Path) -> Dict[str, Any]:
|
||||||
|
"""Load configuration from pyproject.toml."""
|
||||||
|
try:
|
||||||
|
import tomli
|
||||||
|
|
||||||
|
with open(config_path, "rb") as f:
|
||||||
|
pyproject = tomli.load(f)
|
||||||
|
|
||||||
|
tool_section = pyproject.get("tool", {})
|
||||||
|
doc2man_section = tool_section.get("doc2man", {})
|
||||||
|
|
||||||
|
return doc2man_section
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(config_path)
|
||||||
|
|
||||||
|
if "doc2man" in config:
|
||||||
|
return dict(config["doc2man"])
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def save_config(config: Dict[str, Any], config_path: Path) -> None:
|
||||||
|
"""Save configuration to file."""
|
||||||
|
with open(config_path, "w", encoding="utf-8") as f:
|
||||||
|
yaml.dump({"doc2man": config}, f, default_flow_style=False, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def merge_configs(base_config: Dict[str, Any], override_config: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Merge two configurations, with override taking precedence."""
|
||||||
|
merged = base_config.copy()
|
||||||
|
for key, value in override_config.items():
|
||||||
|
if isinstance(value, dict) and key in merged and isinstance(merged[key], dict):
|
||||||
|
merged[key] = merge_configs(merged[key], value)
|
||||||
|
else:
|
||||||
|
merged[key] = value
|
||||||
|
return merged
|
||||||
|
|
||||||
|
|
||||||
|
def validate_config(config: Dict[str, Any]) -> List[str]:
|
||||||
|
"""Validate configuration and return list of warnings."""
|
||||||
|
warnings = []
|
||||||
|
|
||||||
|
valid_formats = ["man", "markdown", "html"]
|
||||||
|
if "format" in config and config["format"] not in valid_formats:
|
||||||
|
warnings.append(f"Invalid format '{config['format']}', must be one of {valid_formats}")
|
||||||
|
|
||||||
|
if "input" in config and not isinstance(config["input"], list):
|
||||||
|
warnings.append("'input' must be a list of paths")
|
||||||
|
|
||||||
|
if "output" in config and not isinstance(config["output"], str):
|
||||||
|
warnings.append("'output' must be a string path")
|
||||||
|
|
||||||
|
if "exclusions" in config and not isinstance(config["exclusions"], list):
|
||||||
|
warnings.append("'exclusions' must be a list of patterns")
|
||||||
|
|
||||||
|
return warnings
|
||||||
Reference in New Issue
Block a user