Skip to content

Commit

Permalink
support config type 'path'
Browse files Browse the repository at this point in the history
  • Loading branch information
ice-black-tea committed Mar 29, 2024
1 parent 6cfcae0 commit 11d22a4
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 78 deletions.
40 changes: 28 additions & 12 deletions src/linktools/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,16 @@
from .rich import prompt, confirm, choose

if TYPE_CHECKING:
from typing import Literal
from ._environ import BaseEnviron

T = TypeVar("T")
EnvironType = TypeVar("EnvironType", bound=BaseEnviron)
ConfigType = Literal["path"]


def _is_type(obj: Any) -> bool:
return isinstance(obj, type)


def _cast_bool(obj: Any) -> bool:
Expand All @@ -72,11 +78,17 @@ def _cast_str(obj: Any) -> str:
return str(obj)


_CONFIG_ENV = "ENV"
def _cast_path(obj: Any) -> str:
if isinstance(obj, str):
return os.path.abspath(os.path.expanduser(obj))
raise TypeError(f"{type(obj)} cannot be converted to path")


_CONFIG_TYPES: "Dict[Type[T], Callable[[Any], T]]" = {
_CONFIG_ENV = "ENV"
_CONFIG_TYPES: "Dict[Union[Type[T], ConfigType], Callable[[Any], T]]" = {
bool: _cast_bool,
str: _cast_str,
"path": _cast_path,
}


Expand All @@ -93,18 +105,20 @@ def optionxform(self, optionstr):
class ConfigProperty(abc.ABC):
__lock__ = threading.RLock()

def __init__(self, type: Type = None, cached: Union[bool, str] = False):
def __init__(self, type: "Union[Type[T], ConfigType]" = None, cached: Union[bool, str] = False):
self._data: Union[str, object] = __missing__
self._type = type
self._cached = cached

def load(self, config: "Config", key: str, type: Type = None) -> Any:
def load(self, config: "Config", key: str, type: "Union[Type[T], ConfigType]" = None) -> Any:
if self._data is not __missing__:
return self._data

with self.__lock__:
if self._data is not __missing__:
return self._data
type = type or self._type

if self._cached:
# load cache from config file
config_parser = ConfigParser()
Expand All @@ -121,7 +135,7 @@ def load(self, config: "Config", key: str, type: Type = None) -> Any:
result = self._load(config, key, config_cache)
if isinstance(result, ConfigProperty):
result = result.load(config, key, type=type)
elif type and not isinstance(result, type):
elif not _is_type(type) or not isinstance(result, type):
result = config.cast(result, type)

# update cache to config file
Expand All @@ -134,13 +148,15 @@ def load(self, config: "Config", key: str, type: Type = None) -> Any:
config_parser.write(fd)

self._data = result

else:
result = self._load(config, key, __missing__)
if isinstance(result, ConfigProperty):
result = result.load(config, key, type=type)
elif type and not isinstance(result, type):
elif not _is_type(type) or not isinstance(result, type):
result = config.cast(result, type)
self._data = result

return self._data

@abc.abstractmethod
Expand Down Expand Up @@ -258,11 +274,11 @@ def load_from_env(self):
except Exception as e:
self._environ.logger.warning(f"Load config from {self.path} failed: {e}")

def cast(self, obj: Optional[str], type: "Type[T]", default: Any = __missing__) -> "T":
def cast(self, obj: Any, type: "Union[Type[T], ConfigType]", default: Any = __missing__) -> "T":
"""
类型转换
"""
if type is not None and type is not __missing__:
if type not in (None, __missing__):
cast = _CONFIG_TYPES.get(type, type)
try:
return cast(obj)
Expand All @@ -272,7 +288,7 @@ def cast(self, obj: Optional[str], type: "Type[T]", default: Any = __missing__)
raise e
return obj

def get(self, key: str, type: "Type[T]" = None, default: Any = __missing__) -> "T":
def get(self, key: str, type: "Union[Type[T], ConfigType]" = None, default: Any = __missing__) -> "T":
"""
获取指定配置,优先会从环境变量中获取
"""
Expand Down Expand Up @@ -394,7 +410,7 @@ def __init__(
prompt: str = None,
password: bool = False,
choices: Optional[List[str]] = None,
type: Type[Union[str, int, float]] = str,
type: "Union[Type[Union[str, int, float]], ConfigType]" = str,
default: Any = __missing__,
cached: Union[bool, str] = False,
always_ask: bool = False,
Expand Down Expand Up @@ -437,7 +453,7 @@ def _load(self, config: "Config", key: str, cache: Any):

return prompt(
self.prompt or f"Please input {key}",
type=self.type,
type=self.type if not isinstance(self.type, str) else str,
password=self.password,
choices=self.choices,
default=default,
Expand Down Expand Up @@ -489,7 +505,7 @@ class Alias(ConfigProperty):
def __init__(
self,
*keys: str,
type: Type = str,
type: "Union[Type[T], ConfigType]" = str,
default: Any = __missing__,
cached: Union[bool, str] = False
):
Expand Down
14 changes: 7 additions & 7 deletions src/linktools/_environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
/_==__==========__==_ooo__ooo=_/' /___________,"
"""
import abc
import getpass
import json
import logging
import os
Expand Down Expand Up @@ -284,13 +283,14 @@ def _create_tools(self) -> "Tools":
index = 0
dir_names = os.environ["PATH"].split(os.pathsep)
for tool in tools:
if not tool.executable:
continue
# dirname(executable[0]) -> environ["PATH"]
if tool.executable:
dir_name = tool.dirname
if dir_name and dir_name not in dir_names:
# insert to head
dir_names.insert(index, tool.dirname)
index += 1
dir_name = tool.dirname
if dir_name and dir_name not in dir_names:
# insert to head
dir_names.insert(index, tool.dirname)
index += 1
# add all paths to environment variables
os.environ["PATH"] = os.pathsep.join(dir_names)

Expand Down
14 changes: 3 additions & 11 deletions src/linktools/assets/containers/100-nginx/compose.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
services:
nginx:
container_name: nginx
build:
context: "{{ container.get_path() }}"
dockerfile: "{{ container.get_docker_file_path() }}"
restart: unless-stopped
volumes:
- "{{ container.get_app_path() }}/conf.d:/etc/nginx/conf.d"
- '{{ container.get_app_path("conf.d") }}:/etc/nginx/conf.d'
ports:
- "{{ HTTP_PORT }}:{{ HTTP_PORT }}"
- "{{ HTTPS_PORT }}:{{ HTTPS_PORT }}"
- '{{ HTTP_PORT }}:{{ HTTP_PORT }}'
- '{{ HTTPS_PORT }}:{{ HTTPS_PORT }}'
networks:
- nginx
logging:
options:
max-size: 1m

networks:
nginx:
Expand Down
15 changes: 5 additions & 10 deletions src/linktools/assets/containers/110-portainer/compose.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
services:
portainer:
container_name: portainer
image: portainer/portainer-ce
restart: unless-stopped
{% if int(PORTAINER_EXPOSE_PORT, 0) > 0 %}
# {% if int(PORTAINER_EXPOSE_PORT, 0) > 0 %}
ports:
- "{{ PORTAINER_EXPOSE_PORT }}:9000"
{% endif %}
- '{{ PORTAINER_EXPOSE_PORT }}:9000'
# {% endif %}
volumes:
- "{{ manager.container_host }}:/var/run/docker.sock"
- "{{ container.get_app_path() }}/data:/data"
- '{{ manager.container_host }}:/var/run/docker.sock'
- '{{ container.get_app_path("data") }}:/data'
networks:
- nginx
logging:
options:
max-size: 1m

networks:
nginx:
Expand Down
30 changes: 14 additions & 16 deletions src/linktools/assets/containers/120-flare/compose.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
services:
flare:
container_name: flare
image: soulteary/flare
user: "{{ DOCKER_PUID }}:{{ DOCKER_PGID }}"
restart: unless-stopped
{% if int(FLARE_EXPOSE_PORT, 0) > 0 %}
user: '{{ DOCKER_PUID }}:{{ DOCKER_PGID }}'
# {% if int(FLARE_EXPOSE_PORT, 0) > 0 %}
ports:
- "{{ FLARE_EXPOSE_PORT }}:5005"
{% endif %}
{% if bool(FLARE_DISABLE_LOGIN) %}
- '{{ FLARE_EXPOSE_PORT }}:5005'
# {% endif %}
# {% if bool(FLARE_DISABLE_LOGIN) %}
command: flare --disable_login=1 --visibility=private
environment:
- FLARE_DISABLE_LOGIN=1
- FLARE_GUIDE=1
{% else %}
- 'FLARE_DISABLE_LOGIN=1'
- 'FLARE_GUIDE=1'
# {% else %}
command: flare --disable_login=0 --visibility=private
environment:
- FLARE_DISABLE_LOGIN=0
- FLARE_USER={{ FLARE_USER }}
- FLARE_PASS={{ FLARE_PASSWORD }}
- FLARE_GUIDE=1
{% endif %}
- 'FLARE_DISABLE_LOGIN=0'
- 'FLARE_USER={{ FLARE_USER }}'
- 'FLARE_PASS={{ FLARE_PASSWORD }}'
- 'FLARE_GUIDE=1'
# {% endif %}
volumes:
- "{{ container.get_app_path() }}/app:/app"
- '{{ container.get_app_path("app") }}:/app'
networks:
- nginx

Expand Down
3 changes: 1 addition & 2 deletions src/linktools/assets/containers/120-flare/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ def exposes(self) -> [ExposeLink]:
return [
self.expose_other("在线工具集合", "tools", "", "https://tool.lu/"),
self.expose_other("在线正则表达式", "regex", "", "https://regex101.com/"),
self.expose_other("正则表达式手册", "regex", "",
"https://tool.oschina.net/uploads/apidocs/jquery/regexp.html"),
self.expose_other("正则表达式手册", "regex", "", "https://tool.oschina.net/uploads/apidocs/jquery/regexp.html"),
self.expose_other("在线json解析", "codeJson", "", "https://www.json.cn/"),
self.expose_other("DNS查询", "dns", "", "https://tool.chinaz.com/dns/"),
self.expose_other("图标下载", "progressDownload", "", "https://materialdesignicons.com/"),
Expand Down
38 changes: 28 additions & 10 deletions src/linktools/container/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,29 @@ def exposes(self) -> List[ExposeLink]:
def docker_compose(self) -> Optional[Dict[str, Any]]:
for name in self.manager.docker_compose_names:
path = self.get_path(name)
if os.path.exists(path):
data = self.render_template(path)
data = yaml.safe_load(data)
if "services" in data and isinstance(data["services"], dict):
for name, service in data["services"].items():
if isinstance(service, dict):
service.setdefault("container_name", name)
if not os.path.exists(path):
continue
data = self.render_template(path)
data = yaml.safe_load(data)
if "services" in data and isinstance(data["services"], dict):
for name, service in data["services"].items():
if not isinstance(service, dict):
continue
service.setdefault("container_name", name)
service.setdefault("restart", "unless-stopped")
service.setdefault("logging", {
"driver": "json-file",
"options": {
"max-size": "10m",
}
})
if "image" not in service and "build" not in service:
path = self.get_docker_file_path()
if path:
service["build"] = {
"context": self.get_path(),
"dockerfile": path
}
return data
return None

Expand Down Expand Up @@ -399,14 +415,16 @@ def render_template(self, source: str, destination: str = None, **kwargs: Any):

context.update(kwargs)
context.setdefault("DEBUG", self.manager.debug)
context.setdefault("manager", self.manager)
context.setdefault("config", self.manager.config)
context.setdefault("container", self)

context.setdefault("bool", lambda obj, default=False: self.manager.config.cast(obj, type=bool, default=default))
context.setdefault("str", lambda obj, default="": self.manager.config.cast(obj, type=str, default=default))
context.setdefault("int", lambda obj, default=0: self.manager.config.cast(obj, type=int, default=default))
context.setdefault("float", lambda obj, default=0.0: self.manager.config.cast(obj, type=float, default=default))

context.setdefault("manager", self.manager)
context.setdefault("config", self.manager.config)
context.setdefault("container", self)

template = Template(utils.read_file(source, text=True))
result = template.render(context)
if destination:
Expand Down
Loading

0 comments on commit 11d22a4

Please sign in to comment.