From 69fe83657e8066041650de98941ac71b27517dad Mon Sep 17 00:00:00 2001
From: Alvin Wan
Date: Sun, 5 Apr 2026 02:11:18 -0700
Subject: [PATCH 1/2] optimize speed and update benchmarks
---
benchmarks/README.md | 18 +++++++++++++-----
pymini/pymini.py | 16 +++++++++-------
2 files changed, 22 insertions(+), 12 deletions(-)
diff --git a/benchmarks/README.md b/benchmarks/README.md
index 858a300..cec5bec 100644
--- a/benchmarks/README.md
+++ b/benchmarks/README.md
@@ -16,13 +16,21 @@ Checked-in fixture comparison:
| `tests/examples/pyminifier.py` | `1,355` bytes | `511` bytes, `62.3%` | `676` bytes, `50.1%` | `1,020` bytes, `24.7%` |
| `tests/examples/pyminify.py` | `1,990` bytes | `981` bytes, `50.7%` | `1,605` bytes, `19.3%` | `983` bytes, `50.6%` |
-TexSoup package validation:
+TexSoup package mode (`pymini` only):
| Input | Original | `pymini` | Reduction |
| --- | ---: | ---: | ---: |
| `TexSoup/` raw Python source (`*.py`) | `98,181` bytes | `33,107` bytes | `66.3%` |
| `TexSoup/` compressed source (`.tar.gz`) | `70,532` bytes | `11,850` bytes | `83.2%` |
+TexSoup file-by-file package comparison. All three outputs pass the upstream
+TexSoup test suite (`78` tests):
+
+| Input | Original | `pymini` | `pyminifier` | `python-minifier` |
+| --- | ---: | ---: | ---: | ---: |
+| `TexSoup/` raw Python source (`*.py`) | `98,181` bytes | `32,131` bytes, `67.3%` | `34,643` bytes, `64.7%` | `83,303` bytes, `15.2%` |
+| `TexSoup/` compressed source (`.tar.gz`) | `23,116` bytes | `10,926` bytes, `52.7%` | `9,741` bytes, `57.9%` | `21,532` bytes, `6.9%` |
+
# Speed
Latency is machine-dependent. Recompute these with
@@ -30,10 +38,10 @@ Latency is machine-dependent. Recompute these with
| Input | `pymini` | `pyminifier` | `python-minifier` |
| --- | ---: | ---: | ---: |
-| `tests/examples/pyminifier.py` | `14.1 ms` | `0.4 ms` | `1.5 ms` |
-| `tests/examples/pyminify.py` | `1227.6 ms` | `1.1 ms` | `4.0 ms` |
-| `TexSoup/` package API | `4928.8 ms` | `—` | `—` |
-| `TexSoup/` package CLI | `5062.0 ms` | `—` | `—` |
+| `tests/examples/pyminifier.py` | `2.5 ms` | `0.4 ms` | `1.6 ms` |
+| `tests/examples/pyminify.py` | `5.7 ms` | `1.2 ms` | `4.2 ms` |
+| `TexSoup/` package API | `410.4 ms` | `—` | `—` |
+| `TexSoup/` package CLI | `414.0 ms` | `—` | `—` |
To reproduce those numbers:
diff --git a/pymini/pymini.py b/pymini/pymini.py
index 2ea7382..816a235 100644
--- a/pymini/pymini.py
+++ b/pymini/pymini.py
@@ -117,7 +117,7 @@ def visit(self, node):
for child in ast.iter_child_nodes(node):
child.parent = node
self.visit(child)
- return super().visit(node)
+ return node
class CommentRemover(NodeTransformer):
@@ -845,7 +845,6 @@ def transform(self, *trees):
)
imported.transform(tree)
append_public_aliases(tree, imported.nodes_to_append)
- ParentSetter().visit(tree)
new_trees.append(tree)
return new_trees
@@ -918,8 +917,6 @@ def transform(self, *trees):
collector = RepeatedStringCollector()
collector.visit(tree)
RepeatedStringRewriter(self.generator, collector.repeated_strings_by_scope).visit(tree)
- ParentSetter().visit(tree)
- ast.fix_missing_locations(tree)
return trees
@@ -1080,10 +1077,7 @@ def __init__(self, generator):
def transform(self, *trees):
for tree in trees:
- ParentSetter().visit(tree)
self.visit(tree)
- ParentSetter().visit(tree)
- ast.fix_missing_locations(tree)
return trees
def _next_safe_name(self, reserved_names):
@@ -1352,6 +1346,13 @@ def transform(self, *trees):
yield ast.unparse(tree)
+class LocationFixer(Transformer):
+ def transform(self, *trees):
+ for tree in trees:
+ ast.fix_missing_locations(tree)
+ return trees
+
+
def module_prefixes(module: Optional[str]) -> List[str]:
if not module:
return []
@@ -1727,6 +1728,7 @@ def minify(sources, modules='main', keep_module_names=False,
),
# final post-processing to remove whitespace (minify)
+ LocationFixer(),
Unparser(),
WhitespaceRemover(),
)
From ddcb418774ed046e6d0c1a8a742195f43963f17e Mon Sep 17 00:00:00 2001
From: Alvin Wan
Date: Sun, 5 Apr 2026 02:23:09 -0700
Subject: [PATCH 2/2] refresh benchmark docs
---
README.md | 6 ++---
benchmarks/README.md | 53 +++++++++++++-------------------------------
2 files changed, 19 insertions(+), 40 deletions(-)
diff --git a/README.md b/README.md
index ddef45d..4bb8462 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
`pymini` is an AST-based Python minifier for scripts and packages. It preserves
package layout by default, can emit a single-file bundle when asked, and can
-shrink Python code by roughly `30%` to `90%` depending on the codebase and
-whether you compare raw source or compressed archives.
+shrink Python code by roughly `15%` to `70%` on the checked-in fixtures and
+validated package benchmarks.
- [Getting Started](#getting-started)
- [Installation](#installation)
@@ -46,7 +46,7 @@ Current checked-in fixtures:
| `tests/examples/pyminifier.py` | `1,355` bytes | `511` bytes | `62.3%` |
| `tests/examples/pyminify.py` | `1,990` bytes | `981` bytes | `50.7%` |
| `TexSoup/` raw Python source (`*.py`) | `98,181` bytes | `33,107` bytes | `66.3%` |
-| `TexSoup/` compressed source (`.tar.gz`) | `70,532` bytes | `11,850` bytes | `83.2%` |
+| `TexSoup/` compressed source (`.tar.gz`) | `23,118` bytes | `11,368` bytes | `50.8%` |
For baseline comparisons, speed results, and TexSoup validation details, see
[benchmarks/README.md](./benchmarks/README.md).
diff --git a/benchmarks/README.md b/benchmarks/README.md
index cec5bec..dbbe726 100644
--- a/benchmarks/README.md
+++ b/benchmarks/README.md
@@ -3,47 +3,26 @@
This directory holds the current size and speed measurements for `pymini`, plus
the benchmark harness used to reproduce them.
-- [Compression](#compression)
-- [Speed](#speed)
+- [Results](#results)
+- [Reproduce](#reproduce)
- [TexSoup Validation](#texsoup-validation)
-# Compression
+# Results
-Checked-in fixture comparison:
+| Input | Original | `pymini` size | `pymini` speed | `pyminifier` size | `pyminifier` speed | `python-minifier` size | `python-minifier` speed |
+| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
+| `pyminifier.py` | `1,355` bytes | `511` bytes, `62.3%` | `2.4 ms` | `676` bytes, `50.1%` | `0.4 ms` | `1,020` bytes, `24.7%` | `1.6 ms` |
+| `pyminify.py` | `1,990` bytes | `981` bytes, `50.7%` | `5.4 ms` | `1,605` bytes, `19.3%` | `1.2 ms` | `983` bytes, `50.6%` | `4.1 ms` |
+| `TexSoup/*.py` | `98,181` bytes | `33,107` bytes, `66.3%` | `399.3 ms` | `34,643` bytes, `64.7%` | `31.3 ms` | `83,303` bytes, `15.2%` | `120.8 ms` |
+| `TexSoup.tar.gz` | `23,118` bytes | `11,368` bytes, `50.8%` | `—` | `9,741` bytes, `57.9%` | `—` | `21,532` bytes, `6.9%` | `—` |
-| Input | Original | `pymini` | `pyminifier` | `python-minifier` |
-| --- | ---: | ---: | ---: | ---: |
-| `tests/examples/pyminifier.py` | `1,355` bytes | `511` bytes, `62.3%` | `676` bytes, `50.1%` | `1,020` bytes, `24.7%` |
-| `tests/examples/pyminify.py` | `1,990` bytes | `981` bytes, `50.7%` | `1,605` bytes, `19.3%` | `983` bytes, `50.6%` |
+`TexSoup/*.py` compares validated package outputs. `pymini` uses package mode;
+the baselines minify each file independently in the preserved package tree. All
+three outputs pass the upstream TexSoup test suite (`78` tests).
-TexSoup package mode (`pymini` only):
+# Reproduce
-| Input | Original | `pymini` | Reduction |
-| --- | ---: | ---: | ---: |
-| `TexSoup/` raw Python source (`*.py`) | `98,181` bytes | `33,107` bytes | `66.3%` |
-| `TexSoup/` compressed source (`.tar.gz`) | `70,532` bytes | `11,850` bytes | `83.2%` |
-
-TexSoup file-by-file package comparison. All three outputs pass the upstream
-TexSoup test suite (`78` tests):
-
-| Input | Original | `pymini` | `pyminifier` | `python-minifier` |
-| --- | ---: | ---: | ---: | ---: |
-| `TexSoup/` raw Python source (`*.py`) | `98,181` bytes | `32,131` bytes, `67.3%` | `34,643` bytes, `64.7%` | `83,303` bytes, `15.2%` |
-| `TexSoup/` compressed source (`.tar.gz`) | `23,116` bytes | `10,926` bytes, `52.7%` | `9,741` bytes, `57.9%` | `21,532` bytes, `6.9%` |
-
-# Speed
-
-Latency is machine-dependent. Recompute these with
-`PYTHONPATH=. .venv/bin/python benchmarks/benchmark_speed.py`.
-
-| Input | `pymini` | `pyminifier` | `python-minifier` |
-| --- | ---: | ---: | ---: |
-| `tests/examples/pyminifier.py` | `2.5 ms` | `0.4 ms` | `1.6 ms` |
-| `tests/examples/pyminify.py` | `5.7 ms` | `1.2 ms` | `4.2 ms` |
-| `TexSoup/` package API | `410.4 ms` | `—` | `—` |
-| `TexSoup/` package CLI | `414.0 ms` | `—` | `—` |
-
-To reproduce those numbers:
+Recompute the speed measurements with:
```bash
python3 -m pip install -e ".[dev]" python-minifier
@@ -56,9 +35,9 @@ PYTHONPATH=. .venv/bin/python benchmarks/benchmark_speed.py --pyminifier-root /t
`pymini` has been validated against the upstream `TexSoup` test suite in
package mode. Current validation: upstream pytest passes (`78` tests), raw
source code is `66.3%` smaller, and compressed source code (`.tar.gz`) is
-`83.2%` smaller.
+`50.8%` smaller when measured on clean `.py`-only package snapshots.
-
+
To reproduce that flow locally: