aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2024-11-11 01:12:29 +0800
committerYuqian Yang <crupest@crupest.life>2025-01-20 15:06:20 +0800
commit12e1272508ba0b5909069319007d677c1c76e355 (patch)
tree969e16dea577b4cbc427791de60083a6c962c1c6
parentfc6cd0846eae8c7f7a50e18b654b1f14f7b7840f (diff)
downloadcrupest-12e1272508ba0b5909069319007d677c1c76e355.tar.gz
crupest-12e1272508ba0b5909069319007d677c1c76e355.tar.bz2
crupest-12e1272508ba0b5909069319007d677c1c76e355.zip
HALF WORK: 2024.1.20
-rw-r--r--tools/cru-py/cru/config.py14
-rw-r--r--tools/cru-py/cru/list.py10
-rw-r--r--tools/cru-py/cru/service/_app.py5
-rw-r--r--tools/cru-py/cru/service/_base.py4
-rw-r--r--tools/cru-py/cru/service/_config.py116
-rw-r--r--tools/cru-py/cru/service/_nginx.py74
-rw-r--r--tools/cru-py/cru/service/_template.py16
-rw-r--r--tools/cru-py/cru/template.py4
8 files changed, 154 insertions, 89 deletions
diff --git a/tools/cru-py/cru/config.py b/tools/cru-py/cru/config.py
index 9efcd55..0f6f0d0 100644
--- a/tools/cru-py/cru/config.py
+++ b/tools/cru-py/cru/config.py
@@ -113,7 +113,7 @@ class ConfigItem(Generic[_T]):
self.value,
self.default,
)
-
+
@property
def description_str(self) -> str:
return f"{self.name} ({self.value_type.name}): {self.description}"
@@ -123,9 +123,19 @@ class Configuration(CruUniqueKeyList[ConfigItem[Any], str]):
def __init__(self):
super().__init__(lambda c: c.name)
+ def get_set_items(self) -> list[ConfigItem[Any]]:
+ return [item for item in self if item.is_set]
+
+ def get_unset_items(self) -> list[ConfigItem[Any]]:
+ return [item for item in self if not item.is_set]
+
+ @property
+ def all_set(self) -> bool:
+ return len(self.get_unset_items()) == 0
+
@property
def all_not_set(self) -> bool:
- return self.cru_iter().all(lambda item: not item.is_set)
+ return len(self.get_set_items()) == 0
def add_text_config(
self,
diff --git a/tools/cru-py/cru/list.py b/tools/cru-py/cru/list.py
index e329ae2..9d210b7 100644
--- a/tools/cru-py/cru/list.py
+++ b/tools/cru-py/cru/list.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from collections.abc import Callable, Iterator
-from typing import Any, Generic, Iterable, TypeAlias, TypeVar
+from typing import Any, Generic, Iterable, TypeAlias, TypeVar, overload
from ._error import CruInternalError
from ._iter import CruIterator
@@ -78,6 +78,14 @@ class CruUniqueKeyList(Generic[_T, _K]):
if len(keys) != len(set(keys)):
raise CruInternalError("Duplicate keys!")
+ @overload
+ def get_or(
+ self, key: _K, fallback: CruNotFound = CruNotFound.VALUE
+ ) -> _T | CruNotFound: ...
+
+ @overload
+ def get_or(self, key: _K, fallback: _O) -> _T | _O: ...
+
def get_or(
self, key: _K, fallback: _O | CruNotFound = CruNotFound.VALUE
) -> _T | _O | CruNotFound:
diff --git a/tools/cru-py/cru/service/_app.py b/tools/cru-py/cru/service/_app.py
index d6f74dd..e72baec 100644
--- a/tools/cru-py/cru/service/_app.py
+++ b/tools/cru-py/cru/service/_app.py
@@ -7,17 +7,20 @@ from ._base import (
from ._config import ConfigManager
from ._data import DataManager
from ._template import TemplateManager
+from ._nginx import NginxManager
APP_ID = "crupest"
+
class App(AppBase):
def __init__(self):
- super().__init__(APP_ID,f"{APP_ID}-service")
+ super().__init__(APP_ID, f"{APP_ID}-service")
self.add_feature(PathCommandProvider())
self.add_feature(AppInitializer())
self.add_feature(DataManager())
self.add_feature(ConfigManager())
self.add_feature(TemplateManager())
+ self.add_feature(NginxManager())
self.add_feature(CommandDispatcher())
def run_command(self):
diff --git a/tools/cru-py/cru/service/_base.py b/tools/cru-py/cru/service/_base.py
index aecd7c9..90e1f85 100644
--- a/tools/cru-py/cru/service/_base.py
+++ b/tools/cru-py/cru/service/_base.py
@@ -219,7 +219,9 @@ class PathCommandProvider(AppCommandFeatureProvider):
subparsers = arg_parser.add_subparsers(
dest="path_command", required=True, metavar="PATH_COMMAND"
)
- _list_parser = subparsers.add_parser("list", help="list all paths.")
+ _list_parser = subparsers.add_parser(
+ "list", help="list special paths used by app"
+ )
def run_command(self, args: Namespace) -> None:
if args.path_command == "list":
diff --git a/tools/cru-py/cru/service/_config.py b/tools/cru-py/cru/service/_config.py
index c28b071..52fed34 100644
--- a/tools/cru-py/cru/service/_config.py
+++ b/tools/cru-py/cru/service/_config.py
@@ -1,5 +1,5 @@
from collections.abc import Iterable
-from typing import Any
+from typing import Any, Literal, overload
from cru import CruException
from cru.config import Configuration, ConfigItem
@@ -117,41 +117,6 @@ class AppConfigDuplicateEntryError(AppConfigFileEntryError):
return "Duplicate entries found in config file"
-class AppConfigEntryKeyError(AppConfigFileEntryError):
- def __init__(
- self,
- message: str,
- configuration: Configuration,
- undefined_entries: Iterable[SimpleLineConfigParser.Entry],
- unset_items: Iterable[ConfigItem],
- *args,
- **kwargs,
- ) -> None:
- super().__init__(message, configuration, undefined_entries, *args, **kwargs)
- self._unset_items = list(unset_items)
-
- @property
- def unset_items(self) -> list[ConfigItem]:
- return self._unset_items
-
- @property
- def friendly_message_head(self) -> str:
- return "Entry key not defined in app config"
-
- @property
- def unset_items_message(self) -> str:
- head = "App config items are not set in app config:\n"
- return head + "\n".join([item.name for item in self.unset_items])
-
- def get_user_message(self):
- m = []
- if len(self.error_entries) > 0:
- m.append(super().get_user_message())
- if len(self.unset_items) > 0:
- m.append(self.unset_items_message)
- return "\n".join(m)
-
-
class AppConfigEntryValueFormatError(AppConfigFileEntryError):
@property
def friendly_message_head(self) -> str:
@@ -162,11 +127,12 @@ class AppConfigItemNotSetError(AppConfigError):
def __init__(
self,
message: str,
+ configuration: Configuration,
items: list[ConfigItem],
*args,
**kwargs,
) -> None:
- super().__init__(message, *args, **kwargs)
+ super().__init__(message, configuration, *args, **kwargs)
self._items = items
@@ -258,8 +224,45 @@ class ConfigManager(AppCommandFeatureProvider):
def config_file_path(self) -> AppFeaturePath:
return self._config_file_path
- def get_config_str_dict(self) -> dict[str, str]:
+ @property
+ def all_set(self) -> bool:
+ return self.configuration.all_set
+
+ def get_item(self, name: str) -> ConfigItem[Any]:
+ if not name.startswith(self.config_name_prefix + "_"):
+ name = f"{self.config_name_prefix}_{name}"
+
+ item = self.configuration.get_or(name, None)
+ if item is None:
+ raise AppConfigError(f"Config item '{name}' not found.", self.configuration)
+ return item
+
+ @overload
+ def get_item_value_str(self, name: str) -> str: ...
+
+ @overload
+ def get_item_value_str(self, name: str, ensure_set: Literal[True]) -> str: ...
+
+ @overload
+ def get_item_value_str(self, name: str, ensure_set: bool = True) -> str | None: ...
+
+ def get_item_value_str(self, name: str, ensure_set: bool = True) -> str | None:
self.reload_config_file()
+ item = self.get_item(name)
+ if ensure_set and not item.is_set:
+ raise AppConfigItemNotSetError(
+ f"Config item '{name}' is not set.", self.configuration, [item]
+ )
+ return item.value_str
+
+ def get_str_dict(self, ensure_all_set: bool = True) -> dict[str, str]:
+ self.reload_config_file()
+ if ensure_all_set and not self.configuration.all_set:
+ raise AppConfigItemNotSetError(
+ "Some config items are not set.",
+ self.configuration,
+ self.configuration.get_unset_items(),
+ )
return self.configuration.to_str_dict()
def get_domain_item_name(self) -> str:
@@ -343,26 +346,6 @@ class ConfigManager(AppCommandFeatureProvider):
return entry_dict
- def _check_key(
- self, entry_dict: dict[str, SimpleLineConfigParser.Entry]
- ) -> dict[str, SimpleLineConfigParser.Entry]:
- undefined: list[SimpleLineConfigParser.Entry] = []
- for key, entry in entry_dict.items():
- if not self.configuration.has_key(key):
- undefined.append(entry)
- unset_items: list[ConfigItem] = []
- for item in self.configuration:
- if item.name not in entry_dict or entry_dict[item.name].value == "":
- unset_items.append(item)
- if len(undefined) > 0 or len(unset_items) > 0:
- raise AppConfigEntryKeyError(
- "Entry keys are not defined in app config.",
- self.configuration,
- undefined,
- unset_items,
- )
- return entry_dict
-
def _check_type(
self, entry_dict: dict[str, SimpleLineConfigParser.Entry]
) -> dict[str, Any]:
@@ -393,7 +376,6 @@ class ConfigManager(AppCommandFeatureProvider):
parsed = self._parse_config_file()
entry_groups = parsed.cru_iter().group_by(lambda e: e.key)
entry_dict = self._check_duplicate(entry_groups)
- entry_dict = self._check_key(entry_dict)
value_dict = self._check_type(entry_dict)
return value_dict
@@ -401,6 +383,8 @@ class ConfigManager(AppCommandFeatureProvider):
self.configuration.reset_all()
value_dict = self._read_config_file()
for key, value in value_dict.items():
+ if value is None:
+ continue
self.configuration.set_config_item(key, value)
def _print_app_config_info(self):
@@ -415,26 +399,22 @@ class ConfigManager(AppCommandFeatureProvider):
dest="config_command", required=True, metavar="CONFIG_COMMAND"
)
_init_parser = subparsers.add_parser(
- "init", help="Create an initial configuration file."
+ "init", help="create an initial config file"
)
_print_app_parser = subparsers.add_parser(
"print-app",
- help="Print application configuration information "
- "of the items defined in the application.",
- )
- _print_parser = subparsers.add_parser(
- "print", help="Print current configuration."
+ help="print information of the config items defined by app",
)
+ _print_parser = subparsers.add_parser("print", help="print current config")
_check_config_parser = subparsers.add_parser(
"check",
- help="Check the validity of the configuration file.",
+ help="check the validity of the config file",
)
_check_config_parser.add_argument(
"-f",
"--format-only",
action="store_true",
- help="Only check content format, not "
- "for application configuration requirements.",
+ help="only check content format, not app config item requirements.",
)
def run_command(self, args) -> None:
diff --git a/tools/cru-py/cru/service/_nginx.py b/tools/cru-py/cru/service/_nginx.py
index a291866..ad29d21 100644
--- a/tools/cru-py/cru/service/_nginx.py
+++ b/tools/cru-py/cru/service/_nginx.py
@@ -1,31 +1,89 @@
from argparse import Namespace
+import re
from ._base import AppCommandFeatureProvider
from ._config import ConfigManager
+from ._template import TemplateManager
class NginxManager(AppCommandFeatureProvider):
- def __init__(self):
+ def __init__(self) -> None:
super().__init__("nginx-manager")
+ self._domains_cache: list[str] | None = None
+ self._domain_config_value_cache: str | None = None
def setup(self) -> None:
pass
@property
- def _template_domain_variable(self) -> str:
+ def domains(self) -> list[str]:
+ if self._domains_cache is None:
+ self._domains_cache = self._get_domains()
+ return self._domains_cache
+
+ @property
+ def _domain_config_name(self) -> str:
return self.app.get_feature(ConfigManager).get_domain_item_name()
- def _create_domain_regex(self):
- raise NotImplementedError()
+ def _get_domain_config_value(self) -> str:
+ if self._domain_config_value_cache is None:
+ self._domain_config_value_cache = self.app.get_feature(
+ ConfigManager
+ ).get_item_value_str(self._domain_config_name)
+ return self._domain_config_value_cache
+
+ def _get_domains_from_text(self, text: str) -> set[str]:
+ domains: set[str] = set()
+ regex = re.compile(r"server_name\s+(\S+)\s*;")
+ domain_variable_str = f"${self._domain_config_name}"
+ brace_domain_variable_regex = re.compile(
+ r"\$\{\s*" + self._domain_config_name + r"\s*\}"
+ )
+ for match in regex.finditer(text):
+ domain_part = match.group(1)
+ if domain_variable_str in domain_part:
+ domains.add(
+ domain_part.replace(
+ domain_variable_str, self._get_domain_config_value()
+ )
+ )
+ continue
+ m = brace_domain_variable_regex.search(domain_part)
+ if m:
+ domains.add(
+ domain_part.replace(m.group(0), self._get_domain_config_value())
+ )
+ continue
+ domains.add(domain_part)
+ return domains
+
+ def _get_nginx_conf_template_text(self) -> str:
+ template_manager = self.app.get_feature(TemplateManager)
+ text = ""
+ for path, template in template_manager.template_tree.templates:
+ if path.as_posix().startswith("nginx/"):
+ text += template.raw_text
+ return text
def _get_domains(self) -> list[str]:
- raise NotImplementedError()
+ text = self._get_nginx_conf_template_text()
+ domains = list(self._get_domains_from_text(text))
+ domains.remove(self._get_domain_config_value())
+ return [self._get_domain_config_value(), *domains]
+
+ def _print_domains(self) -> None:
+ for domain in self.domains:
+ print(domain)
def get_command_info(self):
- raise NotImplementedError()
+ return "nginx", "Manage nginx related things."
def setup_arg_parser(self, arg_parser):
- raise NotImplementedError()
+ subparsers = arg_parser.add_subparsers(
+ dest="nginx_command", required=True, metavar="NGINX_COMMAND"
+ )
+ _list_parser = subparsers.add_parser("list", help="list domains")
def run_command(self, args: Namespace) -> None:
- raise NotImplementedError()
+ if args.nginx_command == "list":
+ self._print_domains()
diff --git a/tools/cru-py/cru/service/_template.py b/tools/cru-py/cru/service/_template.py
index 7d479df..9241a1f 100644
--- a/tools/cru-py/cru/service/_template.py
+++ b/tools/cru-py/cru/service/_template.py
@@ -41,14 +41,14 @@ class TemplateManager(AppCommandFeatureProvider):
)
return self._template_tree
- def print_file_lists(self) -> None:
+ def _print_file_lists(self) -> None:
for file in CruIterator(self.template_tree.templates).transform(lambda t: t[0]):
print(file.as_posix())
- def generate_files(self) -> None:
+ def _generate_files(self) -> None:
config_manager = self.app.get_feature(ConfigManager)
self.template_tree.generate_to(
- self.generated_dir.full_path_str, config_manager.get_config_str_dict()
+ self.generated_dir.full_path_str, config_manager.get_str_dict()
)
def get_command_info(self):
@@ -58,17 +58,17 @@ class TemplateManager(AppCommandFeatureProvider):
subparsers = arg_parser.add_subparsers(
dest="template_command", required=True, metavar="TEMPLATE_COMMAND"
)
- _list_parser = subparsers.add_parser("list", help="List templates.")
+ _list_parser = subparsers.add_parser("list", help="list templates")
_variables_parser = subparsers.add_parser(
- "variables", help="List variables for a specific template."
+ "variables", help="list variables used in all templates"
)
- _generate_parser = subparsers.add_parser("generate", help="Generate template.")
+ _generate_parser = subparsers.add_parser("generate", help="generate templates")
def run_command(self, args: Namespace) -> None:
if args.template_command == "list":
- self.print_file_lists()
+ self._print_file_lists()
elif args.template_command == "variables":
for var in self.template_tree.variables:
print(var)
elif args.template_command == "generate":
- self.generate_files()
+ self._generate_files()
diff --git a/tools/cru-py/cru/template.py b/tools/cru-py/cru/template.py
index eb8c8ab..2b0f1bc 100644
--- a/tools/cru-py/cru/template.py
+++ b/tools/cru-py/cru/template.py
@@ -29,6 +29,10 @@ class CruTemplate:
return self._prefix
@property
+ def raw_text(self) -> str:
+ return self._template.template
+
+ @property
def py_template(self) -> Template:
return self._template