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