Repository navigation
Optional: a python -m docx CLI for inspection and cleanup #118
Description
The inspection and cleanup work in #114, #115 and #116 is all useful from a shell, and most of the questions people have about a .docx are one-off: what styles does this thing define, which are actually used, why is my file 900 KB. Writing a five-line script for that each time is friction.
$ python -m docx styles report house-template.dotx
164 styles defined (36 paragraph, 27 character, 100 table, 1 numbering)
3 in use, 161 unused
137 latent style exceptions
$ python -m docx styles list --unused report.docx
$ python -m docx cleanup report.docx -o report-clean.docx
$ python -m docx styles extract house.dotx --names "Quote,Caption" -o styles.docx
$ python -m docx info report.docx # parts, sizes, content types, relsShape
- A
__main__.pyand adocx.climodule, argparse only — no new runtime dependency - Subcommands mapping one-to-one onto the library operations, so the CLI stays a thin shell over the public API and never grows logic of its own
inforeporting the part inventory with sizes, which is what you actually want when a document is unexpectedly large — this is how the 438 KBstylesWithEffects.xmlin Default template ships 447 KB of parts no generated document needs #113 turned up- Optionally a
[project.scripts]entry point;python -m docxalone is enough to start and commits to nothing
This one is genuinely optional
It is worth saying plainly that a CLI is a real commitment for a library: an argument surface that has to stay backward compatible, its own help text and docs, and a class of bug report ("the CLI prints this wrong") that is not about the library. python-docx has never shipped one, and there is a reasonable position that it should not.
Two things push the other way. The first is that the operations in the three issues above are diagnostic — you run them to find out something about a file, not as part of an application — and a diagnostic API with no command-line front end mostly does not get used. The second is that python -m docx info is the fastest way to answer the most common "why is this file like this" question, and there is currently no way to answer it without opening the zip by hand.
If the answer is no, the three library issues stand on their own and lose nothing. Close this one and the rest still make sense.
If it goes ahead
- Every subcommand takes the document path first and writes to
-orather than in place. Never modify the input by default —cleanupis destructive and someone will point it at their only copy. - Exit codes matter for scripting: non-zero on a document that cannot be opened, and a
--checkmode forcleanupthat reports what it would remove and exits non-zero if anything would be, so it can be a CI gate. - Keep the output greppable. A
--jsonflag on the reporting commands costs nothing and makes the whole thing composable.
Activity
toxicphreAK commentedon Aug 5, 2026
Landed in #139. On the genuinely-optional question: yes, on the terms the issue set.
python -m docxwithinfo,styles report/list/extractandcleanup; argparse only, no new dependency, and every subcommand is a thin shell over one public library operation so the CLI cannot grow logic of its own.The three conditions are met:
cleanuprefuses to write to its input and requires-o; exit codes are non-zero for a document that cannot be opened and for--checkwhen something would be removed, so it can gate CI; and--jsonis on every reporting command. No[project.scripts]entry point — as the issue said,python -m docxcommits to nothing.