swarmauri_xmp_webp 0.1.1.dev32__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.
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: swarmauri_xmp_webp
|
|
3
|
+
Version: 0.1.1.dev32
|
|
4
|
+
Summary: WebP handler scaffold for embedding and extracting XMP packets in Swarmauri runtimes.
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: swarmauri,sdk,standards,xmp,webp
|
|
8
|
+
Author: Jacob Stewart
|
|
9
|
+
Author-email: jacob@swarmauri.com
|
|
10
|
+
Requires-Python: >=3.10,<3.13
|
|
11
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Requires-Dist: swarmauri_base
|
|
22
|
+
Requires-Dist: swarmauri_core
|
|
23
|
+
Project-URL: Homepage, https://github.com/swarmauri/swarmauri-sdk
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<a href="https://pypi.org/project/swarmauri_xmp_webp/">
|
|
31
|
+
<img src="https://img.shields.io/pypi/dm/swarmauri_xmp_webp" alt="PyPI - Downloads"/></a>
|
|
32
|
+
<a href="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_xmp_webp/">
|
|
33
|
+
<img alt="Hits" src="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_xmp_webp.svg"/></a>
|
|
34
|
+
<a href="https://pypi.org/project/swarmauri_xmp_webp/">
|
|
35
|
+
<img src="https://img.shields.io/pypi/pyversions/swarmauri_xmp_webp" alt="PyPI - Python Version"/></a>
|
|
36
|
+
<a href="https://pypi.org/project/swarmauri_xmp_webp/">
|
|
37
|
+
<img src="https://img.shields.io/pypi/l/swarmauri_xmp_webp" alt="PyPI - License"/></a>
|
|
38
|
+
<a href="https://pypi.org/project/swarmauri_xmp_webp/">
|
|
39
|
+
<img src="https://img.shields.io/pypi/v/swarmauri_xmp_webp?label=swarmauri_xmp_webp&color=green" alt="PyPI - swarmauri_xmp_webp"/></a>
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# swarmauri_xmp_webp
|
|
45
|
+
|
|
46
|
+
`swarmauri_xmp_webp` declares the `WebPXMP` handler scaffold for RIFF/WEBP containers and prepares the dynamic registry for a full implementation.
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **Forward compatible** – establishes class signatures today so future implementations slot straight into existing workflows.
|
|
51
|
+
- **Registry aligned** – inherits from `EmbedXmpBase`, enabling automatic discovery through Swarmauri's dynamic registry.
|
|
52
|
+
- **Clear contracts** – raises `NotImplementedError` for read/write/remove until the RIFF logic is complete.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# pip
|
|
58
|
+
pip install swarmauri_xmp_webp
|
|
59
|
+
|
|
60
|
+
# uv
|
|
61
|
+
uv add swarmauri_xmp_webp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Usage
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from swarmauri_xmp_webp import WebPXMP
|
|
68
|
+
|
|
69
|
+
handler = WebPXMP()
|
|
70
|
+
|
|
71
|
+
# Raises NotImplementedError until read/write/remove are implemented
|
|
72
|
+
try:
|
|
73
|
+
handler.read_xmp(b"RIFF....WEBP")
|
|
74
|
+
except NotImplementedError:
|
|
75
|
+
print("WebP XMP support is forthcoming.")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Why it works
|
|
79
|
+
|
|
80
|
+
- **Forward compatible** – declaring the handler today reserves the RIFF chunk namespace for future work.
|
|
81
|
+
- **Registry alignment** – subclassing `EmbedXmpBase` means discovery logic will work without code changes once implemented.
|
|
82
|
+
- **Clear contracts** – explicit `NotImplementedError` exceptions communicate the remaining work to contributors.
|
|
83
|
+
|
|
84
|
+
## Project Resources
|
|
85
|
+
|
|
86
|
+
- Source: <https://github.com/swarmauri/swarmauri-sdk>
|
|
87
|
+
- License: Apache 2.0
|
|
88
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
<p align="center">
|
|
5
|
+
<a href="https://pypi.org/project/swarmauri_xmp_webp/">
|
|
6
|
+
<img src="https://img.shields.io/pypi/dm/swarmauri_xmp_webp" alt="PyPI - Downloads"/></a>
|
|
7
|
+
<a href="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_xmp_webp/">
|
|
8
|
+
<img alt="Hits" src="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_xmp_webp.svg"/></a>
|
|
9
|
+
<a href="https://pypi.org/project/swarmauri_xmp_webp/">
|
|
10
|
+
<img src="https://img.shields.io/pypi/pyversions/swarmauri_xmp_webp" alt="PyPI - Python Version"/></a>
|
|
11
|
+
<a href="https://pypi.org/project/swarmauri_xmp_webp/">
|
|
12
|
+
<img src="https://img.shields.io/pypi/l/swarmauri_xmp_webp" alt="PyPI - License"/></a>
|
|
13
|
+
<a href="https://pypi.org/project/swarmauri_xmp_webp/">
|
|
14
|
+
<img src="https://img.shields.io/pypi/v/swarmauri_xmp_webp?label=swarmauri_xmp_webp&color=green" alt="PyPI - swarmauri_xmp_webp"/></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# swarmauri_xmp_webp
|
|
20
|
+
|
|
21
|
+
`swarmauri_xmp_webp` declares the `WebPXMP` handler scaffold for RIFF/WEBP containers and prepares the dynamic registry for a full implementation.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- **Forward compatible** – establishes class signatures today so future implementations slot straight into existing workflows.
|
|
26
|
+
- **Registry aligned** – inherits from `EmbedXmpBase`, enabling automatic discovery through Swarmauri's dynamic registry.
|
|
27
|
+
- **Clear contracts** – raises `NotImplementedError` for read/write/remove until the RIFF logic is complete.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# pip
|
|
33
|
+
pip install swarmauri_xmp_webp
|
|
34
|
+
|
|
35
|
+
# uv
|
|
36
|
+
uv add swarmauri_xmp_webp
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from swarmauri_xmp_webp import WebPXMP
|
|
43
|
+
|
|
44
|
+
handler = WebPXMP()
|
|
45
|
+
|
|
46
|
+
# Raises NotImplementedError until read/write/remove are implemented
|
|
47
|
+
try:
|
|
48
|
+
handler.read_xmp(b"RIFF....WEBP")
|
|
49
|
+
except NotImplementedError:
|
|
50
|
+
print("WebP XMP support is forthcoming.")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Why it works
|
|
54
|
+
|
|
55
|
+
- **Forward compatible** – declaring the handler today reserves the RIFF chunk namespace for future work.
|
|
56
|
+
- **Registry alignment** – subclassing `EmbedXmpBase` means discovery logic will work without code changes once implemented.
|
|
57
|
+
- **Clear contracts** – explicit `NotImplementedError` exceptions communicate the remaining work to contributors.
|
|
58
|
+
|
|
59
|
+
## Project Resources
|
|
60
|
+
|
|
61
|
+
- Source: <https://github.com/swarmauri/swarmauri-sdk>
|
|
62
|
+
- License: Apache 2.0
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "swarmauri_xmp_webp"
|
|
3
|
+
version = "0.1.1.dev32"
|
|
4
|
+
description = "WebP handler scaffold for embedding and extracting XMP packets in Swarmauri runtimes."
|
|
5
|
+
license = "Apache-2.0"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.10,<3.13"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"License :: OSI Approved :: Apache Software License",
|
|
10
|
+
"Natural Language :: English",
|
|
11
|
+
"Programming Language :: Python :: 3.10",
|
|
12
|
+
"Programming Language :: Python :: 3.11",
|
|
13
|
+
"Programming Language :: Python :: 3.12",
|
|
14
|
+
"Programming Language :: Python :: 3.13",
|
|
15
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
16
|
+
"Programming Language :: Python",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
]
|
|
20
|
+
authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"swarmauri_core",
|
|
23
|
+
"swarmauri_base",
|
|
24
|
+
]
|
|
25
|
+
keywords = [
|
|
26
|
+
'swarmauri',
|
|
27
|
+
'sdk',
|
|
28
|
+
'standards',
|
|
29
|
+
'xmp',
|
|
30
|
+
'webp',
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/swarmauri/swarmauri-sdk"
|
|
35
|
+
|
|
36
|
+
[project.entry-points.'swarmauri.xmp_handlers']
|
|
37
|
+
WebPXMP = "swarmauri_xmp_webp:WebPXMP"
|
|
38
|
+
|
|
39
|
+
[tool.uv.sources]
|
|
40
|
+
swarmauri_core = { workspace = true }
|
|
41
|
+
swarmauri_base = { workspace = true }
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["poetry-core>=1.0.0"]
|
|
45
|
+
build-backend = "poetry.core.masonry.api"
|
|
46
|
+
|
|
47
|
+
[dependency-groups]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=8.0",
|
|
50
|
+
"ruff>=0.9",
|
|
51
|
+
]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""WebP XMP handler implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import ClassVar, Iterator, Tuple
|
|
6
|
+
|
|
7
|
+
from swarmauri_base import register_type
|
|
8
|
+
from swarmauri_base.xmp import EmbedXmpBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@register_type(resource_type=EmbedXmpBase)
|
|
12
|
+
class WebPXMP(EmbedXmpBase):
|
|
13
|
+
"""Embed and extract XMP packets using WebP ``XMP `` chunks."""
|
|
14
|
+
|
|
15
|
+
SIGNATURE: ClassVar[bytes] = b"RIFF"
|
|
16
|
+
FORMAT: ClassVar[bytes] = b"WEBP"
|
|
17
|
+
XMP_CHUNK: ClassVar[bytes] = b"XMP "
|
|
18
|
+
|
|
19
|
+
def _validate(self, data: bytes) -> None:
|
|
20
|
+
if len(data) < 12 or not data.startswith(self.SIGNATURE):
|
|
21
|
+
raise ValueError("Not a WebP payload")
|
|
22
|
+
if data[8:12] != self.FORMAT:
|
|
23
|
+
raise ValueError("RIFF container is not WebP formatted")
|
|
24
|
+
|
|
25
|
+
def _iter_chunks(self, data: bytes) -> Iterator[Tuple[int, bytes, int, int]]:
|
|
26
|
+
self._validate(data)
|
|
27
|
+
pos = 12
|
|
28
|
+
end = len(data)
|
|
29
|
+
while pos + 8 <= end:
|
|
30
|
+
ctype = data[pos : pos + 4]
|
|
31
|
+
size = int.from_bytes(data[pos + 4 : pos + 8], "little")
|
|
32
|
+
data_start = pos + 8
|
|
33
|
+
data_end = data_start + size
|
|
34
|
+
if data_end > end:
|
|
35
|
+
raise ValueError("Truncated WebP chunk")
|
|
36
|
+
yield pos, ctype, data_start, size
|
|
37
|
+
pos = data_end + (size & 1)
|
|
38
|
+
if pos != end:
|
|
39
|
+
# Allow a single padding byte
|
|
40
|
+
if not (pos == end - 1 and data[-1] == 0):
|
|
41
|
+
raise ValueError("Unexpected trailer in WebP payload")
|
|
42
|
+
|
|
43
|
+
def supports(self, header: bytes, path: str) -> bool:
|
|
44
|
+
return path.lower().endswith(".webp") or (
|
|
45
|
+
header.startswith(self.SIGNATURE) and header[8:12] == self.FORMAT
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def read_xmp(self, data: bytes) -> str | None:
|
|
49
|
+
for _offset, ctype, data_off, data_len in self._iter_chunks(data):
|
|
50
|
+
if ctype != self.XMP_CHUNK:
|
|
51
|
+
continue
|
|
52
|
+
payload = data[data_off : data_off + data_len]
|
|
53
|
+
return payload.decode("utf-8", errors="ignore")
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def _chunk_bytes(self, ctype: bytes, payload: bytes) -> bytes:
|
|
57
|
+
chunk = bytearray()
|
|
58
|
+
chunk.extend(ctype)
|
|
59
|
+
chunk.extend(len(payload).to_bytes(4, "little"))
|
|
60
|
+
chunk.extend(payload)
|
|
61
|
+
if len(payload) & 1:
|
|
62
|
+
chunk.append(0)
|
|
63
|
+
return bytes(chunk)
|
|
64
|
+
|
|
65
|
+
def write_xmp(self, data: bytes, xmp_xml: str) -> bytes:
|
|
66
|
+
xmp_bytes = self._ensure_xml(xmp_xml)
|
|
67
|
+
self._validate(data)
|
|
68
|
+
chunks = []
|
|
69
|
+
for _offset, ctype, data_off, data_len in self._iter_chunks(data):
|
|
70
|
+
if ctype == self.XMP_CHUNK:
|
|
71
|
+
continue
|
|
72
|
+
payload = data[data_off : data_off + data_len]
|
|
73
|
+
chunks.append(self._chunk_bytes(ctype, payload))
|
|
74
|
+
chunks.append(self._chunk_bytes(self.XMP_CHUNK, xmp_bytes))
|
|
75
|
+
body = b"".join(chunks)
|
|
76
|
+
size = len(body) + len(self.FORMAT)
|
|
77
|
+
return self.SIGNATURE + size.to_bytes(4, "little") + self.FORMAT + body
|
|
78
|
+
|
|
79
|
+
def remove_xmp(self, data: bytes) -> bytes:
|
|
80
|
+
self._validate(data)
|
|
81
|
+
chunks = []
|
|
82
|
+
for _offset, ctype, data_off, data_len in self._iter_chunks(data):
|
|
83
|
+
if ctype == self.XMP_CHUNK:
|
|
84
|
+
continue
|
|
85
|
+
payload = data[data_off : data_off + data_len]
|
|
86
|
+
chunks.append(self._chunk_bytes(ctype, payload))
|
|
87
|
+
body = b"".join(chunks)
|
|
88
|
+
size = len(body) + len(self.FORMAT)
|
|
89
|
+
return self.SIGNATURE + size.to_bytes(4, "little") + self.FORMAT + body
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
__all__ = ["WebPXMP"]
|