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.
Files changed (103) hide show
  1. {nyl-0.2.0 → nyl-0.2.2}/PKG-INFO +2 -2
  2. {nyl-0.2.0 → nyl-0.2.2}/pyproject.toml +2 -2
  3. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/resource.pyi +1 -0
  4. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/run.py +3 -3
  5. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/template.py +5 -5
  6. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/helmchart.py +3 -3
  7. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/kubeconfig.py +4 -4
  8. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/templating.py +59 -2
  9. nyl-0.2.2/src/nyl/tools/yaml.py +27 -0
  10. {nyl-0.2.0 → nyl-0.2.2}/uv.lock +4 -4
  11. {nyl-0.2.0 → nyl-0.2.2}/.envrc +0 -0
  12. {nyl-0.2.0 → nyl-0.2.2}/.github/workflows/docker.yaml +0 -0
  13. {nyl-0.2.0 → nyl-0.2.2}/.github/workflows/docs.yaml +0 -0
  14. {nyl-0.2.0 → nyl-0.2.2}/.github/workflows/python.yaml +0 -0
  15. {nyl-0.2.0 → nyl-0.2.2}/.gitignore +0 -0
  16. {nyl-0.2.0 → nyl-0.2.2}/.kraken.py +0 -0
  17. {nyl-0.2.0 → nyl-0.2.2}/README.md +0 -0
  18. {nyl-0.2.0 → nyl-0.2.2}/argocd-cmp/Dockerfile +0 -0
  19. {nyl-0.2.0 → nyl-0.2.2}/argocd-cmp/README.md +0 -0
  20. {nyl-0.2.0 → nyl-0.2.2}/argocd-cmp/plugin.yaml +0 -0
  21. {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/.envrc +0 -0
  22. {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/.secrets.yaml +0 -0
  23. {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/.sops.yaml +0 -0
  24. {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/README.md +0 -0
  25. {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/argocd.yaml +0 -0
  26. {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/nyl-project.yaml +0 -0
  27. {nyl-0.2.0 → nyl-0.2.2}/argocd-with-nyl/nyl-secrets.yaml +0 -0
  28. {nyl-0.2.0 → nyl-0.2.2}/docs/content/index.md +0 -0
  29. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/applysets.md +0 -0
  30. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/argocd-plugin.md +0 -0
  31. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/cluster-connectivity.md +0 -0
  32. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration/environment.md +0 -0
  33. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration/profiles.md +0 -0
  34. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration/projects.md +0 -0
  35. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration/secrets.md +0 -0
  36. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration-api/profiles.md +0 -0
  37. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/configuration-api/secrets.md +0 -0
  38. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/templating/basics.md +0 -0
  39. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/templating/components.md +0 -0
  40. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/templating/inlining.md +0 -0
  41. {nyl-0.2.0 → nyl-0.2.2}/docs/content/reference/templating/secrets.md +0 -0
  42. {nyl-0.2.0 → nyl-0.2.2}/docs/mkdocs.yml +0 -0
  43. {nyl-0.2.0 → nyl-0.2.2}/docs/pyproject.toml +0 -0
  44. {nyl-0.2.0 → nyl-0.2.2}/docs/uv.lock +0 -0
  45. {nyl-0.2.0 → nyl-0.2.2}/example/manifests/postgres.yaml +0 -0
  46. {nyl-0.2.0 → nyl-0.2.2}/example/nyl-profiles.yaml +0 -0
  47. {nyl-0.2.0 → nyl-0.2.2}/example/nyl-secrets.yaml +0 -0
  48. {nyl-0.2.0 → nyl-0.2.2}/example/secrets.yaml +0 -0
  49. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/README.md +0 -0
  50. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/__init__.pyi +0 -0
  51. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/__init__.pyi +0 -0
  52. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/client.pyi +0 -0
  53. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/discovery.pyi +0 -0
  54. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/dynamic/exceptions.pyi +0 -0
  55. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/watch/__init__.pyi +0 -0
  56. {nyl-0.2.0 → nyl-0.2.2}/src/kubernetes/watch/watch.pyi +0 -0
  57. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/__init__.py +0 -0
  58. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/__init__.py +0 -0
  59. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/argocd.py +0 -0
  60. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/crds.py +0 -0
  61. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/new.py +0 -0
  62. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/profile.py +0 -0
  63. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/secrets.py +0 -0
  64. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/tools/__init__.py +0 -0
  65. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/tools/bcrypt.py +0 -0
  66. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/tools/sops.py +0 -0
  67. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/commands/tun.py +0 -0
  68. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/__init__.py +0 -0
  69. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/components.py +0 -0
  70. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/dispatch.py +0 -0
  71. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/dispatch_test.py +0 -0
  72. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/generator/statefulsecret.py +0 -0
  73. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/__init__.py +0 -0
  74. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/config.py +0 -0
  75. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/config_test.py +0 -0
  76. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/profiles/tunnel.py +0 -0
  77. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/project/config.py +0 -0
  78. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/__init__.py +0 -0
  79. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/applyset.py +0 -0
  80. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/applyset_test.py +0 -0
  81. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/helmchart.py +0 -0
  82. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/placeholder.py +0 -0
  83. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/resources/statefulsecret.py +0 -0
  84. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/__init__.py +0 -0
  85. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/config.py +0 -0
  86. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/config_test.py +0 -0
  87. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/null.py +0 -0
  88. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/sops.py +0 -0
  89. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/secrets/sops_test.py +0 -0
  90. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/templating_test.py +0 -0
  91. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/fs.py +0 -0
  92. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/fs_test.py +0 -0
  93. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/kubectl.py +0 -0
  94. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/kubernetes.py +0 -0
  95. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/kvstore.py +0 -0
  96. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/loads.py +0 -0
  97. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/logging.py +0 -0
  98. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/shell.py +0 -0
  99. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/testing.py +0 -0
  100. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/typer.py +0 -0
  101. {nyl-0.2.0 → nyl-0.2.2}/src/nyl/tools/types.py +0 -0
  102. {nyl-0.2.0 → nyl-0.2.2}/tests/__init__.py +0 -0
  103. {nyl-0.2.0 → nyl-0.2.2}/tests/conftest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nyl
3
- Version: 0.2.0
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<0.1.0,>=0.0.6
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.0" # sed'ed on publish in CI
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.0.6,<0.1.0",
23
+ "structured-templates>=0.1.1",
24
24
  "typer>=0.12.3",
25
25
  "typing-extensions>=4.12.2",
26
26
  ]
@@ -40,3 +40,4 @@ class ResourceInstance:
40
40
  attributes: ResourceField
41
41
 
42
42
  def __getitem__(self, key: str) -> ResourceField | Any: ...
43
+ def to_dict(self) -> dict[str, Any]: ...
@@ -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.safe_load(kubeconfig.read_text())
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.dump(kubeconfig_data))
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.safe_dump(applyset.dump()))
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.safe_dump(manifest), " "),
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.safe_dump(manifest))
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.safe_load_all(file.read_text())))))
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.safe_dump(res.spec.values))
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.safe_load_all(result.stdout.decode()))))
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.safe_load(raw_kubeconfig.read_text())
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.safe_load(path.read_text())
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.safe_dump(kubeconfig_data))
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)
@@ -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.0.6,<0.1.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.0.6"
557
+ version = "0.1.1"
558
558
  source = { registry = "https://pypi.org/simple" }
559
- sdist = { url = "https://files.pythonhosted.org/packages/f5/13/ff59332e935cc3569a70b4048bceadae6edb3b0e68e701410e1642ec0e0f/structured_templates-0.0.6.tar.gz", hash = "sha256:fbdb689c5cafbeecd9b4bc6f90fc31a1990385692e9f6852db2cc05d5d5503fb", size = 4866 }
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/c6/67/31a676f5bbfb86c17468371e2c465e8168d8dcbd1e7658ce64275731590e/structured_templates-0.0.6-py3-none-any.whl", hash = "sha256:591fcefcf3e474a94e45880abca1d745d176c1019855a046fe540138bbfa8582", size = 6640 },
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]]
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