vyasa 0.3.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vyasa/config.py ADDED
@@ -0,0 +1,224 @@
1
+ """Configuration management for Vyasa.
2
+
3
+ Supports loading configuration from:
4
+ 1. .vyasa file (TOML format) in the current directory or blog root
5
+ 2. Environment variables (as fallback)
6
+ 3. Default values
7
+
8
+ Priority: .vyasa file > environment variables > defaults
9
+ """
10
+
11
+ import os
12
+ import tomllib
13
+ from pathlib import Path
14
+ from typing import Optional
15
+
16
+
17
+ class VyasaConfig:
18
+
19
+ """Configuration handler for Vyasa."""
20
+
21
+ def __init__(self, config_path: Optional[Path] = None):
22
+ """Initialize configuration.
23
+
24
+ Args:
25
+ config_path: Optional path to .vyasa file. If not provided, will search
26
+ in current directory and VYASA_ROOT.
27
+ """
28
+ self._config = {}
29
+ self._load_config(config_path)
30
+
31
+ def _load_config(self, config_path: Optional[Path] = None):
32
+ """Load configuration from .vyasa file if it exists."""
33
+ # Try to find .vyasa file
34
+ config_file = None
35
+
36
+ if config_path and config_path.exists():
37
+ config_file = config_path
38
+ else:
39
+ # Search in VYASA_ROOT first (if set)
40
+ root = os.getenv('VYASA_ROOT')
41
+ if root:
42
+ root_config = Path(root) / '.vyasa'
43
+ if root_config.exists():
44
+ config_file = root_config
45
+
46
+ # Then search in current directory
47
+ if not config_file:
48
+ cwd_config = Path.cwd() / '.vyasa'
49
+ if cwd_config.exists():
50
+ config_file = cwd_config
51
+
52
+ # Load the config file if found
53
+ if config_file:
54
+ try:
55
+ with open(config_file, 'rb') as f:
56
+ self._config = tomllib.load(f)
57
+ print(f"✓ Loaded configuration from: {config_file}")
58
+ except Exception as e:
59
+ print(f"Warning: Failed to load {config_file}: {e}")
60
+ self._config = {}
61
+
62
+ def get(self, key: str, env_var: str, default: any = None) -> any:
63
+ """Get configuration value with priority: config file > env var > default.
64
+
65
+ Args:
66
+ key: Key in the .vyasa config file
67
+ env_var: Environment variable name
68
+ default: Default value if not found
69
+
70
+ Returns:
71
+ Configuration value
72
+ """
73
+ # First check config file
74
+ if key in self._config:
75
+ return self._config[key]
76
+
77
+ # Then check environment variable
78
+ env_value = os.getenv(env_var)
79
+ if env_value is not None:
80
+ return env_value
81
+
82
+ # Finally return default
83
+ return default
84
+
85
+ def get_root_folder(self) -> Path:
86
+ """Get the blog root folder path."""
87
+ root = self.get('root', 'VYASA_ROOT', '.')
88
+ return Path(root).resolve()
89
+
90
+ def get_blog_title(self) -> str:
91
+ """Get the blog title."""
92
+ from .core import slug_to_title # Import here to avoid circular dependency
93
+
94
+ title = self.get('title', 'VYASA_TITLE', None)
95
+ if title:
96
+ return title.upper()
97
+
98
+ # Default to root folder name
99
+ return slug_to_title(self.get_root_folder().name).upper()
100
+
101
+ def get_host(self) -> str:
102
+ """Get the server host."""
103
+ return self.get('host', 'VYASA_HOST', '127.0.0.1')
104
+
105
+ def get_port(self) -> int:
106
+ """Get the server port."""
107
+ port = self.get('port', 'VYASA_PORT', 5001)
108
+ return int(port)
109
+
110
+ def get_auth(self):
111
+ """Get authentication credentials from config, env, or default (None)."""
112
+ user = self.get('username', 'VYASA_USER', None)
113
+ pwd = self.get('password', 'VYASA_PASSWORD', None)
114
+ return user, pwd
115
+
116
+ def _coerce_list(self, value):
117
+ if value is None:
118
+ return []
119
+ if isinstance(value, list):
120
+ return [v for v in value if v is not None and str(v).strip()]
121
+ if isinstance(value, str):
122
+ parts = [v.strip() for v in value.split(",")]
123
+ return [v for v in parts if v]
124
+ return [value]
125
+
126
+ def get_auth_required(self):
127
+ """Return auth_required if set, otherwise None."""
128
+ value = self.get('auth_required', 'VYASA_AUTH_REQUIRED', None)
129
+ if value is None:
130
+ return None
131
+ if isinstance(value, str):
132
+ return value.lower() in ('true', '1', 'yes', 'on')
133
+ return bool(value)
134
+
135
+ def get_google_oauth(self):
136
+ """Get Google OAuth settings (optional)."""
137
+ cfg = self._config.get('google_oauth', {})
138
+ if not isinstance(cfg, dict):
139
+ cfg = {}
140
+
141
+ client_id = cfg.get('client_id') or self.get('google_client_id', 'VYASA_GOOGLE_CLIENT_ID', None)
142
+ client_secret = cfg.get('client_secret') or self.get('google_client_secret', 'VYASA_GOOGLE_CLIENT_SECRET', None)
143
+ allowed_domains = cfg.get('allowed_domains')
144
+ if allowed_domains is None:
145
+ allowed_domains = self.get('google_allowed_domains', 'VYASA_GOOGLE_ALLOWED_DOMAINS', [])
146
+ allowed_emails = cfg.get('allowed_emails')
147
+ if allowed_emails is None:
148
+ allowed_emails = self.get('google_allowed_emails', 'VYASA_GOOGLE_ALLOWED_EMAILS', [])
149
+ default_roles = cfg.get('default_roles')
150
+ if default_roles is None:
151
+ default_roles = self.get('google_default_roles', 'VYASA_GOOGLE_DEFAULT_ROLES', [])
152
+
153
+ return {
154
+ "client_id": client_id,
155
+ "client_secret": client_secret,
156
+ "allowed_domains": self._coerce_list(allowed_domains),
157
+ "allowed_emails": self._coerce_list(allowed_emails),
158
+ "default_roles": self._coerce_list(default_roles),
159
+ }
160
+
161
+ def get_rbac(self):
162
+ """Get RBAC settings (optional)."""
163
+ cfg = self._config.get('rbac', {})
164
+ if not isinstance(cfg, dict):
165
+ cfg = {}
166
+
167
+ enabled = cfg.get('enabled', None)
168
+ enabled_env = os.getenv('VYASA_RBAC_ENABLED')
169
+ if enabled_env is not None:
170
+ enabled = enabled_env.lower() in ('true', '1', 'yes', 'on')
171
+ if enabled is None:
172
+ enabled = bool(cfg.get('rules') or cfg.get('user_roles'))
173
+
174
+ default_roles = cfg.get('default_roles', None)
175
+ if default_roles is None:
176
+ default_roles = self.get('rbac_default_roles', 'VYASA_RBAC_DEFAULT_ROLES', [])
177
+
178
+ user_roles = cfg.get('user_roles', {})
179
+ if not isinstance(user_roles, dict):
180
+ user_roles = {}
181
+
182
+ role_users = cfg.get('role_users', {})
183
+ if not isinstance(role_users, dict):
184
+ role_users = {}
185
+
186
+ rules = cfg.get('rules', [])
187
+ if not isinstance(rules, list):
188
+ rules = []
189
+
190
+ return {
191
+ "enabled": bool(enabled),
192
+ "default_roles": self._coerce_list(default_roles),
193
+ "user_roles": user_roles,
194
+ "role_users": role_users,
195
+ "rules": rules,
196
+ }
197
+
198
+ def get_sidebars_open(self) -> bool:
199
+ """Get whether sidebars should be open by default."""
200
+ value = self.get('sidebars_open', 'VYASA_SIDEBARS_OPEN', True)
201
+ # Handle string values from environment variables
202
+ if isinstance(value, str):
203
+ return value.lower() in ('true', '1', 'yes', 'on')
204
+ return bool(value)
205
+
206
+
207
+
208
+ # Global config instance
209
+ _config: Optional[VyasaConfig] = None
210
+
211
+
212
+ def get_config() -> VyasaConfig:
213
+ """Get or create the global configuration instance."""
214
+ global _config
215
+ if _config is None:
216
+ _config = VyasaConfig()
217
+ return _config
218
+
219
+
220
+ def reload_config(config_path: Optional[Path] = None):
221
+ """Reload configuration, optionally from a specific path."""
222
+ global _config
223
+ _config = VyasaConfig(config_path)
224
+ return _config