1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
"""PyGitWeb project layout settings without importing pygitweb."""
from __future__ import annotations
import json
import os
from dataclasses import dataclass
from pathlib import Path
_DEFAULT_SETTINGS_PATH = Path.home() / ".pygitweb" / "settings.json"
_ENV_PREFIX = "PYGITWEB_"
@dataclass(frozen=True, slots=True)
class PygitwebLayout:
projectroot: Path
projects_list: Path
project_maxdepth: int
list_all: bool
export_ok: str
def load_pygitweb_layout() -> PygitwebLayout:
"""Load project directory settings from ~/.pygitweb/settings.json and PYGITWEB_* env vars."""
file_values = _load_settings_file(_settings_file_path())
layout = _layout_from_mapping(file_values)
return _apply_env_overrides(layout)
def _settings_file_path() -> Path:
explicit = os.environ.get(f"{_ENV_PREFIX}SETTINGS_CONFIG", "").strip()
if explicit:
return Path(explicit).expanduser()
return _DEFAULT_SETTINGS_PATH
def _load_settings_file(path: Path) -> dict[str, object]:
if not path.is_file():
return {}
try:
data = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return {}
return data if isinstance(data, dict) else {}
def _layout_from_mapping(values: dict[str, object]) -> PygitwebLayout:
projectroot = _path_value(values.get("PROJECTROOT"), Path.home())
projects_list_raw = values.get("PROJECTS_LIST")
projects_list = projectroot if projects_list_raw in (None, "") else projectroot
return PygitwebLayout(
projectroot=projectroot,
projects_list=projects_list if projects_list_raw is None else _path_value(projects_list_raw, projectroot),
project_maxdepth=_int_value(values.get("PROJECT_MAXDEPTH"), 1),
list_all=_bool_value(values.get("LIST_ALL"), True),
export_ok=_str_value(values.get("EXPORT_OK"), ""),
)
def _apply_env_overrides(layout: PygitwebLayout) -> PygitwebLayout:
projectroot = _env_path(f"{_ENV_PREFIX}PROJECTROOT", layout.projectroot)
projects_list = layout.projects_list
if os.environ.get(f"{_ENV_PREFIX}PROJECTS_LIST", "").strip():
projects_list = _env_path(f"{_ENV_PREFIX}PROJECTS_LIST", layout.projects_list)
elif not layout.projects_list:
projects_list = projectroot
return PygitwebLayout(
projectroot=projectroot,
projects_list=projects_list,
project_maxdepth=_env_int(f"{_ENV_PREFIX}PROJECT_MAXDEPTH", layout.project_maxdepth),
list_all=_env_bool(f"{_ENV_PREFIX}LIST_ALL", layout.list_all),
export_ok=_env_str(f"{_ENV_PREFIX}EXPORT_OK", layout.export_ok),
)
def _path_value(value: object, default: Path) -> Path:
if value is None or value == "":
return default
return Path(str(value)).expanduser()
def _str_value(value: object, default: str) -> str:
if value is None:
return default
return str(value)
def _int_value(value: object, default: int) -> int:
if value is None:
return default
if isinstance(value, bool):
return int(value)
if isinstance(value, int):
return value
if isinstance(value, str):
try:
return int(value.strip())
except ValueError:
return default
if isinstance(value, float):
return int(value)
return default
def _bool_value(value: object, default: bool) -> bool:
if value is None:
return default
if isinstance(value, bool):
return value
if isinstance(value, int):
return value != 0
if isinstance(value, str):
normalized = value.strip().lower()
if normalized in ("false", "0", "no", "off"):
return False
if normalized in ("true", "1", "yes", "on"):
return True
return default
def _env_str(name: str, default: str) -> str:
raw = os.environ.get(name)
if raw is None or raw.strip() == "":
return default
return raw
def _env_int(name: str, default: int) -> int:
raw = os.environ.get(name)
if raw is None or raw.strip() == "":
return default
try:
return int(raw.strip())
except ValueError:
return default
def _env_bool(name: str, default: bool) -> bool:
raw = os.environ.get(name)
if raw is None or raw.strip() == "":
return default
return _bool_value(raw, default)
def _env_path(name: str, default: Path) -> Path:
raw = os.environ.get(name)
if raw is None or raw.strip() == "":
return default
return Path(raw.strip()).expanduser()