ikichunk 0.2.0__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 (51) hide show
  1. ikichunk-0.2.0/LICENSE +21 -0
  2. ikichunk-0.2.0/PKG-INFO +615 -0
  3. ikichunk-0.2.0/README.MD +574 -0
  4. ikichunk-0.2.0/pyproject.toml +53 -0
  5. ikichunk-0.2.0/setup.cfg +4 -0
  6. ikichunk-0.2.0/src/ikichunk/__init__.py +8 -0
  7. ikichunk-0.2.0/src/ikichunk/automation/__init__.py +3 -0
  8. ikichunk-0.2.0/src/ikichunk/automation/tasks.py +5 -0
  9. ikichunk-0.2.0/src/ikichunk/cli.py +41 -0
  10. ikichunk-0.2.0/src/ikichunk/concurrency/__init__.py +3 -0
  11. ikichunk-0.2.0/src/ikichunk/concurrency/parallel.py +43 -0
  12. ikichunk-0.2.0/src/ikichunk/configuration/__init__.py +3 -0
  13. ikichunk-0.2.0/src/ikichunk/configuration/loader.py +43 -0
  14. ikichunk-0.2.0/src/ikichunk/exceptions.py +25 -0
  15. ikichunk-0.2.0/src/ikichunk/facade.py +167 -0
  16. ikichunk-0.2.0/src/ikichunk/inspection/__init__.py +3 -0
  17. ikichunk-0.2.0/src/ikichunk/inspection/inspectors.py +59 -0
  18. ikichunk-0.2.0/src/ikichunk/integrity/__init__.py +3 -0
  19. ikichunk-0.2.0/src/ikichunk/integrity/hashing.py +21 -0
  20. ikichunk-0.2.0/src/ikichunk/io/__init__.py +6 -0
  21. ikichunk-0.2.0/src/ikichunk/io/atomic.py +20 -0
  22. ikichunk-0.2.0/src/ikichunk/io/core.py +56 -0
  23. ikichunk-0.2.0/src/ikichunk/io/formats.py +252 -0
  24. ikichunk-0.2.0/src/ikichunk/io/streaming.py +27 -0
  25. ikichunk-0.2.0/src/ikichunk/net/__init__.py +3 -0
  26. ikichunk-0.2.0/src/ikichunk/net/http.py +8 -0
  27. ikichunk-0.2.0/src/ikichunk/observability/__init__.py +4 -0
  28. ikichunk-0.2.0/src/ikichunk/observability/logging_.py +18 -0
  29. ikichunk-0.2.0/src/ikichunk/observability/timing.py +32 -0
  30. ikichunk-0.2.0/src/ikichunk/partitioning/__init__.py +3 -0
  31. ikichunk-0.2.0/src/ikichunk/partitioning/strategies.py +177 -0
  32. ikichunk-0.2.0/src/ikichunk/plugins/__init__.py +3 -0
  33. ikichunk-0.2.0/src/ikichunk/plugins/registry.py +5 -0
  34. ikichunk-0.2.0/src/ikichunk/resilience/__init__.py +3 -0
  35. ikichunk-0.2.0/src/ikichunk/resilience/retry.py +21 -0
  36. ikichunk-0.2.0/src/ikichunk/storage/__init__.py +5 -0
  37. ikichunk-0.2.0/src/ikichunk/storage/codecs.py +19 -0
  38. ikichunk-0.2.0/src/ikichunk/storage/compression.py +152 -0
  39. ikichunk-0.2.0/src/ikichunk/system/__init__.py +4 -0
  40. ikichunk-0.2.0/src/ikichunk/system/platform.py +98 -0
  41. ikichunk-0.2.0/src/ikichunk/templates/__init__.py +3 -0
  42. ikichunk-0.2.0/src/ikichunk/templates/rendering.py +31 -0
  43. ikichunk-0.2.0/src/ikichunk/validation/__init__.py +3 -0
  44. ikichunk-0.2.0/src/ikichunk/validation/validate.py +20 -0
  45. ikichunk-0.2.0/src/ikichunk.egg-info/PKG-INFO +615 -0
  46. ikichunk-0.2.0/src/ikichunk.egg-info/SOURCES.txt +49 -0
  47. ikichunk-0.2.0/src/ikichunk.egg-info/dependency_links.txt +1 -0
  48. ikichunk-0.2.0/src/ikichunk.egg-info/entry_points.txt +2 -0
  49. ikichunk-0.2.0/src/ikichunk.egg-info/requires.txt +16 -0
  50. ikichunk-0.2.0/src/ikichunk.egg-info/top_level.txt +1 -0
  51. ikichunk-0.2.0/tests/test_regression.py +241 -0
