EmbeddedSigner 0.1.5.dev1__tar.gz → 0.11.0.dev1__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.
- {embeddedsigner-0.1.5.dev1 → embeddedsigner-0.11.0.dev1}/EmbeddedSigner/_embed_signer.py +48 -15
- {embeddedsigner-0.1.5.dev1 → embeddedsigner-0.11.0.dev1}/EmbeddedSigner/cli.py +39 -13
- {embeddedsigner-0.1.5.dev1 → embeddedsigner-0.11.0.dev1}/PKG-INFO +5 -2
- {embeddedsigner-0.1.5.dev1 → embeddedsigner-0.11.0.dev1}/README.md +4 -1
- {embeddedsigner-0.1.5.dev1 → embeddedsigner-0.11.0.dev1}/pyproject.toml +2 -2
- {embeddedsigner-0.1.5.dev1 → embeddedsigner-0.11.0.dev1}/EmbeddedSigner/__init__.py +0 -0
- {embeddedsigner-0.1.5.dev1 → embeddedsigner-0.11.0.dev1}/LICENSE +0 -0
|
@@ -53,7 +53,9 @@ class EmbedSigner:
|
|
|
53
53
|
self._default_provider = key_provider
|
|
54
54
|
self._default_provider_name = key_provider.__class__.__name__
|
|
55
55
|
elif key_provider_name is not None:
|
|
56
|
-
self._default_provider = self._instantiate_provider(
|
|
56
|
+
self._default_provider = self._instantiate_provider(
|
|
57
|
+
key_provider_name
|
|
58
|
+
)
|
|
57
59
|
self._default_provider_name = key_provider_name
|
|
58
60
|
|
|
59
61
|
provider_for_signer = self._default_provider
|
|
@@ -78,7 +80,9 @@ class EmbedSigner:
|
|
|
78
80
|
Exception
|
|
79
81
|
): # pragma: no cover - defensive against broken entry points
|
|
80
82
|
continue
|
|
81
|
-
if not inspect.isclass(provider_cls) and not callable(
|
|
83
|
+
if not inspect.isclass(provider_cls) and not callable(
|
|
84
|
+
provider_cls
|
|
85
|
+
):
|
|
82
86
|
continue
|
|
83
87
|
factories[entry.name] = provider_cls
|
|
84
88
|
return factories
|
|
@@ -104,9 +108,16 @@ class EmbedSigner:
|
|
|
104
108
|
if self._default_provider_name is not None:
|
|
105
109
|
return self._instantiate_provider(self._default_provider_name)
|
|
106
110
|
raise ValueError(
|
|
107
|
-
|
|
111
|
+
(
|
|
112
|
+
"No default key provider configured; include a provider "
|
|
113
|
+
"name "
|
|
114
|
+
"in the key reference."
|
|
115
|
+
)
|
|
108
116
|
)
|
|
109
|
-
if
|
|
117
|
+
if (
|
|
118
|
+
name == self._default_provider_name
|
|
119
|
+
and self._default_provider is not None
|
|
120
|
+
):
|
|
110
121
|
return self._default_provider
|
|
111
122
|
return self._instantiate_provider(name)
|
|
112
123
|
|
|
@@ -142,7 +153,9 @@ class EmbedSigner:
|
|
|
142
153
|
) from exc
|
|
143
154
|
if not kid:
|
|
144
155
|
raise ValueError("Key reference must include a key identifier")
|
|
145
|
-
return _ParsedKeyRef(
|
|
156
|
+
return _ParsedKeyRef(
|
|
157
|
+
provider=provider_name, kid=kid, version=version_number
|
|
158
|
+
)
|
|
146
159
|
|
|
147
160
|
async def _resolve_key(
|
|
148
161
|
self, key: KeyRef | Mapping[str, Any] | str
|
|
@@ -155,10 +168,14 @@ class EmbedSigner:
|
|
|
155
168
|
ref = self._parse_key_reference(key)
|
|
156
169
|
provider = self._get_provider(ref.provider)
|
|
157
170
|
try:
|
|
158
|
-
resolved = await provider.get_key_by_ref(
|
|
171
|
+
resolved = await provider.get_key_by_ref(
|
|
172
|
+
ref.kid, include_secret=True
|
|
173
|
+
)
|
|
159
174
|
except NotImplementedError:
|
|
160
175
|
resolved = None
|
|
161
|
-
except
|
|
176
|
+
except (
|
|
177
|
+
AttributeError
|
|
178
|
+
): # pragma: no cover - provider without method
|
|
162
179
|
resolved = None
|
|
163
180
|
if resolved is None:
|
|
164
181
|
resolved = await provider.get_key(
|
|
@@ -166,7 +183,10 @@ class EmbedSigner:
|
|
|
166
183
|
)
|
|
167
184
|
return resolved
|
|
168
185
|
raise TypeError(
|
|
169
|
-
|
|
186
|
+
(
|
|
187
|
+
"key must be a Mapping, KeyRef, or string reference "
|
|
188
|
+
"understood by EmbedSigner"
|
|
189
|
+
)
|
|
170
190
|
)
|
|
171
191
|
|
|
172
192
|
# ------------------------------------------------------------------
|
|
@@ -178,13 +198,17 @@ class EmbedSigner:
|
|
|
178
198
|
ref = str(path) if path is not None else None
|
|
179
199
|
return self._embedder.embed(data, xmp_xml, ref)
|
|
180
200
|
|
|
181
|
-
def read_xmp(
|
|
201
|
+
def read_xmp(
|
|
202
|
+
self, data: bytes, *, path: str | Path | None = None
|
|
203
|
+
) -> str | None:
|
|
182
204
|
"""Read XMP metadata from *data* using the configured handlers."""
|
|
183
205
|
|
|
184
206
|
ref = str(path) if path is not None else None
|
|
185
207
|
return self._embedder.read(data, ref)
|
|
186
208
|
|
|
187
|
-
def remove_xmp(
|
|
209
|
+
def remove_xmp(
|
|
210
|
+
self, data: bytes, *, path: str | Path | None = None
|
|
211
|
+
) -> bytes:
|
|
188
212
|
"""Remove XMP metadata from *data* using the configured handlers."""
|
|
189
213
|
|
|
190
214
|
ref = str(path) if path is not None else None
|
|
@@ -201,7 +225,9 @@ class EmbedSigner:
|
|
|
201
225
|
"""Embed XMP metadata directly into a file on disk."""
|
|
202
226
|
|
|
203
227
|
file_path = Path(path)
|
|
204
|
-
updated = self.embed_bytes(
|
|
228
|
+
updated = self.embed_bytes(
|
|
229
|
+
file_path.read_bytes(), xmp_xml, path=file_path
|
|
230
|
+
)
|
|
205
231
|
target = Path(output) if output is not None else file_path
|
|
206
232
|
if write_back:
|
|
207
233
|
target.write_bytes(updated)
|
|
@@ -220,7 +246,9 @@ class EmbedSigner:
|
|
|
220
246
|
write_back: bool = False,
|
|
221
247
|
output: str | Path | None = None,
|
|
222
248
|
) -> bytes:
|
|
223
|
-
"""
|
|
249
|
+
"""
|
|
250
|
+
Remove XMP metadata from a file, optionally persisting the result.
|
|
251
|
+
"""
|
|
224
252
|
|
|
225
253
|
file_path = Path(path)
|
|
226
254
|
updated = self.remove_xmp(file_path.read_bytes(), path=file_path)
|
|
@@ -240,7 +268,9 @@ class EmbedSigner:
|
|
|
240
268
|
alg: Optional[str] = None,
|
|
241
269
|
signer_opts: Optional[Mapping[str, Any]] = None,
|
|
242
270
|
) -> Sequence[Signature]:
|
|
243
|
-
"""
|
|
271
|
+
"""
|
|
272
|
+
Produce signatures for *payload* using the requested signer format.
|
|
273
|
+
"""
|
|
244
274
|
|
|
245
275
|
resolved_key = await self._resolve_key(key)
|
|
246
276
|
opts: dict[str, Any] = dict(signer_opts or {})
|
|
@@ -286,7 +316,9 @@ class EmbedSigner:
|
|
|
286
316
|
signer_opts: Optional[Mapping[str, Any]] = None,
|
|
287
317
|
write_back: bool = False,
|
|
288
318
|
) -> tuple[bytes, Sequence[Signature]]:
|
|
289
|
-
"""
|
|
319
|
+
"""
|
|
320
|
+
Embed metadata into *path* and optionally persist the signed bytes.
|
|
321
|
+
"""
|
|
290
322
|
|
|
291
323
|
file_path = Path(path)
|
|
292
324
|
embedded, signatures = await self.embed_and_sign_bytes(
|
|
@@ -336,6 +368,7 @@ class EmbedSigner:
|
|
|
336
368
|
)
|
|
337
369
|
|
|
338
370
|
def supported_signers(self) -> Sequence[str]:
|
|
339
|
-
"""Expose signer formats advertised by the underlying
|
|
371
|
+
"""Expose signer formats advertised by the underlying
|
|
372
|
+
:class:`MediaSigner`."""
|
|
340
373
|
|
|
341
374
|
return tuple(self._signer.supported_formats())
|
|
@@ -30,14 +30,18 @@ def _resolve_key_argument(args: argparse.Namespace) -> Any:
|
|
|
30
30
|
if value is not None
|
|
31
31
|
]
|
|
32
32
|
if len(provided) != 1:
|
|
33
|
-
raise SystemExit(
|
|
33
|
+
raise SystemExit(
|
|
34
|
+
"Provide exactly one of --key-ref, --key-json, or --key-file."
|
|
35
|
+
)
|
|
34
36
|
if args.key_ref:
|
|
35
37
|
return args.key_ref
|
|
36
38
|
if args.key_json:
|
|
37
39
|
try:
|
|
38
40
|
return json.loads(args.key_json)
|
|
39
41
|
except json.JSONDecodeError as exc: # pragma: no cover - defensive
|
|
40
|
-
raise SystemExit(
|
|
42
|
+
raise SystemExit(
|
|
43
|
+
f"Invalid JSON passed to --key-json: {exc}"
|
|
44
|
+
) from exc
|
|
41
45
|
if args.key_file:
|
|
42
46
|
return json.loads(Path(args.key_file).read_text(encoding="utf-8"))
|
|
43
47
|
raise SystemExit("A key reference or JSON description must be supplied.")
|
|
@@ -120,7 +124,9 @@ def _command_sign(args: argparse.Namespace) -> int:
|
|
|
120
124
|
for token in args.option:
|
|
121
125
|
name, _, value = token.partition("=")
|
|
122
126
|
if not name:
|
|
123
|
-
raise SystemExit(
|
|
127
|
+
raise SystemExit(
|
|
128
|
+
"Signer options must use the form name=value."
|
|
129
|
+
)
|
|
124
130
|
opts[name] = value
|
|
125
131
|
signatures = asyncio.run(
|
|
126
132
|
_async_sign(
|
|
@@ -180,7 +186,9 @@ def _command_embed_sign(args: argparse.Namespace) -> int:
|
|
|
180
186
|
for token in args.option:
|
|
181
187
|
name, _, value = token.partition("=")
|
|
182
188
|
if not name:
|
|
183
|
-
raise SystemExit(
|
|
189
|
+
raise SystemExit(
|
|
190
|
+
"Signer options must use the form name=value."
|
|
191
|
+
)
|
|
184
192
|
opts[name] = value
|
|
185
193
|
output_path = Path(args.output) if args.output else None
|
|
186
194
|
signatures = asyncio.run(
|
|
@@ -206,7 +214,9 @@ def _command_embed_sign(args: argparse.Namespace) -> int:
|
|
|
206
214
|
|
|
207
215
|
|
|
208
216
|
def build_parser() -> argparse.ArgumentParser:
|
|
209
|
-
parser = argparse.ArgumentParser(
|
|
217
|
+
parser = argparse.ArgumentParser(
|
|
218
|
+
description="Embed XMP metadata and sign media."
|
|
219
|
+
)
|
|
210
220
|
parser.add_argument(
|
|
211
221
|
"--key-provider",
|
|
212
222
|
dest="key_provider",
|
|
@@ -217,11 +227,17 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
217
227
|
embed_parser = subparsers.add_parser("embed", help="Embed XMP into a file")
|
|
218
228
|
embed_parser.add_argument("input", help="Path to the media file")
|
|
219
229
|
embed_parser.add_argument("--xmp", help="Inline XMP XML payload")
|
|
220
|
-
embed_parser.add_argument(
|
|
221
|
-
|
|
230
|
+
embed_parser.add_argument(
|
|
231
|
+
"--xmp-file", help="Read the XMP payload from a file"
|
|
232
|
+
)
|
|
233
|
+
embed_parser.add_argument(
|
|
234
|
+
"--output", help="Write the embedded media to this path"
|
|
235
|
+
)
|
|
222
236
|
embed_parser.set_defaults(func=_command_embed)
|
|
223
237
|
|
|
224
|
-
read_parser = subparsers.add_parser(
|
|
238
|
+
read_parser = subparsers.add_parser(
|
|
239
|
+
"read", help="Read XMP metadata from a file"
|
|
240
|
+
)
|
|
225
241
|
read_parser.add_argument("input", help="Path to the media file")
|
|
226
242
|
read_parser.set_defaults(func=_command_read)
|
|
227
243
|
|
|
@@ -229,13 +245,21 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
229
245
|
"remove", help="Strip XMP metadata from a file"
|
|
230
246
|
)
|
|
231
247
|
remove_parser.add_argument("input", help="Path to the media file")
|
|
232
|
-
remove_parser.add_argument(
|
|
248
|
+
remove_parser.add_argument(
|
|
249
|
+
"--output", help="Write the stripped media to this path"
|
|
250
|
+
)
|
|
233
251
|
remove_parser.set_defaults(func=_command_remove)
|
|
234
252
|
|
|
235
|
-
sign_parser = subparsers.add_parser(
|
|
253
|
+
sign_parser = subparsers.add_parser(
|
|
254
|
+
"sign", help="Generate signatures for a file"
|
|
255
|
+
)
|
|
236
256
|
sign_parser.add_argument("input", help="Path to the media file")
|
|
237
|
-
sign_parser.add_argument(
|
|
238
|
-
|
|
257
|
+
sign_parser.add_argument(
|
|
258
|
+
"--format", required=True, help="MediaSigner format name"
|
|
259
|
+
)
|
|
260
|
+
sign_parser.add_argument(
|
|
261
|
+
"--key-ref", dest="key_ref", help="Key reference string"
|
|
262
|
+
)
|
|
239
263
|
sign_parser.add_argument(
|
|
240
264
|
"--key-json",
|
|
241
265
|
dest="key_json",
|
|
@@ -291,7 +315,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
291
315
|
action="append",
|
|
292
316
|
help="Additional signer option in the form name=value",
|
|
293
317
|
)
|
|
294
|
-
embed_sign_parser.add_argument(
|
|
318
|
+
embed_sign_parser.add_argument(
|
|
319
|
+
"--alg", help="Explicit algorithm identifier"
|
|
320
|
+
)
|
|
295
321
|
embed_sign_parser.add_argument(
|
|
296
322
|
"--detached",
|
|
297
323
|
action="store_true",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: EmbeddedSigner
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0.dev1
|
|
4
4
|
Summary: Embed XMP metadata and sign media assets using Swarmauri plugins.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -111,7 +111,8 @@ Description-Content-Type: text/markdown
|
|
|
111
111
|
<img src="https://img.shields.io/pypi/l/EmbeddedSigner" alt="PyPI - License"/></a>
|
|
112
112
|
<a href="https://pypi.org/project/EmbeddedSigner/">
|
|
113
113
|
<img src="https://img.shields.io/pypi/v/EmbeddedSigner?label=EmbeddedSigner&color=green" alt="PyPI - EmbeddedSigner"/></a>
|
|
114
|
-
|
|
114
|
+
<a href="https://discord.gg/N4UpBuQv8T">
|
|
115
|
+
<img src="https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white" alt="Discord"/></a></p>
|
|
115
116
|
|
|
116
117
|
# Embed metadata into a file and write it back in place.
|
|
117
118
|
signer.embed_file("image.png", xmp_xml)
|
|
@@ -193,3 +194,5 @@ embedded-signer embed-sign example.png \
|
|
|
193
194
|
EmbeddedSigner is released under the Apache 2.0 License. See the
|
|
194
195
|
[LICENSE](LICENSE) file for details.
|
|
195
196
|
|
|
197
|
+
|
|
198
|
+
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
<img src="https://img.shields.io/pypi/l/EmbeddedSigner" alt="PyPI - License"/></a>
|
|
12
12
|
<a href="https://pypi.org/project/EmbeddedSigner/">
|
|
13
13
|
<img src="https://img.shields.io/pypi/v/EmbeddedSigner?label=EmbeddedSigner&color=green" alt="PyPI - EmbeddedSigner"/></a>
|
|
14
|
-
|
|
14
|
+
<a href="https://discord.gg/N4UpBuQv8T">
|
|
15
|
+
<img src="https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white" alt="Discord"/></a></p>
|
|
15
16
|
|
|
16
17
|
# Embed metadata into a file and write it back in place.
|
|
17
18
|
signer.embed_file("image.png", xmp_xml)
|
|
@@ -92,3 +93,5 @@ embedded-signer embed-sign example.png \
|
|
|
92
93
|
|
|
93
94
|
EmbeddedSigner is released under the Apache 2.0 License. See the
|
|
94
95
|
[LICENSE](LICENSE) file for details.
|
|
96
|
+
|
|
97
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "EmbeddedSigner"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.11.0.dev1"
|
|
4
4
|
description = "Embed XMP metadata and sign media assets using Swarmauri plugins."
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
readme = "README.md"
|
|
@@ -175,7 +175,7 @@ dev = [
|
|
|
175
175
|
]
|
|
176
176
|
|
|
177
177
|
[tool.pytest.ini_options]
|
|
178
|
-
addopts = "--pylicense-package=EmbeddedSigner --pylicense-mode=parameterized --pylicense-accept-deps=cffi,
|
|
178
|
+
addopts = "--pylicense-package=EmbeddedSigner --pylicense-mode=parameterized --pylicense-accept-deps=cffi,standard-imghdr"
|
|
179
179
|
|
|
180
180
|
[tool.poetry]
|
|
181
181
|
packages = [
|
|
File without changes
|
|
File without changes
|