diff --git a/.idea/compose_gen.iml b/.idea/compose_gen.iml
index ac26cc3..23f40df 100644
--- a/.idea/compose_gen.iml
+++ b/.idea/compose_gen.iml
@@ -5,7 +5,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 401fa09..ddf20c1 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,14 +3,11 @@
-
+
-
-
-
\ No newline at end of file
diff --git a/src/docker_compose/__init__.py b/src/docker_compose/__init__.py
index 87ee1a3..39a08ed 100644
--- a/src/docker_compose/__init__.py
+++ b/src/docker_compose/__init__.py
@@ -8,6 +8,8 @@ from docker_compose.util.yaml_util import to_yaml
def load_all() -> Iterator[Rendered]:
for path in CFG_ROOT.iterdir():
+ if path.stem.startswith("."):
+ continue
if path == TRAEFIK_PATH:
continue
yield from Rendered.from_path(path)
@@ -15,9 +17,7 @@ def load_all() -> Iterator[Rendered]:
def render_all() -> Iterator[str]:
for rendered in load_all():
- rendered.write()
- rendered.write_bind_vols()
- rendered.mk_bind_vols()
+ rendered()
yield from rendered.proxy_nets
diff --git a/src/docker_compose/cfg/__init__.py b/src/docker_compose/cfg/__init__.py
index 84d0e80..936ede4 100644
--- a/src/docker_compose/cfg/__init__.py
+++ b/src/docker_compose/cfg/__init__.py
@@ -2,6 +2,6 @@ from pathlib import Path
ROOT = Path("/nas")
DATA_ROOT = ROOT.joinpath("apps")
-CFG_ROOT = ROOT.joinpath("cfg/templates")
+CFG_ROOT = ROOT.joinpath("docker_templates")
TRAEFIK_PATH = CFG_ROOT.joinpath("traefik")
# TEMPLATE_DIR = CFG_ROOT.joinpath("templates")
diff --git a/src/docker_compose/cfg/cfg_paths.py b/src/docker_compose/cfg/cfg_paths.py
index f191c5e..ea6b72b 100644
--- a/src/docker_compose/cfg/cfg_paths.py
+++ b/src/docker_compose/cfg/cfg_paths.py
@@ -1,11 +1,11 @@
from collections.abc import Iterator
from dataclasses import dataclass
from itertools import chain
-from shutil import copyfile
from typing import Self, final
from docker_compose.cfg.compose_paths import ComposePaths
from docker_compose.cfg.dest_path import DestPaths
+from docker_compose.cfg.env import Env
from docker_compose.cfg.org import OrgData
from docker_compose.cfg.src_path import SrcPaths
@@ -26,16 +26,17 @@ class CfgData:
yield cls(
src_paths,
org,
- ComposePaths.from_iters(
- src_paths.service_dir.files,
- src_paths.vol_dir.files,
+ ComposePaths(
+ frozenset(src_paths.service_dir.files),
+ frozenset(src_paths.vol_dir.files),
),
dest,
)
def pre_render(self, data: str) -> str:
for func in chain(
- self.org_data.pre_render_funcs, self.dest_paths.pre_render_funcs
+ self.org_data.pre_render_funcs,
+ self.dest_paths.pre_render_funcs,
):
data = func(data)
return data
@@ -55,8 +56,11 @@ class CfgData:
data = func(data)
return data
- def mk_compose_env(self) -> None:
+ def mk_compose_env(self, force: bool = False) -> None:
src = self.src_paths.env_file
dest = self.dest_paths.env_file
- if src.exists() and not dest.exists():
- _ = copyfile(src, dest)
+ if not src.exists():
+ return
+ if dest.exists() and not force:
+ return
+ Env.copy(src, dest)
diff --git a/src/docker_compose/cfg/compose_paths.py b/src/docker_compose/cfg/compose_paths.py
index 1ef48da..832ad11 100644
--- a/src/docker_compose/cfg/compose_paths.py
+++ b/src/docker_compose/cfg/compose_paths.py
@@ -1,56 +1,45 @@
-from collections.abc import Callable, Iterable, Iterator, MutableMapping
+from collections.abc import Callable, Iterator, MutableMapping
from dataclasses import dataclass, field
from pathlib import Path
-from typing import Self, cast, final, override
+from typing import ClassVar, cast, final
import yaml
from docker_compose.cfg.org import OrgData
-from docker_compose.cfg.record import Record, RecordCls, RecordName
+from docker_compose.cfg.replace import ReplaceDynamic, RecordCls
from docker_compose.compose.services_yaml import ServiceYamlRead
from docker_compose.compose.volume_yaml import VolYaml
from docker_compose.util.Ts import T_YamlRW
from docker_compose.util.yaml_util import path_to_typed, read_yaml
+# _SERVICE = "service"
@final
@dataclass(frozen=True, slots=True)
-class ServiceValNew:
- val: Path
-
- @override
- def __str__(self) -> str:
- return str(RecordName(self.val.stem))
-
- @property
- def replace(self) -> Record[Self, str]:
- return Record(self, self.val.stem)
-
-
-@final
-@dataclass(frozen=True, slots=True)
-class ServiceVal(RecordCls[ServiceValNew]):
- old = RecordName("service")
-
- @property
- def stage_two(self):
- return self.new.replace
+class ServiceVal(RecordCls):
+ rep: ClassVar[str] = "service"
@final
@dataclass(frozen=True, slots=True)
class ServicePath:
- fqdn = Record[RecordName, str](
- RecordName("fqdn"),
- f"{OrgData.org_app.old!s}_{ServiceVal.old!s}",
- )
-
path: Path
- replace: ServiceVal = field(init=False)
+ fqdn: ReplaceDynamic = field(init=False)
+ replace_pre: ReplaceDynamic = field(init=False)
+ replace_post: ReplaceDynamic = field(init=False)
def __post_init__(self):
setter = super(ServicePath, self).__setattr__
- setter("replace", ServiceVal(ServiceValNew(self.path)))
+ pre, post = ServiceVal.two_stage(self.path.stem)
+ setter("replace_pre", pre)
+ setter("replace_post", post)
+ setter(
+ "fqdn",
+ ReplaceDynamic(
+ "fqdn",
+ f"{OrgData.org_app!s}_{pre!s}",
+ ),
+ )
@property
def as_dict(self) -> ServiceYamlRead:
@@ -67,11 +56,10 @@ class ServicePath:
@property
def pre_render_funcs(self) -> Iterator[Callable[[str], str]]:
yield self.fqdn
- yield self.replace
+ yield self.replace_pre
- @property
- def render_funcs(self) -> Iterator[Callable[[str], str]]:
- yield self.replace.stage_two
+ # for service in self.services:
+ # yield service.replace_pre
@final
@@ -94,19 +82,24 @@ class ComposePaths:
services: frozenset[ServicePath]
volumes: frozenset[VolumePath]
- @classmethod
- def from_iters(cls, services: Iterable[ServicePath], volumes: Iterable[VolumePath]):
- return cls(
- frozenset(services),
- frozenset(volumes),
- )
+ @property
+ def render_funcs(self) -> Iterator[Callable[[str], str]]:
+ for service in self.services:
+ yield service.replace_post
+
+ # @classmethod
+ # def from_iters(cls, services: Iterable[ServicePath], volumes: Iterable[VolumePath]):
+ # return cls(
+ # frozenset(services),
+ # frozenset(volumes),
+ # )
@property
def volumes_k_v(self) -> Iterator[tuple[str, VolYaml]]:
for path in self.volumes:
yield path.as_k_v
- @property
- def render_funcs(self) -> Iterator[Callable[[str], str]]:
- for path in self.services:
- yield from path.render_funcs
+ # @property
+ # def render_funcs(self) -> Iterator[Callable[[str], str]]:
+ # for path in self.services:
+ # yield from path.render_funcs
diff --git a/src/docker_compose/cfg/dest_path.py b/src/docker_compose/cfg/dest_path.py
index 54fd07b..622a632 100644
--- a/src/docker_compose/cfg/dest_path.py
+++ b/src/docker_compose/cfg/dest_path.py
@@ -5,8 +5,8 @@ from pathlib import Path
from typing import Self, final
from docker_compose.cfg import DATA_ROOT
-from docker_compose.cfg.org import AppVal, OrgData, OrgVal
-from docker_compose.cfg.record import Record, RecordName
+from docker_compose.cfg.org import App, Org, OrgData
+from docker_compose.cfg.replace import ReplaceDynamic
@final
@@ -43,10 +43,11 @@ class ComposeFileRendered:
@final
@dataclass(frozen=True, slots=True)
class DestPaths:
- data_root = Record[RecordName, Path](RecordName("data_root"), DATA_ROOT)
- data_path = Record[RecordName, str](
- RecordName("data"),
- sep.join((str(data_root.old), str(OrgVal.old), str(AppVal.old))),
+ # data_root = Record("data_root", str(DATA_ROOT))
+ data_root = ReplaceDynamic.auto_format("data", str(DATA_ROOT))
+ data_path = ReplaceDynamic(
+ data_root.src,
+ sep.join((data_root.src, Org.rep, App.rep)),
)
data_dir: Path
env_file: Path = field(init=False)
@@ -60,7 +61,7 @@ class DestPaths:
@classmethod
def from_org(cls, org: OrgData) -> Self:
- return cls.from_path(DATA_ROOT.joinpath(org.org.val, org.app.val))
+ return cls.from_path(DATA_ROOT.joinpath(str(org.org), str(org.app)))
@classmethod
def from_path(cls, path: Path) -> Self:
diff --git a/src/docker_compose/cfg/env.py b/src/docker_compose/cfg/env.py
new file mode 100644
index 0000000..27f2f92
--- /dev/null
+++ b/src/docker_compose/cfg/env.py
@@ -0,0 +1,53 @@
+import re
+import secrets
+from collections.abc import Iterator
+from dataclasses import dataclass
+from functools import partial
+from pathlib import Path
+from typing import Self, final
+
+from docker_compose.cfg.replace import ReplaceDynamic
+
+
+@final
+@dataclass
+class Env:
+ pswd = ReplaceDynamic.auto_format(
+ "pswd",
+ partial(secrets.token_urlsafe, 12),
+ )
+ data: dict[str, str]
+
+ @classmethod
+ def get_lines(cls, data: str) -> Iterator[tuple[str, str]]:
+ line_valid = re.compile(r"(^\w+)=(.+)\s*")
+ for line in data.splitlines():
+ res = line_valid.match(line)
+ if not res:
+ continue
+ yield res.group(1), res.group(2)
+
+ @classmethod
+ def from_path(cls, path: Path) -> Self:
+ with path.open(mode="rt") as f:
+ data = f.read()
+ return cls({k: v for k, v in cls.get_lines(data)})
+
+ @property
+ def with_pass(self)->Iterator[tuple[str,str]]:
+ for k,v in self.data.items():
+ if self.pswd.src not in v:
+ yield k,v
+ continue
+ yield k,self.pswd(v)
+
+
+ @property
+ def as_txt(self) -> str:
+ return '\n'.join(sorted(map('='.join, self.with_pass)))
+
+ @classmethod
+ def copy(cls, src: Path, dest: Path) -> None:
+ txt = cls.from_path(src).as_txt
+ with dest.open(mode="wt") as f:
+ _ = f.write(txt)
diff --git a/src/docker_compose/cfg/org.py b/src/docker_compose/cfg/org.py
index 6b7ce5c..95b5540 100644
--- a/src/docker_compose/cfg/org.py
+++ b/src/docker_compose/cfg/org.py
@@ -1,80 +1,106 @@
from collections.abc import Iterator
-from dataclasses import dataclass, field
+from dataclasses import dataclass
from typing import Callable, ClassVar, Self, final, override
from docker_compose.cfg.org_yaml import OrgDataYaml
-from docker_compose.cfg.record import Record, RecordCls, RecordName
+from docker_compose.cfg.replace import ReplaceDynamic, RecordCls
+
+#
+# _ORG = "org"
+# _APP = "name"
+# Org = partial(Record, ORG)
+# App = partial(Record, APP)
@final
@dataclass(frozen=True, slots=True)
-class OrgVal(RecordCls[str]):
- old: ClassVar[RecordName] = RecordName("org")
+class Org(RecordCls):
+ rep: ClassVar[str] = "org"
@final
@dataclass(frozen=True, slots=True)
-class Org:
- val: str
- replace: OrgVal = field(init=False)
-
- def __post_init__(self) -> None:
- setter = super(Org, self).__setattr__
- setter("replace", OrgVal(self.val))
+class App(RecordCls):
+ rep: ClassVar[str] = "name"
@final
@dataclass(frozen=True, slots=True)
-class AppVal(RecordCls[str]):
- old: ClassVar[RecordName] = RecordName("name")
-
-
-@final
-@dataclass(frozen=True, slots=True)
-class App:
- val: str
- replace: AppVal = field(init=False)
-
- def __post_init__(self) -> None:
- setter = super(App, self).__setattr__
- setter("replace", AppVal(self.val))
-
-
-@final
-@dataclass(frozen=True, slots=True)
-class UrlValNew:
- val: str | None
+class Url(RecordCls):
+ rep: ClassVar[str] = "url"
@override
- def __str__(self) -> str:
- if not self.val:
- return ""
- return ".".join((self.val, "ccamper7", "net"))
+ @classmethod
+ def from_str(cls, string: str | None) -> Self:
+ return super(Url, cls).from_str(".".join((string, "ccamper7", "net")) if string else "")
+
+ # return Record("url", ".".join((val, "ccamper7", "net")) if val else "")
-@final
-@dataclass(frozen=True, slots=True)
-class UrlVal(RecordCls[UrlValNew]):
- old = RecordName("url")
+#
+# @final
+# @dataclass(frozen=True, slots=True)
+# class Org:
+# val: str
+# replace: Record = field(init=False)
+#
+# def __post_init__(self) -> None:
+# setter = super(Org, self).__setattr__
+# setter("replace", OrgVal(self.val))
-@final
-@dataclass(frozen=True, slots=True)
-class Url:
- val: str | None
- replace: UrlVal = field(init=False)
+# @final
+# @dataclass(frozen=True, slots=True)
+# class AppVal(RecordCls[str]):
+# old: ClassVar[RecordName] = RecordName("name")
+#
+#
+# @final
+# @dataclass(frozen=True, slots=True)
+# class App:
+# val: str
+# replace: AppVal = field(init=False)
+#
+# def __post_init__(self) -> None:
+# setter = super(App, self).__setattr__
+# setter("replace", AppVal(self.val))
- def __post_init__(self) -> None:
- setter = super(Url, self).__setattr__
- setter("replace", UrlVal(UrlValNew(self.val)))
+
+# @final
+# @dataclass(frozen=True, slots=True)
+# class UrlValNew:
+# val: str | None
+#
+# @override
+# def __str__(self) -> str:
+# if not self.val:
+# return ""
+# return ".".join((self.val, "ccamper7", "net"))
+#
+#
+# @final
+# @dataclass(frozen=True, slots=True)
+# class UrlVal(RecordCls[UrlValNew]):
+# old = RecordName("url")
+#
+#
+# @final
+# @dataclass(frozen=True, slots=True)
+# class Url:
+# val: str | None
+# replace: UrlVal = field(init=False)
+#
+# def __post_init__(self) -> None:
+# setter = super(Url, self).__setattr__
+# setter("replace", UrlVal(UrlValNew(self.val)))
@final
@dataclass(frozen=True, slots=True)
class OrgData:
- org_app = Record[RecordName, str](
- RecordName(f"{OrgVal.old.val}_{AppVal.old.val}"),
- f"{OrgVal.old!s}_{AppVal.old!s}",
+ org_app = ReplaceDynamic(
+ f"${Org.rep.upper()}_{Org.rep.upper()}",
+ f"{ReplaceDynamic.get_format(Org.rep)}_{ReplaceDynamic.get_format(App.rep)}",
)
app: App
org: Org
@@ -83,16 +109,16 @@ class OrgData:
@classmethod
def from_dict(cls, app: str, org: str, data: OrgDataYaml) -> Self:
return cls(
- App(app),
- Org(org),
- Url(data.get("url")),
+ App.from_str(app),
+ Org.from_str(org),
+ Url.from_str(data.get("url")),
)
@property
def render_funcs(self) -> Iterator[Callable[[str], str]]:
- yield self.app.replace
- yield self.org.replace
- yield self.url.replace
+ yield self.app
+ yield self.org
+ yield self.url
@property
def pre_render_funcs(self) -> Iterator[Callable[[str], str]]:
diff --git a/src/docker_compose/cfg/record.py b/src/docker_compose/cfg/record.py
deleted file mode 100644
index d3b3a02..0000000
--- a/src/docker_compose/cfg/record.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from dataclasses import dataclass
-from typing import ClassVar, Protocol, final, override
-
-
-class String(Protocol):
- @override
- def __str__(self) -> str: ...
-
-
-@final
-@dataclass(frozen=True, slots=True)
-class RecordName:
- val: str
-
- @override
- def __str__(self) -> str:
- return f"${{_{str(self.val).upper()}}}"
-
- # def replace(self, string: str) -> str:
- # return string.replace(str(self), str(self))
-
-
-@dataclass(frozen=True, slots=True)
-class RecordCls[T_New: String]:
- old: ClassVar[String]
- new: T_New
-
- def __call__(self, string: str) -> str:
- return string.replace(str(self.old), str(self.new))
-
-
-@dataclass(frozen=True, slots=True)
-class Record[T_Old: String, T_New: String]:
- old: T_Old
- new: T_New
-
- def __call__(self, string: str) -> str:
- return string.replace(str(self.old), str(self.new))
diff --git a/src/docker_compose/cfg/replace.py b/src/docker_compose/cfg/replace.py
new file mode 100644
index 0000000..3d834d3
--- /dev/null
+++ b/src/docker_compose/cfg/replace.py
@@ -0,0 +1,87 @@
+from collections.abc import Callable
+from dataclasses import dataclass, field
+from typing import ClassVar, Self, override, final
+
+
+#
+# class String(Protocol):
+# @override
+# def __str__(self) -> str: ...
+
+
+# def replace(self, string: str) -> str:
+# return string.replace(str(self), str(self))
+
+
+# @dataclass(frozen=True, slots=True)
+# class RecordCls[T_New: str | Callable[[None],str]]:
+# old: ClassVar[str]
+# new: T_New
+#
+# def __call__(self, string: str) -> str:
+# return string.replace(str(self.old), self.new if isinstance(self.new, str) else self.new())
+@final
+@dataclass(frozen=True, slots=True)
+class ReplaceStatic:
+ val: 'str| ReplaceStatic'
+ fmt: str = field(init=False)
+
+ def __post_init__(self):
+ setter = super(ReplaceStatic, self).__setattr__
+ setter('fmt', f"${{_{self.val.upper()}}}")
+
+ def __call__(self, string: str) -> str:
+ return string.replace(
+ self.fmt,
+ str(self)
+ )
+ def __str__(self) -> str:
+ return self.val if isinstance(self.val, str) else self.val.fmt
+
+@dataclass(frozen=True, slots=True)
+class ReplaceDynamic:
+ src: ReplaceStatic
+ dest: str | Callable[[], str]
+
+ def __call__(self, string: str) -> str:
+ return string.replace(
+ self.src,
+ str(self),
+ )
+
+ @override
+ def __str__(self) -> str:
+ return self.dest if isinstance(self.dest, str) else self.dest()
+ #
+ # @staticmethod
+ # def get_format(val: str) -> str:
+ # return f"${{_{val.upper()}}}"
+
+ @classmethod
+ def auto_format(cls, src: str, dest: str | Callable[[], str]) -> Self:
+ return cls(ReplaceStatic(src), dest)
+
+ @classmethod
+ def from_str(cls, string: str) -> Self:
+ return cls.auto_format(string, string)
+
+
+ # @property
+ # def stage_two(self) -> 'Record':
+ # return Record.from_str(self.dest if isinstance(self.dest, str) else self.dest())
+
+
+@dataclass(frozen=True, slots=True)
+class RecordCls(ReplaceDynamic):
+ # val: ClassVar[str]# = 'org'
+ rep: ClassVar[ReplaceStatic] # = Record.get_format(val)
+
+ @override
+ @classmethod
+ def from_str(cls, string: str) -> Self:
+ return cls(cls.rep, string)
+
+ @classmethod
+ def two_stage(cls, dest: str) -> tuple[Self, Self]:
+ dest_var = ReplaceStatic(dest)
+ return cls(cls.rep, dest_var.fmt), cls(dest_var, dest)
diff --git a/src/docker_compose/compose/compose.py b/src/docker_compose/compose/compose.py
index bc24cb7..578e402 100644
--- a/src/docker_compose/compose/compose.py
+++ b/src/docker_compose/compose/compose.py
@@ -53,7 +53,7 @@ class Compose:
@property
def as_dict(self) -> ComposeYaml:
return ComposeYaml(
- name=str(OrgData.org_app.old),
+ name=str(OrgData.org_app),
services={
service.service_name: service.as_dict for service in self.services
},
@@ -67,3 +67,6 @@ class Compose:
def write_template(self):
self.cfg.src_paths.compose_file.write(self.as_template)
+
+ def __call__(self):
+ self.write_template()
diff --git a/src/docker_compose/compose/net.py b/src/docker_compose/compose/net.py
index 81e3393..1e6a261 100644
--- a/src/docker_compose/compose/net.py
+++ b/src/docker_compose/compose/net.py
@@ -27,7 +27,7 @@ class NetArgs:
@override
def __str__(self) -> str:
- return f"{OrgData.org_app.old!s}_{self.name}"
+ return f"{OrgData.org_app!s}_{self.name}"
@property
def external(self) -> bool:
diff --git a/src/docker_compose/compose/rendered.py b/src/docker_compose/compose/rendered.py
index 4253b84..14b9187 100644
--- a/src/docker_compose/compose/rendered.py
+++ b/src/docker_compose/compose/rendered.py
@@ -1,7 +1,7 @@
from collections.abc import Iterator
from dataclasses import dataclass
from pathlib import Path
-from typing import final
+from typing import final, override
from docker_compose.cfg import ROOT
from docker_compose.compose.compose import Compose
@@ -52,3 +52,11 @@ class Rendered(Compose):
def write(self) -> None:
self.cfg.dest_paths.compose_file.write(self.as_rendered)
+
+ @override
+ def __call__(self, force_env:bool=False) -> None:
+ super(Rendered, self).__call__()
+ self.mk_bind_vols()
+ self.cfg.mk_compose_env(force_env)
+ self.write()
+
diff --git a/src/docker_compose/compose/services.py b/src/docker_compose/compose/services.py
index 9052998..a64c0fa 100644
--- a/src/docker_compose/compose/services.py
+++ b/src/docker_compose/compose/services.py
@@ -1,8 +1,9 @@
from dataclasses import dataclass
from typing import Self, final, override
-from docker_compose.cfg.compose_paths import ServicePath, ServiceVal
-from docker_compose.cfg.org import OrgData, UrlVal
+from docker_compose.cfg.compose_paths import ServicePath
+from docker_compose.cfg.org import OrgData, Url
+from docker_compose.cfg.replace import ReplaceDynamic
from docker_compose.compose.services_yaml import (
HealthCheck,
ServiceYamlRead,
@@ -16,24 +17,25 @@ from docker_compose.util.Ts import T_Primitive
class Service:
_traefik_labels = frozenset(
(
- f"traefik.http.routers.{OrgData.org_app.old!s}.rule=Host(`{UrlVal.old!s}`)",
- f"traefik.http.routers.{OrgData.org_app.old!s}.entrypoints=websecure",
- f"traefik.docker.network={OrgData.org_app.old!s}_proxy",
- f"traefik.http.routers.{OrgData.org_app.old!s}.tls.certresolver=le",
+ f"traefik.http.routers.{OrgData.org_app.src}.rule=Host(`{Url.rep}`)",
+ f"traefik.http.routers.{OrgData.org_app.src}.entrypoints=websecure",
+ f"traefik.docker.network={OrgData.org_app.src}_proxy",
+ f"traefik.http.routers.{OrgData.org_app.src}.tls.certresolver=le",
)
)
@override
def __hash__(self) -> int:
- return hash(self.service_name)
+ return hash(str(self.fqdn))
@property
def service_name(self) -> str:
- return self.service_val.new.val.stem
+ return str(self.fqdn).split("_", maxsplit=3)[-1]
_sec_opts = frozenset(("no-new-privileges:true",))
# service_name: str
- service_val: ServiceVal
+ # service_val: ServiceVal
+ fqdn: ReplaceDynamic
command: tuple[str, ...]
entrypoint: tuple[str, ...]
environment: dict[str, T_Primitive]
@@ -46,21 +48,23 @@ class Service:
user: str | None
volumes: frozenset[str]
shm_size: str | None
- depends_on: frozenset[str]
+ depends_on: frozenset[str] | dict[str, dict[str, str]]
healthcheck: HealthCheck | None
ports: frozenset[str]
@classmethod
def from_path(cls, path: ServicePath) -> Self:
- return cls.from_dict(path.replace, path.as_dict)
+ return cls.from_dict(path.fqdn, path.as_dict)
@classmethod
- def from_dict(cls, service_val: ServiceVal, data: ServiceYamlRead) -> Self:
+ def from_dict(cls, fqdn: ReplaceDynamic, data: ServiceYamlRead) -> Self:
# helper = ServiceYamlProps(data)
labels = frozenset(data.get("labels", ()))
# ports = (f'"{p}"' for p in data.get("ports", ()))
+ deps = data.get("depends_on", ())
return cls(
- service_val,
+ # service_val,
+ fqdn,
tuple(data.get("command", ())),
tuple(data.get("entrypoint", ())),
data.get("environment", {}),
@@ -75,7 +79,7 @@ class Service:
data.get("user"),
frozenset(data.get("volumes", ())),
data.get("shm_size"),
- frozenset(data.get("depends_on", ())),
+ deps if isinstance(deps, dict) else frozenset(deps),
data.get("healthcheck"),
frozenset(data.get("ports", ())),
)
@@ -93,7 +97,7 @@ class Service:
security_opt=self.security_opt,
user=self.user,
volumes=self.volumes,
- container_name=self.service_val(ServicePath.fqdn.new),
+ container_name=str(self.fqdn),
restart=self.restart,
shm_size=self.shm_size,
depends_on=self.depends_on,
diff --git a/src/docker_compose/compose/services_yaml.py b/src/docker_compose/compose/services_yaml.py
index 0f0d17c..161be56 100644
--- a/src/docker_compose/compose/services_yaml.py
+++ b/src/docker_compose/compose/services_yaml.py
@@ -23,7 +23,7 @@ class ServiceYamlRead(TypedDict):
user: NotRequired[str]
volumes: NotRequired[list[str]]
shm_size: NotRequired[str]
- depends_on: NotRequired[list[str]]
+ depends_on: NotRequired[list[str]|dict[str,dict[str,str]]]
healthcheck: NotRequired[HealthCheck]
ports: NotRequired[list[str]]
@@ -42,6 +42,6 @@ class ServiceYamlWrite(TypedDict):
container_name: str
restart: str
shm_size: str | None
- depends_on: frozenset[str]
+ depends_on: frozenset[str]|dict[str,dict[str,str]]
healthcheck: HealthCheck | None
ports: frozenset[str]
diff --git a/src/docker_compose/util/Ts.py b/src/docker_compose/util/Ts.py
index 73cc698..dfede4e 100644
--- a/src/docker_compose/util/Ts.py
+++ b/src/docker_compose/util/Ts.py
@@ -93,6 +93,8 @@ def get_types(
if isinstance(annotation, TypeAliasType):
annotation = annotation.__value__ # pyright: ignore[reportAny]
if isinstance(annotation, GenericAlias):
+ # print(annotation)
+ # print(get_origin(annotation))
return get_origin(annotation)
if isinstance(annotation, UnionType):
return tuple(get_union_types(annotation))
diff --git a/src/docker_compose/util/yaml_util.py b/src/docker_compose/util/yaml_util.py
index 35efe20..2648106 100644
--- a/src/docker_compose/util/yaml_util.py
+++ b/src/docker_compose/util/yaml_util.py
@@ -142,10 +142,18 @@ def validate_typed_dict(
continue
# try:
- if not isinstance(val, get_types(t2)): # pyright: ignore[reportAny]
- raise TypeError(
- f"key: {key} expected *{type(t2).__name__}*, got *{type(val).__name__}*" # pyright: ignore[reportAny]
+ # print(t2)
+ # print(get_types(t2))
+ t2 = get_types(t2)
+ if not isinstance(val, t2):
+ msg = (
+ ", ".join(t.__name__ for t in t2)
+ if isinstance(t2, tuple)
+ else t.__name__
)
+ e = TypeError(f"key: {key} expected *{msg}*, got *{type(val).__name__}*")
+ e.add_note(f"key: {key!s}")
+ raise e
# valid = isinstance(val, get_types(t2))
# except TypeError: