Metadata-Version: 2.4
Name: codechu-ipc
Version: 0.1.0
Summary: Stdlib-only local IPC — Unix sockets, FIFOs, JSON-line protocol.
Author: Codechu
License: MIT License
        
        Copyright (c) 2026 Codechu
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/codechu/ipc-py
Project-URL: Source, https://github.com/codechu/ipc-py
Project-URL: Issues, https://github.com/codechu/ipc-py/issues
Keywords: ipc,unix-socket,fifo,json,stdlib,linux
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: POSIX :: BSD
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: System :: Networking
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Dynamic: license-file

```text
   ┌─────────────────┐      ╔═════════════════╗
   │  codechu — ipc  │═════>║   your daemon   ║
   │  client         │ JSON ║   handler(req)  ║
   │  request()      │<═════║   → response    ║
   └─────────────────┘ line ╚═════════════════╝
        unix socket  ·  fifo  ·  json-line  ·  pidfile
```

> *Local IPC the boring way — Unix sockets, FIFOs, one JSON object per line.*

# codechu-ipc

Stdlib-only local IPC for Linux daemons and sidecars: Unix domain
sockets, named pipes (FIFOs), JSON-line framing, server lifecycle
helpers. No third-party dependencies. Python 3.10+.

Linux is the primary target; BSD/macOS are best-effort (anything
POSIX-y with `AF_UNIX` and `mkfifo` should work).

## Install

```bash
pip install codechu-ipc
```

## API at a glance

```python
from codechu_ipc import UnixServer, UnixClient, FifoChannel, pidfile

# --- Server side ------------------------------------------------------
def handler(req: dict) -> dict:
    return {"echo": req}

with UnixServer("/run/codechu/myapp/control.sock", handler):
    ...  # serve until the context exits

# --- Client side ------------------------------------------------------
client = UnixClient("/run/codechu/myapp/control.sock")
response = client.request({"cmd": "status"})       # → {"echo": ...}
client.notify({"cmd": "shutdown"})                  # fire-and-forget

# --- FIFO -------------------------------------------------------------
ch = FifoChannel("/run/codechu/myapp/events.fifo")
ch.send({"event": "rescan-done"})                   # non-blocking
msg = ch.recv()                                     # blocks for next

# --- Lifecycle --------------------------------------------------------
with pidfile("/run/codechu/myapp/daemon.pid"):
    serve_forever()
```

## Why JSON-line?

- **One object per line.** Trivial to parse from any language, any
  shell pipeline, any log viewer.
- **No length prefixes, no schemas.** Easy to inspect with `nc -U`
  or `socat` while debugging.
- **Backpressure is the OS's problem.** The kernel's socket buffers
  do the queuing; this library stays a thin wrapper.

If you need binary framing, multiplexing, or RPC semantics, you want
gRPC, not this.

## `UnixServer(path, handler, *, mode=0o600, backlog=16)`

- Background accept-loop thread; one worker thread per connection.
- Default socket permissions: **owner-only (0o600)**. Override with
  `mode=`.
- Auto-cleans stale socket files from a crashed previous run; refuses
  to overwrite a live socket (raises `FileExistsError`).
- Handler returning `None` means "this was a notification, send no
  response". Anything else is encoded back as one JSON line.
- Handler exceptions are caught and returned to the client as
  `{"error": "...", "type": "..."}` — the server keeps serving.
- Context manager: `with UnixServer(...) as srv: ...` calls
  `start()`/`stop()` for you.

## `UnixClient(path, *, timeout=5.0, retries=3, retry_backoff=0.5)`

- `request(payload)` — send a JSON line, read one back.
- `notify(payload)` — send a JSON line, close. No response read.
- Retries on `ConnectionRefusedError` / `FileNotFoundError`, with
  exponential backoff (`0.5s, 1.0s, 2.0s, ...`). Useful for the
  short window between "daemon is starting" and "daemon is ready".

## `JsonLineProtocol`

The framing helper, exposed so you can build your own transport on
top of it.

```python
data = JsonLineProtocol.encode({"hello": "world"})  # → b'{"hello":"world"}\n'

with open("/var/log/myapp.jsonl", "rb") as f:
    for obj in JsonLineProtocol.decode_stream(f):
        print(obj)
```

## `FifoChannel(path, *, mode=0o600)`

- Auto-creates the FIFO with `mkfifo` if missing.
- `send()` opens write-side non-blocking — raises `BrokenPipeError`
  if no reader is attached, so you don't deadlock.
- `recv()` opens read-side blocking and yields one decoded message.
- FIFOs are unidirectional; for request/response use `UnixServer`.

## `pidfile(path)`

Context manager that:

1. Creates the parent directory.
2. Opens the pidfile and acquires an exclusive `flock`.
3. If another process holds the lock, raises `BlockingIOError`
   immediately — perfect for "don't start twice" guards.
4. Writes the current PID, releases the lock, removes the file on
   exit (including exceptions).

```python
try:
    with pidfile("/run/codechu/myapp/daemon.pid"):
        run()
except BlockingIOError:
    sys.exit("already running")
```

## Design

- **Pure stdlib.** `socket`, `threading`, `fcntl`, `os`, `json` — no
  more. The whole library is five small modules.
- **Linux-first.** AF_UNIX, `mkfifo`, `flock` — all POSIX, but Linux
  is the platform we run CI on. BSD/macOS work in practice.
- **No magic.** No global registry, no event loop, no decorators.
  You start the server, you stop the server.
- **Crash-safe.** Stale socket files are detected on startup; pidfiles
  are removed on exit; handler exceptions don't kill the server.

## Tests

```bash
pip install -e ".[dev]"
pytest -q
ruff check src tests
```

## License

MIT — see [LICENSE](LICENSE).
