nyl 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {nyl-0.2.0 → nyl-0.2.2}/PKG-INFO +2 -2
- {nyl-0.2.0 → nyl-0.2.2}/pyproject.toml +2 -2
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/resource.pyi +1 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/run.py +3 -3
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/template.py +5 -5
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/helmchart.py +3 -3
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/kubeconfig.py +4 -4
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/templating.py +59 -2
- nyl-0.2.2/src/nyl/tools/yaml.py +27 -0
- {nyl-0.2.0 → nyl-0.2.2}/uv.lock +4 -4
- {nyl-0.2.0 → nyl-0.2.2}/.envrc +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/.github/workflows/docker.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/.github/workflows/docs.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/.github/workflows/python.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/.gitignore +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/.kraken.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/README.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-cmp/Dockerfile +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-cmp/README.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-cmp/plugin.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/.envrc +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/.secrets.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/.sops.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/README.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/argocd.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/nyl-project.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/nyl-secrets.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/index.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/applysets.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/argocd-plugin.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/cluster-connectivity.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration/environment.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration/profiles.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration/projects.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration/secrets.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration-api/profiles.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration-api/secrets.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/templating/basics.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/templating/components.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/templating/inlining.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/templating/secrets.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/mkdocs.yml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/pyproject.toml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/docs/uv.lock +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/example/manifests/postgres.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/example/nyl-profiles.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/example/nyl-secrets.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/example/secrets.yaml +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/README.md +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/__init__.pyi +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/__init__.pyi +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/client.pyi +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/discovery.pyi +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/exceptions.pyi +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/watch/__init__.pyi +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/watch/watch.pyi +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/__init__.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/__init__.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/argocd.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/crds.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/new.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/profile.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/secrets.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/tools/__init__.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/tools/bcrypt.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/tools/sops.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/tun.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/__init__.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/components.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/dispatch.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/dispatch_test.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/statefulsecret.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/__init__.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/config.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/config_test.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/tunnel.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/project/config.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/__init__.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/applyset.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/applyset_test.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/helmchart.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/placeholder.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/statefulsecret.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/__init__.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/config.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/config_test.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/null.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/sops.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/sops_test.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/templating_test.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/fs.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/fs_test.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/kubectl.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/kubernetes.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/kvstore.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/loads.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/logging.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/shell.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/testing.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/typer.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/types.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/tests/__init__.py +0 -0
- {nyl-0.2.0 → nyl-0.2.2}/tests/conftest.py +0 -0
{nyl-0.2.0 → nyl-0.2.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: nyl
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Advanced templating engine for Kubernetes and GitOps.
|
|
5
5
|
Author-email: Niklas Rosenstein <rosensteinniklas@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -15,7 +15,7 @@ Requires-Dist: nr-stream>=1.1.5
|
|
|
15
15
|
Requires-Dist: pyyaml>=6.0.1
|
|
16
16
|
Requires-Dist: requests>=2.32.3
|
|
17
17
|
Requires-Dist: stablehash<0.3.0,>=0.2.1
|
|
18
|
-
Requires-Dist: structured-templates
|
|
18
|
+
Requires-Dist: structured-templates>=0.1.1
|
|
19
19
|
Requires-Dist: typer>=0.12.3
|
|
20
20
|
Requires-Dist: typing-extensions>=4.12.2
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nyl"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2" # sed'ed on publish in CI
|
|
8
8
|
description = "Advanced templating engine for Kubernetes and GitOps."
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Niklas Rosenstein", email = "rosensteinniklas@gmail.com"},
|
|
@@ -20,7 +20,7 @@ dependencies = [
|
|
|
20
20
|
"pyyaml>=6.0.1",
|
|
21
21
|
"requests>=2.32.3",
|
|
22
22
|
"stablehash>=0.2.1,<0.3.0",
|
|
23
|
-
"structured-templates>=0.
|
|
23
|
+
"structured-templates>=0.1.1",
|
|
24
24
|
"typer>=0.12.3",
|
|
25
25
|
"typing-extensions>=4.12.2",
|
|
26
26
|
]
|
|
@@ -6,7 +6,7 @@ import sys
|
|
|
6
6
|
from tempfile import TemporaryDirectory
|
|
7
7
|
from loguru import logger
|
|
8
8
|
from typer import Argument, Option
|
|
9
|
-
import yaml
|
|
9
|
+
from nyl.tools import yaml
|
|
10
10
|
from nyl.profiles import ActivatedProfile, ProfileManager
|
|
11
11
|
from nyl.profiles.kubeconfig import _trim_to_context
|
|
12
12
|
from nyl.tools.logging import lazy_str
|
|
@@ -56,7 +56,7 @@ def run(
|
|
|
56
56
|
sys.exit(1)
|
|
57
57
|
|
|
58
58
|
try:
|
|
59
|
-
kubeconfig_data = yaml.
|
|
59
|
+
kubeconfig_data = yaml.loads(kubeconfig.read_text())
|
|
60
60
|
kubeconfig_data = _trim_to_context(kubeconfig_data, profile_name)
|
|
61
61
|
except ValueError:
|
|
62
62
|
logger.debug("Failed to parse the kubeconfig file/find context '{}'.", profile_name)
|
|
@@ -83,7 +83,7 @@ def run(
|
|
|
83
83
|
tmpdir = TemporaryDirectory()
|
|
84
84
|
atexit.register(tmpdir.cleanup)
|
|
85
85
|
kubeconfig = Path(tmpdir.name) / "kubeconfig"
|
|
86
|
-
kubeconfig.write_text(yaml.
|
|
86
|
+
kubeconfig.write_text(yaml.dumps(kubeconfig_data))
|
|
87
87
|
kubeconfig.chmod(0o600)
|
|
88
88
|
|
|
89
89
|
profile = ActivatedProfile(kubeconfig)
|
|
@@ -6,7 +6,7 @@ from textwrap import indent
|
|
|
6
6
|
from typing import Optional
|
|
7
7
|
from loguru import logger
|
|
8
8
|
from typer import Argument, Option
|
|
9
|
-
import yaml
|
|
9
|
+
from nyl.tools import yaml
|
|
10
10
|
from nyl.generator import reconcile_generator
|
|
11
11
|
from nyl.generator.dispatch import DispatchingGenerator
|
|
12
12
|
from nyl.profiles import ProfileManager
|
|
@@ -252,7 +252,7 @@ def template(
|
|
|
252
252
|
kubectl.diff(Manifests([applyset.dump()]))
|
|
253
253
|
else:
|
|
254
254
|
print("---")
|
|
255
|
-
print(yaml.
|
|
255
|
+
print(yaml.dumps(applyset.dump()))
|
|
256
256
|
|
|
257
257
|
# Find all manifests without a namespace and inject the namespace name into them.
|
|
258
258
|
# If there is an applyset, ensure they are marked as part of the applyset.
|
|
@@ -267,7 +267,7 @@ def template(
|
|
|
267
267
|
logger.opt(ansi=True).error(
|
|
268
268
|
"A manifest in <yellow>'{}'</> has no <cyan>metadata</> key:\n\n{}",
|
|
269
269
|
source.file,
|
|
270
|
-
indent(yaml.
|
|
270
|
+
indent(yaml.dumps(manifest), " "),
|
|
271
271
|
)
|
|
272
272
|
exit(1)
|
|
273
273
|
|
|
@@ -317,7 +317,7 @@ def template(
|
|
|
317
317
|
# If we're not going to be applying the manifests immediately via `kubectl`, we print them to stdout.
|
|
318
318
|
for manifest in source.manifests:
|
|
319
319
|
print("---")
|
|
320
|
-
print(yaml.
|
|
320
|
+
print(yaml.dumps(manifest))
|
|
321
321
|
|
|
322
322
|
|
|
323
323
|
def load_manifests(paths: list[Path]) -> list[ManifestsWithSource]:
|
|
@@ -347,7 +347,7 @@ def load_manifests(paths: list[Path]) -> list[ManifestsWithSource]:
|
|
|
347
347
|
|
|
348
348
|
result = []
|
|
349
349
|
for file in files:
|
|
350
|
-
manifests = Manifests(list(map(Manifest, filter(None, yaml.
|
|
350
|
+
manifests = Manifests(list(map(Manifest, filter(None, yaml.loads_all(file.read_text())))))
|
|
351
351
|
result.append(ManifestsWithSource(manifests, file))
|
|
352
352
|
|
|
353
353
|
return result
|
|
@@ -9,7 +9,7 @@ from textwrap import indent
|
|
|
9
9
|
from urllib.parse import parse_qs, urlparse
|
|
10
10
|
|
|
11
11
|
from loguru import logger
|
|
12
|
-
import yaml
|
|
12
|
+
from nyl.tools import yaml
|
|
13
13
|
from nyl.generator import Generator
|
|
14
14
|
from nyl.resources.helmchart import ChartRef, HelmChart, ReleaseMetadata
|
|
15
15
|
from nyl.tools.shell import pretty_cmd
|
|
@@ -152,7 +152,7 @@ class HelmChartGenerator(Generator[HelmChart], resource_type=HelmChart):
|
|
|
152
152
|
|
|
153
153
|
with TemporaryDirectory() as tmp:
|
|
154
154
|
values_file = Path(tmp) / "values.yaml"
|
|
155
|
-
values_file.write_text(yaml.
|
|
155
|
+
values_file.write_text(yaml.dumps(res.spec.values))
|
|
156
156
|
|
|
157
157
|
command = [
|
|
158
158
|
"helm",
|
|
@@ -207,6 +207,6 @@ class HelmChartGenerator(Generator[HelmChart], resource_type=HelmChart):
|
|
|
207
207
|
f"stderr:\n{indent(e.stderr.decode(), prefix)}"
|
|
208
208
|
)
|
|
209
209
|
|
|
210
|
-
manifests = Manifests(list(filter(None, yaml.
|
|
210
|
+
manifests = Manifests(list(filter(None, yaml.loads_all(result.stdout.decode()))))
|
|
211
211
|
|
|
212
212
|
return manifests
|
|
@@ -5,8 +5,8 @@ import shlex
|
|
|
5
5
|
import subprocess
|
|
6
6
|
from typing import Any
|
|
7
7
|
from urllib.parse import urlparse
|
|
8
|
-
import yaml
|
|
9
8
|
|
|
9
|
+
from nyl.tools import yaml
|
|
10
10
|
from nyl.tools.shell import pretty_cmd
|
|
11
11
|
from .config import KubeconfigFromSsh, LocalKubeconfig
|
|
12
12
|
from loguru import logger
|
|
@@ -87,7 +87,7 @@ class KubeconfigManager:
|
|
|
87
87
|
raise ValueError(f"Unsupported Kubeconfig type: {source.__class__.__name__}")
|
|
88
88
|
|
|
89
89
|
# Find the Kubernetes API host and port.
|
|
90
|
-
kubeconfig_data = yaml.
|
|
90
|
+
kubeconfig_data = yaml.loads(raw_kubeconfig.read_text())
|
|
91
91
|
kubeconfig_data = _trim_to_context(kubeconfig_data, source.context)
|
|
92
92
|
server: str = kubeconfig_data["clusters"][0]["cluster"]["server"]
|
|
93
93
|
api_host, api_port = (lambda parsed: (parsed.hostname, parsed.port or 443))(urlparse(server))
|
|
@@ -108,7 +108,7 @@ class KubeconfigManager:
|
|
|
108
108
|
def get_updated_kubeconfig(
|
|
109
109
|
self, *, profile_name: str, path: Path, context: str, api_host: str, api_port: int
|
|
110
110
|
) -> Path:
|
|
111
|
-
kubeconfig_data = yaml.
|
|
111
|
+
kubeconfig_data = yaml.loads(path.read_text())
|
|
112
112
|
kubeconfig_data = _trim_to_context(kubeconfig_data, context, rename_context=profile_name)
|
|
113
113
|
|
|
114
114
|
# TODO: Do we need to support the Kubernetes API hosted on a subpath?
|
|
@@ -116,7 +116,7 @@ class KubeconfigManager:
|
|
|
116
116
|
|
|
117
117
|
final_kubeconfig = self._state_dir / profile_name / "kubeconfig.local"
|
|
118
118
|
final_kubeconfig.parent.mkdir(parents=True, exist_ok=True)
|
|
119
|
-
final_kubeconfig.write_text(yaml.
|
|
119
|
+
final_kubeconfig.write_text(yaml.dumps(kubeconfig_data))
|
|
120
120
|
final_kubeconfig.chmod(0o600)
|
|
121
121
|
|
|
122
122
|
return final_kubeconfig
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
Implements Nyl's variant of structured templating.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from collections.abc import Mapping
|
|
5
6
|
from contextlib import contextmanager
|
|
6
7
|
from dataclasses import dataclass
|
|
7
|
-
from typing import Any, Callable, ClassVar, Iterator, TypeVar, cast
|
|
8
|
+
from typing import Any, Callable, ClassVar, Iterator, Sequence, TypeVar, cast
|
|
8
9
|
from structured_templates import TemplateEngine as _TemplateEngine
|
|
9
10
|
from structured_templates.exceptions import TemplateError
|
|
10
11
|
from kubernetes.client.api_client import ApiClient
|
|
11
12
|
|
|
12
13
|
from kubernetes.dynamic.client import DynamicClient
|
|
13
14
|
from kubernetes.client.exceptions import ApiException
|
|
15
|
+
from kubernetes.dynamic.resource import ResourceField, ResourceInstance
|
|
14
16
|
from nyl.secrets.config import SecretProvider
|
|
15
17
|
from nyl.tools.types import Manifest, Manifests
|
|
16
18
|
|
|
@@ -90,7 +92,61 @@ def lookup(api_version: str, kind: str, name: str, namespace: str) -> Any:
|
|
|
90
92
|
raise LookupError(f"Resource '{kind}/{name}' not found in namespace '{namespace}'.")
|
|
91
93
|
else:
|
|
92
94
|
raise
|
|
93
|
-
return obj
|
|
95
|
+
return LookupResourceWrapper(obj)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class LookupResourceWrapper(Mapping[str, Any]):
|
|
99
|
+
"""
|
|
100
|
+
A wrapper for a Kubernetes resources returned by `lookup()` that permits looking up fields by `__getitem__()`
|
|
101
|
+
and `__getattr__()`. This wraps a `ResourceInstance` or `ResourceField`, which can later be treated by the
|
|
102
|
+
`NylTemplateEngine` to serialize into a dictionary when embedded into a manifest.
|
|
103
|
+
|
|
104
|
+
This class is needed because the YAML serializer will not be able to serialize the `ResourceInstance` or
|
|
105
|
+
`ResourceField` objects returned by `lookup()`.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def maybe_wrap(obj: Any) -> Any:
|
|
110
|
+
if isinstance(obj, (ResourceInstance, ResourceField)):
|
|
111
|
+
return LookupResourceWrapper(obj)
|
|
112
|
+
if isinstance(obj, Sequence) and not isinstance(obj, str):
|
|
113
|
+
obj = [LookupResourceWrapper.maybe_wrap(x) for x in obj]
|
|
114
|
+
return obj
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def materialize(obj: Any) -> Any:
|
|
118
|
+
match obj:
|
|
119
|
+
case LookupResourceWrapper():
|
|
120
|
+
return obj.__obj.to_dict()
|
|
121
|
+
case str():
|
|
122
|
+
return obj
|
|
123
|
+
if isinstance(obj, Sequence):
|
|
124
|
+
return [LookupResourceWrapper.materialize(x) for x in obj]
|
|
125
|
+
if isinstance(obj, Mapping):
|
|
126
|
+
return {k: LookupResourceWrapper.materialize(v) for k, v in obj.items()}
|
|
127
|
+
return obj
|
|
128
|
+
|
|
129
|
+
def __init__(self, obj: ResourceInstance | ResourceField) -> None:
|
|
130
|
+
assert isinstance(obj, (ResourceInstance, ResourceField)), f"got {type(obj).__name__}"
|
|
131
|
+
self.__obj = obj
|
|
132
|
+
|
|
133
|
+
def __iter__(self) -> Iterator[str]:
|
|
134
|
+
if isinstance(self.__obj, ResourceInstance):
|
|
135
|
+
target = self.__obj.attributes
|
|
136
|
+
else:
|
|
137
|
+
target = self.__obj
|
|
138
|
+
for key, _ in target:
|
|
139
|
+
yield key
|
|
140
|
+
|
|
141
|
+
def __len__(self) -> int:
|
|
142
|
+
# TODO: Find a better (faster) way to get the length of the resource instance/field attributes.
|
|
143
|
+
return len(self.__obj.to_dict())
|
|
144
|
+
|
|
145
|
+
def __getattr__(self, key: str) -> Any:
|
|
146
|
+
return LookupResourceWrapper.maybe_wrap(getattr(self.__obj, key))
|
|
147
|
+
|
|
148
|
+
def __getitem__(self, key: str) -> Any:
|
|
149
|
+
return LookupResourceWrapper.maybe_wrap(self.__obj[key])
|
|
94
150
|
|
|
95
151
|
|
|
96
152
|
class LookupError(Exception):
|
|
@@ -165,6 +221,7 @@ class NylTemplateEngine:
|
|
|
165
221
|
)
|
|
166
222
|
)
|
|
167
223
|
|
|
224
|
+
result = LookupResourceWrapper.materialize(result)
|
|
168
225
|
return Manifests(result)
|
|
169
226
|
|
|
170
227
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from collections.abc import Iterator
|
|
2
|
+
from typing import Any, TextIO
|
|
3
|
+
from yaml import SafeDumper, SafeLoader
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PatchedSafeLoader(SafeLoader):
|
|
8
|
+
yaml_implicit_resolvers = SafeLoader.yaml_implicit_resolvers.copy()
|
|
9
|
+
|
|
10
|
+
# See https://github.com/yaml/pyyaml/issues/89
|
|
11
|
+
yaml_implicit_resolvers.pop("=")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PatchedSafeDumper(SafeDumper):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def dumps(data: Any) -> str:
|
|
19
|
+
return yaml.dump(data, Dumper=PatchedSafeDumper)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def loads(text: str) -> Any:
|
|
23
|
+
return yaml.load(text, Loader=PatchedSafeLoader)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def loads_all(stream: str | TextIO) -> Iterator[Any]:
|
|
27
|
+
return yaml.load_all(stream, Loader=PatchedSafeLoader)
|
{nyl-0.2.0 → nyl-0.2.2}/uv.lock
RENAMED
|
@@ -340,7 +340,7 @@ requires-dist = [
|
|
|
340
340
|
{ name = "pyyaml", specifier = ">=6.0.1" },
|
|
341
341
|
{ name = "requests", specifier = ">=2.32.3" },
|
|
342
342
|
{ name = "stablehash", specifier = ">=0.2.1,<0.3.0" },
|
|
343
|
-
{ name = "structured-templates", specifier = ">=0.
|
|
343
|
+
{ name = "structured-templates", specifier = ">=0.1.1" },
|
|
344
344
|
{ name = "typer", specifier = ">=0.12.3" },
|
|
345
345
|
{ name = "typing-extensions", specifier = ">=4.12.2" },
|
|
346
346
|
]
|
|
@@ -554,11 +554,11 @@ wheels = [
|
|
|
554
554
|
|
|
555
555
|
[[package]]
|
|
556
556
|
name = "structured-templates"
|
|
557
|
-
version = "0.
|
|
557
|
+
version = "0.1.1"
|
|
558
558
|
source = { registry = "https://pypi.org/simple" }
|
|
559
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
559
|
+
sdist = { url = "https://files.pythonhosted.org/packages/04/97/89afe2eecb86ecb2673ebec1d18cc80bd252559ad27dc8940ff5d8a9b45f/structured_templates-0.1.1.tar.gz", hash = "sha256:b94d6c6c0b8a193e869dfa2511cc3b8b73a88f7edd69ecaa9ff4fd5539b6c5dc", size = 6044 }
|
|
560
560
|
wheels = [
|
|
561
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
561
|
+
{ url = "https://files.pythonhosted.org/packages/18/02/d9b2946086264afae6fe757af1a64b8f1629197106b2c93399f5814174d4/structured_templates-0.1.1-py3-none-any.whl", hash = "sha256:72b9e1b8ead52f33e91028838ccdf6ea69e1373da6d26b7b3d20e628472d3eaa", size = 7261 },
|
|
562
562
|
]
|
|
563
563
|
|
|
564
564
|
[[package]]
|
{nyl-0.2.0 → nyl-0.2.2}/.envrc
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|