Metadata-Version: 2.5
Name: sumall
Version: 0.0.2
Summary: Sum numbers from the clipboard, stdin, or arguments and report the total in MB / KB / bytes.
Project-URL: Homepage, https://github.com/Junbo-Zheng/sumall
Project-URL: Issues, https://github.com/Junbo-Zheng/sumall/issues
Author: Junbo Zheng
License: Apache-2.0
License-File: LICENSE
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# sumall

[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)

A fast, zero-dependency CLI that sums a column of numbers and reports the total in MB / KB / bytes.

Built for the everyday "add up these sizes" moment: paste from a spreadsheet, pipe `du -sh` output, or pass numbers as arguments. The per-line breakdown shows exactly which values were included, so the total is verifiable at a glance.

## Features

- **Three input sources**, first match wins: command-line arguments, piped stdin, the system clipboard (bare invocation)
- **du / ls -lh style suffixes**: `768K`, `1.9M`, `2G`, `15Gi` — mixed units are normalized to bytes before summing
- **`ls -l` long-format lines**: the size field is parsed as bytes (`ls -lh` suffixes honored); directory lines are skipped since their "size" is just the inode
- **Table pastes**: markdown tables and spreadsheet TSV (Excel / Feishu clipboard) — the number can sit in any column; when a row holds several numbers, unit-marked cells win, then the last column (`--column N` pins one)
- **Trailing labels tolerated**: `177K background.png` parses as 177 KB; the label rides along in the breakdown
- **Unit-aware totals**: bare numbers sum unitless; values with a `K/M/G` suffix are normalized to bytes first; `--unit` anchors bare numbers to a size unit
- **Per-line breakdown** plus totals in MB / KB / bytes
- Pure Python 3.10+ stdlib — no runtime dependencies, starts in ~60 ms

> [!NOTE]
> Units follow the binary (MiB) convention — 1 MB = 1024 KB = 1,048,576 bytes — matching `du -h` / `df -h` output.

## Usage

### From arguments

Arguments are strict: every token must be a number (optionally with a suffix). A typo fails loudly instead of being silently skipped. Tokens may contain spaces or newlines, so pasting a whole column as one quoted argument works too.

```console
$ ./main.py 10 37 6 1.46
source: arguments
input: 4 values

breakdown:
   1) 10     2) 37     3) 6      4) 1.46

total:
  54.46
```

Bare numbers stay unitless (the `--unit` flag anchors them); values with a `K/M/G` suffix are always normalized to bytes.

### From the clipboard

Copy the numbers, then run bare — no arguments needed. Linux (X11 via `xclip`/`xsel`, Wayland via `wl-paste`) and macOS (`pbpaste`) are supported; the first tool found wins.

Clipboard (and stdin) parsing is line-based and lenient: each line is split into cells (tabs, then markdown pipes, then whitespace) and the value is located among them; lines with no number — headers, separators, prose, directories, summary rows — are skipped and counted in the header.

### From stdin

```console
$ du -sh img/* | ./main.py
source: stdin
input: 4 values, unit mixed

breakdown:
  13M app.bin       =  13.0000 MB
  1.9M audio.bin    =  1.9000 MB
  768K ota.bin      =  0.7500 MB
  1.7M sensor.bin   =  1.7000 MB

total:
  17.35 MB
  17766.40 KB
  18192794 bytes
```

### From an `ls -l` paste

`ls -l` puts the size in field 5, not at the start of the line — a paste is recognized as long-format lines and the size field is taken as **bytes** (`ls -lh` suffixes like `12K` are honored). Directory lines and the `total` summary are skipped:

```console
$ ls -l | ./main.py
source: stdin
input: 4 values, unit mixed (3 lines skipped)

breakdown:
  11358B LICENSE        =  0.0108 MB
  499B main.py*         =  0.0005 MB
  1666B pyproject.toml  =  0.0016 MB
  3588B README.md       =  0.0034 MB

total:
  0.02 MB
  16.71 KB
  17111 bytes
```

### From a table paste

Markdown tables and spreadsheet clipboard (Excel / Feishu copy = TSV) put the number after the name, in any column — both work. When a row holds several numbers (`size` and `used`, or a `df -h` row), cells carrying a unit marker win over bare ones, and otherwise the last column is taken; a yellow note is printed whenever that heuristic fired, so a wrong pick is visible:

```console
$ ./main.py -c 2 < partitions.md
source: stdin
input: 3 values, unit mixed (2 lines skipped)

breakdown:
  13M app.bin      =  13.0000 MB
  1.9M audio.bin   =  1.9000 MB
  768K ota.bin     =  0.7500 MB

total:
  15.65 MB
  16025.60 KB
  16410214 bytes
```

`-c N` takes the Nth table column (counting all columns, not just numeric ones) instead of the heuristic; footer rows starting with `total` / `总计` / `合计` are always skipped so totals never count twice.

### Units

| Input line | Parsed as |
|---|---|
| `10` | a bare number: summed as-is (no unit) when every value is bare; anchored to a unit when a suffix-marked value or `--unit` is present |
| `768K` / `768k` | 768 KB — suffix always wins over the default unit |
| `1.9M` / `15Gi` / `1,048,576` | 1.9 MB / 15 GB / 1048576 of the default unit |
| `2G` | 2 GB |
| `177K file.png` | 177 KB, label `file.png` |
| `\| app.bin \| 13M \|` | 13 MB, label `app.bin` (markdown row) |
| `ap<TAB>13.5 MB` | 13.5 MB, label `ap` (spreadsheet cell) |
| `ap 13.5 16` | several numbers: unit-marked wins, else last; `-c N` pins one |
| `-rw-r--r-- 1 mi mi 11358 Sep 1 09:48 LICENSE` | 11358 bytes, label `LICENSE` |
| `drwxr-xr-x 3 mi mi 4096 Sep 1 09:50 src/` | skipped — directory inode size, not content |
| `total 32` / `合计 17111` | skipped — block counts and footer totals |
| `30%` | 30 percent — percent-only input sums as-is, shown as `xx.xx %` |
| `v1.2` / `2025-09-01` | skipped — not sizes |
| `hello` | skipped (stdin/clipboard) or an error (argument) |

Use `--unit B|KB|MB|GB` to give bare numbers a size unit, e.g. `./main.py --unit KB 768 128` sums to 896 KB; without it, an all-bare input sums unitless (e.g. `46.4%`-style or plain-count pastes).

## Installation

No install is needed — run straight from a checkout:

```console
$ ./main.py 10 37 1.46
```

Or install it as a `sumall` command on your `PATH`:

```console
$ pip install -e .
$ sumall 10 37 1.46
```

## Development

```console
$ pip install -e ".[dev]"   # pytest + ruff
$ pytest                    # runs against src/ via pythonpath — no build step
$ ruff check src tests main.py
$ ruff format --check src tests main.py
```

CI (`.github/workflows/ci.yml`) lints and tests across Python 3.10–3.12 and additionally builds the wheel, installs it clean, and reruns the suite against the installed package.

## License

This project is licensed under the Apache License, Version 2.0. See
[LICENSE](LICENSE) or <https://www.apache.org/licenses/LICENSE-2.0> for the
full text.