ikichunk-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Franz Monzales
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,615 @@
1
+ Metadata-Version: 2.4
2
+ Name: ikichunk
3
+ Version: 0.2.0
4
+ Summary: Enterprise-portable Swiss-army-knife scripting toolkit for data engineering and systems automation.
5
+ Author: ikidevz
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/yourusername/ikichunk
8
+ Project-URL: Repository, https://github.com/yourusername/ikichunk.git
9
+ Project-URL: Documentation, https://github.com/yourusername/ikichunk
10
+ Project-URL: Issues, https://github.com/yourusername/ikichunk/issues
11
+ Keywords: data-engineering,partitioning,streaming,compression,parallel-processing,scripting
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Systems Administration
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Provides-Extra: yaml
29
+ Requires-Dist: PyYAML>=6.0; extra == "yaml"
30
+ Provides-Extra: parquet
31
+ Requires-Dist: pyarrow>=14.0; extra == "parquet"
32
+ Provides-Extra: zstd
33
+ Requires-Dist: zstandard>=0.22; extra == "zstd"
34
+ Provides-Extra: full
35
+ Requires-Dist: PyYAML>=6.0; extra == "full"
36
+ Requires-Dist: pyarrow>=14.0; extra == "full"
37
+ Requires-Dist: rich>=13.0; extra == "full"
38
+ Requires-Dist: tqdm>=4.0; extra == "full"
39
+ Requires-Dist: zstandard>=0.22; extra == "full"
40
+ Dynamic: license-file
41
+
42
+ # IKiChunk
43
+
44
+ **Enterprise-portable Swiss-army-knife scripting toolkit** for Data Engineers, Data Architects, Data Scientists, Software Engineers, and Systems Engineers.
45
+
46
+ One facade, zero required dependencies—tackle common data engineering tasks with minimal code.
47
+
48
+ ![Image_Cover](assets/image.png)
49
+
50
+ ```python
51
+ from ikichunk import partition # the only import most users need
52
+
53
+ parts = partition.smart_split("big.csv", goal="parallel")
54
+ results = partition.pmap(process, parts, workers=len(parts))
55
+ ```
56
+
57
+ Every method documented below is real, production-tested code—not a spec. See `IKiChunk-Codebase-v2.md` for the full source and `IKiChunk-Examples.md` for worked examples against a 2,000,000-row dataset.
58
+
59
+ ---
60
+
61
+ ## Table of Contents
62
+
63
+ 1. [Installation](#installation)
64
+ 2. [Quickstart](#quickstart)
65
+ 3. [Core Concepts](#core-concepts)
66
+ 4. [Feature Reference](#feature-reference)
67
+ - [I/O](#io)
68
+ - [Inspect](#inspect)
69
+ - [Config & Secrets](#config--secrets)
70
+ - [Logging](#logging)
71
+ - [Time](#time)
72
+ - [Retry](#retry)
73
+ - [Parallel (pmap)](#parallel-pmap)
74
+ - [Partition & Chunk](#partition--chunk-the-namesake-feature)
75
+ - [Hash & Integrity](#hash--integrity)
76
+ - [Compress](#compress)
77
+ - [Archive](#archive)
78
+ - [Platform & Portability](#platform--portability)
79
+ - [Process](#process)
80
+ - [Net](#net)
81
+ - [Watch](#watch)
82
+ - [Template](#template)
83
+ - [Validate](#validate)
84
+ 5. [Extensibility](#extensibility)
85
+ 6. [CLI](#cli)
86
+ 7. [Known Limitations](#known-limitations)
87
+ 8. [Project Structure](#project-structure)
88
+
89
+ ---
90
+
91
+ ## Installation
92
+
93
+ ```bash
94
+ # from the project root (contains pyproject.toml and src/)
95
+ pip install -e .
96
+
97
+ # with optional format/codec support
98
+ pip install -e ".[yaml]" # PyYAML for .yaml/.yml
99
+ pip install -e ".[parquet]" # pyarrow for .parquet
100
+ pip install -e ".[zstd]" # zstandard for algo="zstd" compression
101
+ pip install -e ".[full]" # all of the above + rich/tqdm for nicer CLI output
102
+ ```
103
+
104
+ Requires Python ≥3.9. The core package has **zero required dependencies**—everything below works out of the box on a bare Python install.
105
+
106
+ ---
107
+
108
+ ## Quickstart
109
+
110
+ ```python
111
+ from ikichunk import partition
112
+
113
+ # 1. Inspect unknown data — safe on config, files, DataFrames, lists, dicts
114
+ print(partition.inspect("data.csv"))
115
+
116
+ # 2. Safe, atomic write with a backup of the previous version
117
+ partition.write("out.json", {"status": "ok"}, backup=True)
118
+
119
+ # 3. Split a big file without loading it into memory
120
+ parts = partition.split_file("big.csv", by="rows", rows=100_000)
121
+
122
+ # 4. Or let it decide the split shape for you
123
+ parts = partition.smart_split("big.csv", goal="parallel")
124
+
125
+ # 5. Process each partition in parallel
126
+ results = partition.pmap(my_transform, parts, workers=len(parts))
127
+
128
+ # 6. Verify nothing got corrupted along the way
129
+ m = partition.manifest(parts)
130
+ ok = all(partition.verify(f["path"], f["hash"]) for f in m["files"])
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Core Concepts
136
+
137
+ - **The facade.** `partition` is a ready-to-use singleton instance of the `Partition` class. Every feature is reachable as `partition.<method>`—you never import a submodule directly for normal use.
138
+ - **No silent fallback.** Ambiguous formats, unpicklable process-pool arguments, and shell-metacharacter strings raise clearly instead of silently guessing.
139
+ - **Streaming-first.** Anything that touches "big" data (`stream`, `split_file`, `hash`, `download`) is designed to avoid loading entire files into memory.
140
+ - **Instantiable, not just a singleton.** Need an isolated instance (e.g., for tests)?
141
+
142
+ ```python
143
+ from ikichunk import Partition
144
+
145
+ test_partition = Partition(log_level="DEBUG", env_prefix="TEST_")
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Feature Reference
151
+
152
+ ### I/O
153
+
154
+ ```python
155
+ partition.read(path, fmt=None, **kwargs) -> Any
156
+ partition.write(path, data, *, atomic=True, backup=False, fmt=None, **kwargs) -> str
157
+ partition.exists(path) -> bool
158
+ partition.ensure_dir(path) -> str
159
+ partition.list_files(path=".", pattern="*", recursive=False) -> list[str]
160
+ partition.ls(...) # alias for list_files
161
+ partition.cat(path) -> str # alias for read(path, fmt="text")
162
+ partition.stream(path, fmt=None, chunk_size=None) -> Iterator[Any]
163
+ ```
164
+
165
+ Supported formats: `json`, `yaml` _(extra)_, `csv`, `tsv`, `parquet` _(extra)_, `pickle`, `text`—auto-detected from the file extension or passed explicitly via `fmt=`.
166
+
167
+ ```python
168
+ partition.write("report.json", {"rows": 42}, atomic=True, backup=True)
169
+ data = partition.read("report.json")
170
+
171
+ # Streaming: constant memory regardless of file size
172
+ for row in partition.stream("huge.csv"):
173
+ process(row)
174
+
175
+ # Unknown extensions raise instead of silently guessing "text"
176
+ partition.read("data.xyz")
177
+ # → UnknownFormatError: Cannot determine format for 'data.xyz' ...
178
+ ```
179
+
180
+ **Extending formats at runtime:**
181
+
182
+ ```python
183
+ from ikichunk.io.formats import FormatHandler
184
+
185
+ def read_upper(path, kwargs): return path.read_text().upper()
186
+ def write_upper(path, data, kwargs): path.write_text(str(data).upper())
187
+
188
+ partition.register_format("shout", FormatHandler("shout", read_upper, write_upper), extensions=[".shout"])
189
+ partition.write("out.shout", "hello")
190
+ partition.read("out.shout") # "HELLO"
191
+ ```
192
+
193
+ ---
194
+
195
+ ### Inspect
196
+
197
+ ```python
198
+ partition.inspect(obj_or_path, sample=3, **kwargs) -> dict
199
+ partition.head(obj_or_path, n=5, **kwargs) -> Any
200
+ ```
201
+
202
+ Works on file paths, lists, dicts, strings, and pandas DataFrames (if installed). Automatically redacts values whose keys look like secrets (`*_key`, `*_token`, `*_secret`, `*_password`, etc.):
203
+
204
+ ```python
205
+ partition.inspect({"user": "alice", "api_key": "sk-live-..."})
206
+ # {'type': 'dict', 'keys': ['user', 'api_key'],
207
+ # 'sample': {'user': 'alice', 'api_key': '<redacted>'}}
208
+ ```
209
+
210
+ `sample=` controls how many items are included—set it high enough (`sample=len(obj)`) to see all keys, not just the first few.
211
+
212
+ ---
213
+
214
+ ### Config & Secrets
215
+
216
+ ```python
217
+ partition.config(*sources, secrets=None, env_prefix=None, **kwargs) -> dict
218
+ partition.env(key, default=None, cast=str) -> Any
219
+ ```
220
+
221
+ Merge order (later values override earlier ones): files → secrets → environment variables.
222
+
223
+ ```python
224
+ cfg = partition.config("config.yaml", secrets=".env", env_prefix="APP_")
225
+ db_host = partition.env("DB_HOST", default="localhost")
226
+ port = partition.env("PORT", default=8080, cast=int)
227
+ ```
228
+
229
+ Secrets loaded via `secrets=` are flagged for redaction whenever the config dict passes through `inspect()`. They are never cached to disk beyond the source file.
230
+
231
+ ---
232
+
233
+ ### Logging
234
+
235
+ ```python
236
+ partition.log(name=None, level=None, **kwargs) -> logging.Logger
237
+ ```
238
+
239
+ Zero-config, console-friendly, structured output.
240
+
241
+ ```python
242
+ log = partition.log("etl", level="DEBUG")
243
+ log.info("processed %d rows", 1000)
244
+ ```
245
+
246
+ ---
247
+
248
+ ### Time
249
+
250
+ ```python
251
+ partition.now(fmt="%Y-%m-%d %H:%M:%S") -> str
252
+ partition.timer(name="block") # context manager
253
+ partition.duration(seconds) -> str # human-readable
254
+ ```
255
+
256
+ ```python
257
+ with partition.timer("load"):
258
+ df = partition.read("big.csv")
259
+ # prints: [load] 3s
260
+
261
+ print(partition.duration(3725)) # "1h 2m 5s"
262
+ ```
263
+
264
+ ---
265
+
266
+ ### Retry
267
+
268
+ ```python
269
+ @partition.retry(tries=3, delay=1.0, backoff=2.0, exceptions=(Exception,))
270
+ def flaky_call(): ...
271
+ ```
272
+
273
+ Exponential backoff decorator that composes cleanly with `net.fetch` for retry-on-failure HTTP calls:
274
+
275
+ ```python
276
+ @partition.retry(tries=3, delay=1.0, exceptions=(TimeoutError,))
277
+ def fetch_with_retry(url):
278
+ return partition.fetch(url, timeout=5)
279
+ ```
280
+
281
+ ---
282
+
283
+ ### Parallel (pmap)
284
+
285
+ ```python
286
+ partition.pmap(func, items, *, workers=None, backend="thread"|"process",
287
+ retries=0, progress=False, ordered=True) -> list
288
+ ```
289
+
290
+ ```python
291
+ results = partition.pmap(transform, records, workers=8, retries=2, progress=True)
292
+ ```
293
+
294
+ - `backend="thread"` (default)—no picklability constraint, best for I/O-bound work.
295
+ - `backend="process"`—for CPU-bound work. Both `func` and every item must be picklable (no lambdas or closures). A clear `PartitionParallelError` is raised at call time if they aren't, rather than a raw multiprocessing traceback.
296
+
297
+ ```python
298
+ def region_revenue(part_path): # must be a top-level function for backend="process"
299
+ ...
300
+
301
+ partition.pmap(region_revenue, parts, workers=4, backend="process")
302
+ ```
303
+
304
+ ---
305
+
306
+ ### Partition & Chunk _(the namesake feature)_
307
+
308
+ ```python
309
+ partition.split_file(path, *, by="size"|"rows"|"count", size=None, rows=None,
310
+ count=None, out_dir=None, fmt=None, prefix=None) -> list[str]
311
+ partition.smart_split(path, *, goal="parallel"|"memory-safe"|"storage",
312
+ workers=None, out_dir=None, fmt=None, explain=False) -> list[str]
313
+ partition.chunks(iterable, size) -> Iterator[list]
314
+ partition.manifest(paths, *, hash_algo="sha256") -> dict
315
+ ```
316
+
317
+ **Manual splitting**—three strategies, all streaming (never load whole source files into memory):
318
+
319
+ ```python
320
+ partition.split_file("big.csv", by="size", size="256MB", out_dir="parts/")
321
+ partition.split_file("big.csv", by="rows", rows=100_000, out_dir="parts/")
322
+ partition.split_file("big.csv", by="count", count=8, out_dir="parts/") # exact even split
323
+ ```
324
+
325
+ `by="size"` is a raw byte-level split—fast and works on any file, but partitions may cut mid-row for row-based formats. `by="rows"`/`by="count"` are row-aware for `csv`/`tsv` and repeat the header in every partition by default.
326
+
327
+ **Auto-strategy splitting**—inspects the file and system, picks an optimal shape for you:
328
+
329
+ ```python
330
+ parts = partition.smart_split("big.csv", goal="parallel") # count = CPU count
331
+ parts = partition.smart_split("big.csv", goal="memory-safe") # size capped under available RAM
332
+ parts = partition.smart_split("big.csv", goal="storage") # 100MB parts, object-storage sized
333
+
334
+ # See the reasoning, not just the result:
335
+ parts, why = partition.smart_split("big.csv", goal="parallel", explain=True)
336
+ print(why) # {'goal': 'parallel', 'cpu_count': 8, 'chosen_strategy': {...}, 'partition_count': 8, ...}
337
+ ```
338
+
339
+ **Generic in-memory chunking**—feeds `pmap`:
340
+
341
+ ```python
342
+ for batch in partition.chunks(records, size=500):
343
+ partition.pmap(transform, batch, workers=8)
344
+ ```
345
+
346
+ **Manifests**—an auditable record of a partition batch:
347
+
348
+ ```python
349
+ m = partition.manifest(parts)
350
+ # {'algo': 'sha256', 'files': [{'path': ..., 'size_bytes': ..., 'hash': ...}, ...]}
351
+ partition.write("manifest.json", m)
352
+ ```
353
+
354
+ **Extending goals at runtime:**
355
+
356
+ ```python
357
+ def goal_tiny(file_size, info, workers):
358
+ return {"by": "size", "size": 1024 * 1024} # force 1MB partitions
359
+
360
+ partition.register_split_goal("tiny", goal_tiny)
361
+ partition.smart_split("big.csv", goal="tiny")
362
+ ```
363
+
364
+ ---
365
+
366
+ ### Hash & Integrity
367
+
368
+ ```python
369
+ partition.hash(path_or_bytes, algo="sha256") -> str
370
+ partition.verify(path, expected_hash, algo="sha256") -> bool
371
+ ```
372
+
373
+ Streams the file in fixed-size blocks—no full-file memory load.
374
+
375
+ ```python
376
+ h = partition.hash("part1.csv")
377
+ assert partition.verify("part1.csv", h) # True — unless the file changed
378
+ ```
379
+
380
+ Typical use: verify a `manifest()` after copying or transferring partitions to another location.
381
+
382
+ ```python
383
+ manifest = partition.read("manifest.json")
384
+ for entry in manifest["files"]:
385
+ if not partition.verify(entry["path"], entry["hash"]):
386
+ raise RuntimeError(f"corrupted: {entry['path']}")
387
+ ```
388
+
389
+ ---
390
+
391
+ ### Compress
392
+
393
+ ```python
394
+ partition.compress(path, *, algo="gzip"|"zip"|"zstd", out=None, keep_original=True) -> str
395
+ partition.decompress(path, *, out=None) -> str
396
+ ```
397
+
398
+ `gzip` and `zip` are in the standard library. `algo="zstd"` requires `pip install ikichunk[zstd]`—attempting it without the extra raises a `MissingDependencyError` with the exact install command.
399
+
400
+ ```python
401
+ gz_path = partition.compress("part1.csv", algo="gzip")
402
+ restored = partition.decompress(gz_path, out="restored.csv")
403
+ ```
404
+
405
+ **Extending codecs at runtime** via `partition.register_codec(name, codec)` with a `storage.codecs.Codec(name, default_ext, compress_fn, decompress_fn)`.
406
+
407
+ ---
408
+
409
+ ### Archive
410
+
411
+ ```python
412
+ partition.archive(source, out_path, *, fmt="tar.gz"|"zip") -> str
413
+ partition.extract(archive_path, *, out_dir=None) -> str
414
+ ```
415
+
416
+ Distinct from `compress()`—this bundles a **directory** (or multiple files) into a single archive, rather than shrinking one file.
417
+
418
+ ```python
419
+ partition.archive("parts/", "parts_batch.tar.gz")
420
+ partition.extract("parts_batch.tar.gz", out_dir="restored/")
421
+ ```
422
+
423
+ `extract()` refuses to write outside `out_dir` (zip-slip guard)—this is a hard rule, not configurable.
424
+
425
+ ---
426
+
427
+ ### Platform & Portability
428
+
429
+ ```python
430
+ partition.platform_info() -> dict
431
+ partition.which(cmd) -> str | None
432
+ partition.normalize_path(path) -> str
433
+ ```
434
+
435
+ ```python
436
+ info = partition.platform_info()
437
+ # {'os': 'Linux', 'cpu_count': 8, 'available_memory_bytes': ..., 'ikichunk_version': '0.2.0', ...}
438
+
439
+ if partition.which("docker") is None:
440
+ raise RuntimeError("docker not found on PATH")
441
+ ```
442
+
443
+ ---
444
+
445
+ ### Process
446
+
447
+ ```python
448
+ partition.is_running(pid) -> bool
449
+ partition.kill(pid, *, timeout=5, force=False) -> bool
450
+ partition.wait_for_port(host, port, *, timeout=30, interval=0.5) -> bool
451
+ partition.is_port_open(host, port, *, timeout=1) -> bool
452
+ ```
453
+
454
+ Checks and signals processes—**not** a supervisor. It never restarts or daemonizes.
455
+
456
+ ```python
457
+ partition.run(["systemctl", "restart", "myapp"])
458
+ if partition.wait_for_port("localhost", 8080, timeout=30):
459
+ log.info("service is up")
460
+ ```
461
+
462
+ `kill()` sends `SIGTERM` first, polls up to `timeout` seconds, and only sends `SIGKILL` if `force=True` and the process is still alive.
463
+
464
+ ---
465
+
466
+ ### Net
467
+
468
+ ```python
469
+ partition.fetch(url, *, timeout=10, headers=None) -> bytes | str
470
+ partition.download(url, path, *, timeout=30, progress=False) -> str
471
+ partition.reachable(host, port=None, *, timeout=2) -> bool
472
+ ```
473
+
474
+ Built on stdlib `urllib`—**not** a `requests` replacement (no sessions, auth flows, or pagination). Covers the "I just need to fetch one thing" case.
475
+
476
+ ```python
477
+ if partition.reachable("api.example.com", 443):
478
+ data = partition.fetch("https://api.example.com/status")
479
+
480
+ partition.download("https://example.com/dataset.csv", "dataset.csv", progress=True)
481
+ ```
482
+
483
+ `download()` streams directly to a temp file and atomically renames into place—a failed download never leaves a corrupted partial file.
484
+
485
+ ---
486
+
487
+ ### Watch
488
+
489
+ ```python
490
+ partition.watch(path, *, on_change=None, interval=1.0, recursive=False) -> WatchHandle
491
+ partition.watch_once(path, *, since=None) -> bool
492
+ ```
493
+
494
+ Poll-based (mtime + size), not OS-native filesystem events—portable across every OS with zero dependencies.
495
+
496
+ ```python
497
+ handle = partition.watch("config/", on_change=lambda p: reload_config(), interval=1.0)
498
+ # ... later
499
+ handle.stop()
500
+ ```
501
+
502
+ ---
503
+
504
+ ### Template
505
+
506
+ ```python
507
+ partition.render(template, variables, *, out=None, strict=True) -> str
508
+ ```
509
+
510
+ Variable substitution only (stdlib `string.Template`, `$var` syntax)—**not** a templating engine. No loops or conditionals.
511
+
512
+ ```python
513
+ partition.render("app.env.tmpl", {"DB_HOST": partition.env("DB_HOST")}, out="app.env")
514
+ ```
515
+
516
+ `strict=True` (default) raises on a missing variable instead of silently leaving it unrendered. Pass `strict=False` to allow partial renders.
517
+
518
+ ---
519
+
520
+ ### Validate
521
+
522
+ ```python
523
+ partition.require(condition, msg="Requirement failed")
524
+ partition.not_none(value, name="value") -> value
525
+ ```
526
+
527
+ ```python
528
+ partition.require(len(records) > 0, "no records loaded")
529
+ config_val = partition.not_none(cfg.get("api_key"), name="api_key")
530
+ ```
531
+
532
+ ---
533
+
534
+ ## Extensibility
535
+
536
+ Every registry-backed feature above (`register_format`, `register_codec`, `register_split_goal`, `register_inspector`) adds new capabilities without editing the library source. The facade has one more general mechanism:
537
+
538
+ ```python
539
+ def double(x):
540
+ return x * 2
541
+
542
+ partition.register("double", double)
543
+ partition.double(21) # 42
544
+
545
+ # Refuses to silently clobber an existing method:
546
+ partition.register("read", lambda x: x)
547
+ # ValueError: 'read' is already a facade method/attribute
548
+ ```
549
+
550
+ **Independent instances** for testing or multi-configuration use:
551
+
552
+ ```python
553
+ from ikichunk import Partition
554
+
555
+ worker_partition = Partition(log_level="DEBUG", env_prefix="WORKER_")
556
+ ```
557
+
558
+ **Pip-installable plugins** register automatically via an `ikichunk.plugins` setuptools entry point, discovered at import time—no manual `register()` call needed for third-party packages.
559
+
560
+ ---
561
+
562
+ ## CLI
563
+
564
+ ```bash
565
+ ikichunk inspect data.csv
566
+ ikichunk ls . -r -p "*.json"
567
+ ikichunk split big.csv --by size --size 256MB --out-dir parts/
568
+ ikichunk smart-split big.csv --goal parallel --out-dir parts/
569
+ ikichunk wait-for-port localhost 8080 --timeout 30
570
+ ikichunk archive parts/ parts.tar.gz
571
+ ikichunk platform
572
+ ikichunk version
573
+ ```
574
+
575
+ Every subcommand maps 1:1 to a facade method—the CLI carries no logic the Python API doesn't also expose. Run `ikichunk --help` for the full list of available subcommands.
576
+
577
+ ---
578
+
579
+ ## Known Limitations
580
+
581
+ - `split_file(by="count")` on row-based formats (CSV/TSV) does two streaming passes—at most one partition's worth of rows is held in memory at a time, not the whole file. `by="rows"`/`by="size"` are single-pass throughout.
582
+ - `net` and `template` are deliberately minimal—reach for `requests`/`httpx` or `Jinja2` if you need sessions, auth flows, or templating logic.
583
+ - `watch()` is poll-based, not OS-native events—suitable for config-reload use cases, not designed for high-frequency, low-latency file watching at scale.
584
+ - `smart_split(goal="memory-safe")` uses a heuristic safety margin (10% of available RAM, 64MB floor), not a guaranteed bound—tune it via `register_split_goal` if your workload needs stricter guarantees.
585
+
586
+ ---
587
+
588
+ ## Project Structure
589
+
590
+ ```
591
+ ikichunk/
592
+ ├── pyproject.toml
593
+ └── src/
594
+ └── ikichunk/
595
+ ├── __init__.py # exports `partition` (singleton) and `Partition` (class)
596
+ ├── facade.py # Partition class — the single public entry point
597
+ ├── exceptions.py # centralized custom exceptions
598
+ ├── io/ # read/write/stream — Strategy-pattern format registry
599
+ ├── inspection/ # inspect/head — type-dispatch registry
600
+ ├── configuration/ # config/env
601
+ ├── observability/ # log/now/timer/duration
602
+ ├── resilience/ # retry
603
+ ├── concurrency/ # pmap
604
+ ├── partitioning/ # split_file/smart_split/chunks/manifest — the namesake package
605
+ ├── integrity/ # hash/verify
606
+ ├── storage/ # compress/decompress/archive/extract — Strategy-pattern codecs
607
+ ├── system/ # platform_info/process/shell
608
+ ├── net/ # fetch/download/reachable
609
+ ├── automation/ # watch/render
610
+ ├── validation/ # require/not_none
611
+ ├── plugins/ # entry-point plugin discovery
612
+ └── cli/ # Command-pattern CLI, thin wrapper over the facade
613
+ ```
614
+
615
+ See `IKiChunk-Blueprint.md` for design rationale, `IKiChunk-Codebase-v2.md` for the full source, and `IKiChunk-Examples.md` for executed examples with output captured against a real 2,000,000-row dataset.