pith-jpeg 0.1.1__py3-none-win_amd64.whl
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.
- pith_jpeg/__init__.py +233 -0
- pith_jpeg/pith_jpeg.dll +0 -0
- pith_jpeg-0.1.1.dist-info/METADATA +137 -0
- pith_jpeg-0.1.1.dist-info/RECORD +6 -0
- pith_jpeg-0.1.1.dist-info/WHEEL +5 -0
- pith_jpeg-0.1.1.dist-info/top_level.txt +1 -0
pith_jpeg/__init__.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 pith-hash
|
|
3
|
+
"""pith-jpeg SDK: JPEG decoding and kernel replay through ctypes.
|
|
4
|
+
|
|
5
|
+
Decodes a native JPEG (baseline SOF0/SOF1 and progressive SOF2,
|
|
6
|
+
byte-exact with libjpeg ``-dct int -nosmooth``) through the Rust
|
|
7
|
+
cdylib. The cdylib computes the ``fnv1a64`` digest of the decoded
|
|
8
|
+
sample bytes internally, so this package stays zero-dependency: one
|
|
9
|
+
call per fixture returns the exact digest the ``reference.json``
|
|
10
|
+
``digests`` section pins. The four numeric kernels the ``vectors``
|
|
11
|
+
section pins (de-zigzag, dequantize, the fixed-point islow IDCT and
|
|
12
|
+
the orthonormal ``f64`` oracle) replay through the same boundary over
|
|
13
|
+
little-endian ``f64`` bit-pattern buffers.
|
|
14
|
+
|
|
15
|
+
The cdylib is located through the suite's discovery chain:
|
|
16
|
+
|
|
17
|
+
1. ``PITH_CDYLIB`` — an explicit cdylib *file* path;
|
|
18
|
+
2. ``PITH_CDYLIB_DIR`` — a *directory* scanned for the cdylib names
|
|
19
|
+
(the CD pipeline points this at ``target/release``);
|
|
20
|
+
3. the package directory itself (the built wheel ships the cdylib as
|
|
21
|
+
package data);
|
|
22
|
+
4. ``<repo root>/target/release`` — the repository working-tree layout,
|
|
23
|
+
so a source checkout runs against a local cargo build with no
|
|
24
|
+
configuration.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import ctypes
|
|
30
|
+
import os
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"FfiError",
|
|
35
|
+
"LibraryNotFoundError",
|
|
36
|
+
"find_cdylib",
|
|
37
|
+
"decode_digest",
|
|
38
|
+
"channels",
|
|
39
|
+
"dequant_zigzag",
|
|
40
|
+
"dequant",
|
|
41
|
+
"idct_islow",
|
|
42
|
+
"idct_oracle",
|
|
43
|
+
"STATUS_OK",
|
|
44
|
+
"STATUS_INVALID",
|
|
45
|
+
"STATUS_REJECTED",
|
|
46
|
+
"LAYOUT_GRAY8",
|
|
47
|
+
"LAYOUT_RGB8",
|
|
48
|
+
"LAYOUT_RGBA8",
|
|
49
|
+
"BLOCK",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
#: Status: success.
|
|
53
|
+
STATUS_OK = 0
|
|
54
|
+
#: Status: a caller argument is invalid — null pointer, unknown layout,
|
|
55
|
+
#: wrong buffer length.
|
|
56
|
+
STATUS_INVALID = -1
|
|
57
|
+
#: Status: the decoder refused the input (malformed JPEG), or the
|
|
58
|
+
#: requested conversion is not offered (RGB→gray).
|
|
59
|
+
STATUS_REJECTED = -2
|
|
60
|
+
|
|
61
|
+
#: Output layout: 8-bit grayscale (the ``pith-image`` code).
|
|
62
|
+
LAYOUT_GRAY8 = 0
|
|
63
|
+
#: Output layout: 8-bit RGB, channel-interleaved (the ``pith-image``
|
|
64
|
+
#: code).
|
|
65
|
+
LAYOUT_RGB8 = 2
|
|
66
|
+
#: Output layout: 8-bit RGBA, channel-interleaved (the ``pith-image``
|
|
67
|
+
#: code).
|
|
68
|
+
LAYOUT_RGBA8 = 4
|
|
69
|
+
|
|
70
|
+
#: One 8×8 kernel block: 64 ``f64`` values.
|
|
71
|
+
BLOCK = 64
|
|
72
|
+
|
|
73
|
+
#: Every cdylib file name cargo may drop into the build directory, per
|
|
74
|
+
#: platform (windows / linux / macOS).
|
|
75
|
+
CDYLIB_NAMES = ("pith_jpeg.dll", "libpith_jpeg.so", "libpith_jpeg.dylib")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class LibraryNotFoundError(OSError):
|
|
79
|
+
"""No cdylib was found through the discovery chain."""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class FfiError(Exception):
|
|
83
|
+
"""A non-zero status code came back from the cdylib."""
|
|
84
|
+
|
|
85
|
+
def __init__(self, op: str, status: int) -> None:
|
|
86
|
+
detail = {
|
|
87
|
+
STATUS_INVALID: "invalid argument",
|
|
88
|
+
STATUS_REJECTED: "input rejected",
|
|
89
|
+
}.get(status, "unknown failure")
|
|
90
|
+
super().__init__(f"{op} failed: {detail} (status {status})")
|
|
91
|
+
#: The raw status code the FFI returned.
|
|
92
|
+
self.status = status
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def find_cdylib() -> Path:
|
|
96
|
+
"""Locates the cdylib through the suite's discovery chain."""
|
|
97
|
+
explicit = os.environ.get("PITH_CDYLIB")
|
|
98
|
+
if explicit:
|
|
99
|
+
p = Path(explicit)
|
|
100
|
+
if p.is_file():
|
|
101
|
+
return p
|
|
102
|
+
env_dir = os.environ.get("PITH_CDYLIB_DIR")
|
|
103
|
+
candidates: list[Path] = []
|
|
104
|
+
if env_dir:
|
|
105
|
+
env_dir_path = Path(env_dir)
|
|
106
|
+
candidates.append(env_dir_path)
|
|
107
|
+
if not env_dir_path.is_absolute():
|
|
108
|
+
# CD and local runs invoke tools from the repository root or
|
|
109
|
+
# from sdk/<lang>; resolve the env value against both.
|
|
110
|
+
candidates.append(Path.cwd() / env_dir_path)
|
|
111
|
+
candidates.append(Path(__file__).resolve().parents[3] / env_dir_path)
|
|
112
|
+
candidates.append(Path(__file__).resolve().parent) # packaged wheel
|
|
113
|
+
candidates.append(Path(__file__).resolve().parents[3] / "target" / "release")
|
|
114
|
+
for directory in candidates:
|
|
115
|
+
for name in CDYLIB_NAMES:
|
|
116
|
+
p = directory / name
|
|
117
|
+
if p.is_file():
|
|
118
|
+
return p
|
|
119
|
+
raise LibraryNotFoundError(
|
|
120
|
+
"no pith-jpeg cdylib found (searched PITH_CDYLIB, PITH_CDYLIB_DIR, "
|
|
121
|
+
"the package directory and <repo>/target/release); "
|
|
122
|
+
"run `cargo build --release` first"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_lib: ctypes.CDLL | None = None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _load() -> ctypes.CDLL:
|
|
130
|
+
global _lib
|
|
131
|
+
if _lib is None:
|
|
132
|
+
lib = ctypes.CDLL(str(find_cdylib()))
|
|
133
|
+
lib.pith_jpeg_decode.argtypes = [
|
|
134
|
+
ctypes.c_void_p, # data
|
|
135
|
+
ctypes.c_size_t, # len
|
|
136
|
+
ctypes.c_uint32, # layout
|
|
137
|
+
ctypes.POINTER(ctypes.c_uint64), # out digest
|
|
138
|
+
]
|
|
139
|
+
lib.pith_jpeg_decode.restype = ctypes.c_int32
|
|
140
|
+
lib.pith_jpeg_channels.argtypes = [
|
|
141
|
+
ctypes.c_void_p, # data
|
|
142
|
+
ctypes.c_size_t, # len
|
|
143
|
+
ctypes.POINTER(ctypes.c_uint32), # out channels
|
|
144
|
+
]
|
|
145
|
+
lib.pith_jpeg_channels.restype = ctypes.c_int32
|
|
146
|
+
for op in (
|
|
147
|
+
"pith_jpeg_dequant_zigzag",
|
|
148
|
+
"pith_jpeg_dequant",
|
|
149
|
+
"pith_jpeg_idct_islow",
|
|
150
|
+
"pith_jpeg_idct_oracle",
|
|
151
|
+
):
|
|
152
|
+
fn = getattr(lib, op)
|
|
153
|
+
fn.argtypes = [
|
|
154
|
+
ctypes.c_void_p, # input buffer (LE f64 lanes)
|
|
155
|
+
ctypes.c_size_t, # input length in bytes
|
|
156
|
+
ctypes.c_void_p, # output buffer (LE f64 lanes)
|
|
157
|
+
ctypes.c_size_t, # output length in bytes
|
|
158
|
+
]
|
|
159
|
+
fn.restype = ctypes.c_int32
|
|
160
|
+
_lib = lib
|
|
161
|
+
return _lib
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def decode_digest(data: bytes, layout: int) -> int:
|
|
165
|
+
"""Decodes a JPEG and returns the ``fnv1a64`` digest of the sample
|
|
166
|
+
bytes in ``layout`` encoding: row-major, channel-interleaved — the
|
|
167
|
+
exact value ``reference.json`` pins for the native layout.
|
|
168
|
+
|
|
169
|
+
Raises :class:`FfiError` with ``status == STATUS_REJECTED`` for
|
|
170
|
+
malformed input and for the refused RGB→gray conversion, and with
|
|
171
|
+
``status == STATUS_INVALID`` for an unknown layout code.
|
|
172
|
+
"""
|
|
173
|
+
digest = ctypes.c_uint64()
|
|
174
|
+
status = _load().pith_jpeg_decode(data, len(data), layout, ctypes.byref(digest))
|
|
175
|
+
if status != STATUS_OK:
|
|
176
|
+
raise FfiError("pith_jpeg_decode", status)
|
|
177
|
+
return digest.value
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def channels(data: bytes) -> int:
|
|
181
|
+
"""Returns the frame's native channel count (1 or 3) — the digest-
|
|
182
|
+
exact layout is :data:`LAYOUT_GRAY8` for 1 and :data:`LAYOUT_RGB8`
|
|
183
|
+
for 3."""
|
|
184
|
+
out = ctypes.c_uint32()
|
|
185
|
+
status = _load().pith_jpeg_channels(data, len(data), ctypes.byref(out))
|
|
186
|
+
if status != STATUS_OK:
|
|
187
|
+
raise FfiError("pith_jpeg_channels", status)
|
|
188
|
+
return out.value
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _replay(op: str, values: list[float], out_lanes: int) -> list[float]:
|
|
192
|
+
"""Feeds ``values`` to one kernel op over little-endian ``f64``
|
|
193
|
+
bit-pattern buffers and returns the decoded output lanes."""
|
|
194
|
+
import struct
|
|
195
|
+
|
|
196
|
+
packed = b"".join(struct.pack("<d", v) for v in values)
|
|
197
|
+
out = (ctypes.c_ubyte * (out_lanes * 8))()
|
|
198
|
+
status = getattr(_load(), op)(
|
|
199
|
+
packed, len(packed), out, len(out)
|
|
200
|
+
)
|
|
201
|
+
if status != STATUS_OK:
|
|
202
|
+
raise FfiError(op, status)
|
|
203
|
+
raw = bytes(out)
|
|
204
|
+
return list(struct.unpack(f"<{out_lanes}d", raw))
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def dequant_zigzag(payload: list[float]) -> list[float]:
|
|
208
|
+
"""Replays the ``dequant.zigzag.8x8`` kernel: 64 DQT-payload
|
|
209
|
+
values in scan order in, the same values in natural (row-major)
|
|
210
|
+
order out (``natural[ZIGZAG[k]] = payload[k]``)."""
|
|
211
|
+
return _replay("pith_jpeg_dequant_zigzag", payload, BLOCK)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def dequant(coefs: list[float], qt: list[float]) -> list[float]:
|
|
215
|
+
"""Replays the ``dequant.8x8`` kernel: 64 natural-order
|
|
216
|
+
coefficients and 64 quantizer values in, the 64 element-wise
|
|
217
|
+
products out."""
|
|
218
|
+
return _replay("pith_jpeg_dequant", coefs + qt, BLOCK)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def idct_islow(coefs: list[float], qt: list[float]) -> list[float]:
|
|
222
|
+
"""Replays the ``idct.islow.8x8`` kernel: the same input layout as
|
|
223
|
+
:func:`dequant`, the 64 ``u8`` samples of the fixed-point islow
|
|
224
|
+
transcription out as ``f64`` lanes."""
|
|
225
|
+
return _replay("pith_jpeg_idct_islow", coefs + qt, BLOCK)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def idct_oracle(block: list[float]) -> list[float]:
|
|
229
|
+
"""Replays the ``idct.oracle.8x8`` kernel: 64 dequantized
|
|
230
|
+
coefficients in, the raw orthonormal ``f64`` DCT-III oracle result
|
|
231
|
+
out. Platform-``libm``-shaped: compare within the recorded
|
|
232
|
+
tolerance, never bit-for-bit."""
|
|
233
|
+
return _replay("pith_jpeg_idct_oracle", block, BLOCK)
|
pith_jpeg/pith_jpeg.dll
ADDED
|
Binary file
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pith-jpeg
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: pith-jpeg SDK: JPEG decoding and kernel replay through the pith-hash Rust cdylib
|
|
5
|
+
Author: pith-hash
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/pith-hash/pith-jpeg
|
|
8
|
+
Keywords: jpeg,phash,perceptual-hash,pith-hash,ffi,ctypes
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
16
|
+
Classifier: Topic :: Security :: Cryptography
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<img src="https://pith-jpeg.n24q02m.com/logo.svg" alt="pith-jpeg" width="120">
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<h1 align="center">pith-jpeg</h1>
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<strong>pith jpeg lane: baseline and progressive JPEG decoding, byte-exact with libjpeg islow (zero-dep Rust)</strong>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<a href="https://github.com/pith-hash/pith-jpeg/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/pith-hash/pith-jpeg/actions/workflows/ci.yml/badge.svg"></a>
|
|
32
|
+
<a href="https://github.com/pith-hash/pith-jpeg/actions/workflows/cd.yml"><img alt="CD" src="https://github.com/pith-hash/pith-jpeg/actions/workflows/cd.yml/badge.svg"></a>
|
|
33
|
+
<a href="https://github.com/pith-hash/pith-jpeg/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/pith-hash/pith-jpeg?display_name=tag&sort=semver"></a>
|
|
34
|
+
<a href="https://github.com/n24q02m/better-semantic-release"><img alt="semantic-release" src="https://img.shields.io/badge/semantic--release-e10079?logo=semantic-release&logoColor=white"></a>
|
|
35
|
+
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg"></a>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<a href="#install">Install</a> ·
|
|
40
|
+
<a href="#quick-start">Quick start</a> ·
|
|
41
|
+
<a href="#the-pith-suite-contract">Suite contract</a>
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
<!-- BEGIN: AUTO-GENERATED-CROSS-PROMO -->
|
|
45
|
+
<!-- END: AUTO-GENERATED-CROSS-PROMO -->
|
|
46
|
+
|
|
47
|
+
## The pith suite contract
|
|
48
|
+
|
|
49
|
+
pith-jpeg is part of the **pith** suite (pith-hash). Every suite repository
|
|
50
|
+
follows the same rules; CI enforces them mechanically:
|
|
51
|
+
|
|
52
|
+
- **Naming**: a library is always `pith-<domain>` (`pith-image`, `pith-audio`,
|
|
53
|
+
`pith-zip`, ...). The curator/repository of repositories is the bare
|
|
54
|
+
`pith-hash`. Never invent a second naming scheme inside the suite.
|
|
55
|
+
- **Version pinning**: cross-library dependencies pin `~0.1` (e.g.
|
|
56
|
+
`pith-image = { version = "~0.1", path = "../pith-image" }`). The whole suite
|
|
57
|
+
moves together inside 0.1.x; breaking changes require a suite-wide version
|
|
58
|
+
bump, never a silent minor drift.
|
|
59
|
+
- **Zero third-party dependencies**: every crate depends only on other
|
|
60
|
+
`pith-*` crates plus `std`. `scripts/check-zero-deps.py` (run in CI) fails
|
|
61
|
+
the build on any other crate, for normal, build and dev dependencies alike.
|
|
62
|
+
- **No unsafe**: every crate root carries `#![forbid(unsafe_code)]`.
|
|
63
|
+
- **Hex-exact vectors**: `reference.json` at the repo root is the
|
|
64
|
+
cross-language source of truth. The `gen-reference` binary regenerates it;
|
|
65
|
+
CI verifies the committed copy is current (`gen-reference verify`), and CD
|
|
66
|
+
ships the regenerated file with every SDK artifact. Python, Node and Go SDKs
|
|
67
|
+
MUST test against the same bytes.
|
|
68
|
+
|
|
69
|
+
## Repository layout
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
crates/ one published crate per suite lib (pith-<domain>)
|
|
73
|
+
tools/gen-reference the vector generator binary (bin name: gen-reference)
|
|
74
|
+
sdk/python ctypes wheel; build backend reads PITH_CDYLIB_DIR
|
|
75
|
+
sdk/node koffi-based package; prebuilds/<os-arch>/ carry the cdylib
|
|
76
|
+
sdk/go cgo binding; go.mod carries the module's cgo flags
|
|
77
|
+
fuzz/corpus fuzz inputs, replayed by tests/fuzz_corpus.rs (parser crates)
|
|
78
|
+
reference.json hex-exact cross-SDK test vectors
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Overview
|
|
82
|
+
|
|
83
|
+
JPEG decoding per ITU T.81: baseline (SOF0), extended sequential (SOF1)
|
|
84
|
+
and progressive (SOF2) Huffman-coded frames — spectral selection,
|
|
85
|
+
successive approximation and EOBRUN included — with restart markers,
|
|
86
|
+
4:4:4/4:2:2/4:4:0/4:2:0 and other integral sampling factors, and 1- or
|
|
87
|
+
3-component output as `pith_image::raster::Image` (`Gray`/`Rgb`, 8-bit).
|
|
88
|
+
|
|
89
|
+
The pixel pipeline is a byte-exact transcription of libjpeg-turbo's
|
|
90
|
+
`islow` integer IDCT, `fancy` triangle chroma upsampling and
|
|
91
|
+
fixed-point YCbCr→RGB, validated against Pillow 12.3.0 fixtures byte
|
|
92
|
+
for byte (see `tests/fixtures/PROVENANCE.md`). Arithmetic-coded,
|
|
93
|
+
lossless, differential, hierarchical, 12-bit and CMYK/YCCK streams are
|
|
94
|
+
refused with `Error::Unsupported` naming the coding process.
|
|
95
|
+
|
|
96
|
+
## Install
|
|
97
|
+
|
|
98
|
+
Rust (the core library):
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
cargo add pith-jpeg
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Python / Node / Go SDKs are published from the same cdylib on every release;
|
|
105
|
+
see the release assets or the package registries for the matching version.
|
|
106
|
+
|
|
107
|
+
## Quick start
|
|
108
|
+
|
|
109
|
+
Rust (the core library):
|
|
110
|
+
|
|
111
|
+
```rust
|
|
112
|
+
let img = pith_jpeg::decode(&bytes)?;
|
|
113
|
+
|
|
114
|
+
match img {
|
|
115
|
+
pith_jpeg::Jpeg::Gray(g) => println!("{}x{}", g.width(), g.height()),
|
|
116
|
+
pith_jpeg::Jpeg::Rgb(rgb) => println!("{}x{}", rgb.width(), rgb.height()),
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`decode` never panics on untrusted input: malformed structure surfaces
|
|
121
|
+
as `Error::Truncated` / `Error::BadValue`, refusals as
|
|
122
|
+
`Error::Unsupported`. Hex-exact cross-SDK vectors — decode digests for
|
|
123
|
+
every committed fixture plus the DCT/dequant numerics — live in
|
|
124
|
+
`reference.json` (regenerate with `cargo run --bin gen-reference`,
|
|
125
|
+
verify with `cargo run --bin gen-reference -- verify`).
|
|
126
|
+
|
|
127
|
+
## Contributing
|
|
128
|
+
|
|
129
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
130
|
+
|
|
131
|
+
## Security
|
|
132
|
+
|
|
133
|
+
See [SECURITY.md](SECURITY.md).
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
[MIT](LICENSE) © pith-hash
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
pith_jpeg/__init__.py,sha256=9x9V6F47bUciIiEaJ1z-R94WnWWaKURb12FPDGha3JY,8337
|
|
2
|
+
pith_jpeg/pith_jpeg.dll,sha256=6yvwQQZOu9j2oI0HrElBem67VfRtwfFdCqmfku6QkPU,184832
|
|
3
|
+
pith_jpeg-0.1.1.dist-info/METADATA,sha256=rBNaKHTpPvoADYK3PSAdWXb9apPdDrbddb1sc3Fe58I,5851
|
|
4
|
+
pith_jpeg-0.1.1.dist-info/WHEEL,sha256=Zx98gwb_dQKckJK3HEOVKnxxD52EfU_PXDG_usMJ2ng,98
|
|
5
|
+
pith_jpeg-0.1.1.dist-info/top_level.txt,sha256=kbWDcEQWIvue9olhdeuUxmjIDsNM4oFqmK4xpcm8gCc,10
|
|
6
|
+
pith_jpeg-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pith_jpeg
|