Skip to main content

ez

PyPI Version Python Versions License: AGPLv3+

A cross-platform Python utility library for easy shell interaction and common programming tasks on Linux, macOS, and Windows.


Installation

pip install ez

Requires Python 3.11+.

For packages used by external scripts (Flask and its extensions, Gunicorn, cachelib, DuckDB, parse, PyYAML, xmltodict, and macOS-only imessage_reader):

pip install 'ez[extra]'

These packages are optional and are not installed with plain ez.


Quick Start

from ez import *

# File operations
files = ls('~/Documents', r'\.py$')
cp('source.txt', '~/backup/')
mv('old_name.txt', 'new_name.txt')
rm('temp/')

# Path utilities
print(cwd())                            # current working directory
print(jp('~', 'Documents', 'file'))     # join path components
print(sp('/path/to/file.txt'))          # => ['/path/to', 'file', '.txt']

# Execute shell commands
execute('ls -la')
output = execute2('echo hello')
esp('echo %s', 'world')

# Read/write spreadsheets
data = readx('data.xlsx')
savex('output.xlsx', data, header=['Name', 'Age'])

Function Catalog

Generated by ez/prepublish.py from the same source as ez.__doc__.

Runtime and Errors

Function Aliases Description
ShellDebug debug Set the shell debug mode used by file and process operations.

HTTP and Retries

Function Aliases Description
retry Retry a callable with a timeout and randomized waiting intervals.
rget Fetch an HTTP response as JSON when possible, otherwise as text.
rgetraw Make a retrying HTTP GET request and return its response.
rgetfile Download an HTTP response body to a file, retrying failed requests.

Paths

Function Aliases Description
pwd cwd Imported from os.getcwd.
fullpath fp Resolve an absolute path, expanding variables, user directories, and wildcards.
csd Return the running script directory, or the working directory in interactive mode.
stepfolder Change to a sibling folder selected by step or regular expression.
csf Return the running script filename without its extension.
parentdir pr Return the parent directory of a path.
joinpath jp Join path components, supporting vectorized arguments.
splitpath sp Split a path into directory, filename, and extension.
extpath ep Replace a path's filename extension, supporting vectorized arguments.
cleanpath xp Clean the filename component of a path.

Strings and Regular Expressions

Function Aliases Description
trim Normalize spaces and trim leading or trailing characters.
quote Wrap a value in double quotes.
join Join strings or a sequence using a separator.
sprintf Format a string using supplied values or the caller's local variables.
regexp Find regular expression matches, split text, or match from the start.
regexpi Find regular expression matches without case sensitivity.
regexprep Replace regular expression matches in a string.
regexprepi Replace regular expression matches without case sensitivity.

Directories and Files

Function Aliases Description
cd Change the working directory.
ce Changes to working directory in Finder
cf Changes to working directory in Path Finder, QSpace, or Finder (auto-detected)
ls List matching files in a directory.
lsd List matching subdirectories.
fls List matching files recursively from a directory tree.
flsd List matching folders recursively from a directory tree.
clorox clx Find or delete operating-system metadata files.
mkdir Create a directory and any missing parents.
exists Test whether a path or wildcard pattern exists.
rn Rename a file or folder.
trash Move a file or folder to trash. Requires: pip install Send2Trash
rm Deletes a file or folder.
cp Copies a source file or folder to destination.
mv Moves a source file or folder to destination.
lns Creates a soft link/symbolic link/shortcut.
gapfile Finds missing and extra files based on a regex pattern.
tree Print a directory tree, optionally including files or saving the output.
lines Count lines in matching source files.

Shell and Processes

Function Aliases Description
execute0 Run a shell command and capture its output.
execute Run a shell command without capturing its output.
esp0 Run a formatted shell command and capture its output.
esp Run a formatted shell command without capturing its output.
espR0 Run formatted R code and capture its output.
espR Run formatted R code without capturing its output.
espA Execute formatted AppleScript through a temporary script file.
contextmanager Imported from contextlib.contextmanager.
nooutput Temporarily suppress stdout, stderr in a context

Console, System and Logging

Function Aliases Description
ppprint Imported from pprint.pprint.
error Raise an exception with the supplied message.
pprint Print colored text to the terminal.
beep Beeps to notify user.
where Print the location of a module, function, or Python interpreter.
which Imported from shutil.which.
doc help Print the docstring of a named module, class, or function.
getos Return the operating system name.
ver version Print the installed version of Python or a package.
require Ensure a package is installed at the latest compatible or a specified version via pip or Conda.
evaluate Evaluate a Python expression or execute a statement in the caller's scope.
who whos List imported names in the requested scope.
_setlog Enable or disable redirecting stdout to both a file and the terminal.
logon Enable logging stdout to both a file and the terminal.
clear Remove a module, optionally including submodules, from sys.modules.
logoff Disable file logging and restore stdout.

Randomness

Function Aliases Description
Randomize randomize Imported from random.seed.
RandomizeArray randomizearray Imported from random.shuffle.
Random Imported from random.randint.
RandomChoice randomchoice Imported from random.choice.
keygen Generate a random key.
Permute permute Return all permutations of an iterable as a list.

Time and Waiting

Function Aliases Description
sleep wait Imported from time.sleep.
pause Wait for Enter; return False if interrupted with Ctrl+C.
daylight Test whether a datetime is in daylight saving time in a timezone.

Numbers and General Helpers

Function Aliases Description
iff ifelse Return one of two values according to an expression.
round2 Round a number with a small adjustment for halfway values.
num Convert a value to a number, optionally returning pandas.NA on failure.
isempty Test for None, an empty value, or a missing numeric value.

Collections

Function Aliases Description
replace Replace matching items in a list.
remove Remove matching items from a list.
sort Return a sorted copy of an iterable.
chunk Split the list, xs, into n chunks.
chunk2 Split a list into chunks of a requested size.
is_iterable Test for an iterable, treating strings and tuples as atomic values.
unique Return unique sequence elements in their original order.
union Return the ordered union of two sequences.
intersect Return the ordered intersection of two sequences.
setdiff Return elements of the first sequence absent from the second, in order.
setdiff2 Return the elements of seq1 that are absent from seq2.
compare Print union, intersection, and uniqueness counts for two sequences.
duplicated Find repeated list items or candidate duplicate files in a directory.

Hashing

Function Aliases Description
hashes Hash or verify text with PBKDF2, or calculate a file's digest values.

Data and File Formats

Function Aliases Description
readt Read a text file as a list of lines.
xlcolconv Convert between Excel column numbers and letters.
readx Read an Excel or CSV file into lists.
savex writex Write tabular data to an Excel or CSV file.
savexlist writexlist Write multiple pandas dataframes to sheets in an Excel workbook.
readp Read a Parquet file into a pandas object.
readplist Read and concatenate multiple Parquet files.
mergep Merge matching Parquet files into one file.
savep writep Save a pandas object as a Parquet file.
savek writek Serialize an object to a pickle file.
readk Load and return an object from a pickle file.
readj Load JSON from a file.
savej writej Save a value as JSON, encoding datetime values as ISO strings.
savet writet Write a list of lines to a text file.
savew writew Write lines and an optional heading to a Word document.

Text and Encoding

Function Aliases Description
hanzifreq Count Chinese character frequencies in a text file.
pinyinauthor Format author names as surname and initials for APA references.
encoding_detect Guess the encoding of text or a file.
encoding_convert Convert a file to UTF-8.

Desktop and Keyboard Maestro

Function Aliases Description
opens Open files with their default application.
vx Open a pandas dataframe in a spreadsheet application.
getkmvar Read a Keyboard Maestro environment variable.
setkmvar Set a Keyboard Maestro variable using AppleScript.
runkm Run a Keyboard Maestro macro by UUID.
playfirefoxmacro Run a UI.Vision browser macro in Firefox.
msgbox Display a message using Keyboard Maestro.
menubar Set Keyboard Maestro's menubar text.

Office-to-Markdown Conversion

Function Aliases Description
_output_paths Resolve paths and protect source files and existing outputs.
_output_write Write UTF-8 text to a file or standard output.
_md_combine Combine document Markdown with source headings in input order.
_parse_ranges Parse a one-based comma-separated list of values and ranges.
_md_load_office Load an Office document and normalize missing-member errors.
_md_validate_ocr_mode Validate the Apple Vision text recognition mode.
_md_validate_ocr_language Validate the Apple Vision OCR language selection.
_md_require_vision Load Apple's Vision framework on supported macOS systems.
_md_check_ocr_platform Require macOS when OCR is explicitly requested.
_md_validate_ocr_image Skip malformed or zero-sized embedded images with a visible warning.
_md_vision_ocr Recognize text in image bytes with Apple's local Vision framework.
_md_ocr_block Format recognized image text as a labeled Markdown block.
_md_pptx_image_blobs Yield picture bytes, including pictures nested in grouped shapes.
pptx2md Convert a PowerPoint presentation to Markdown using MarkItDown.
_md_docx_ocr Insert OCR text after each embedded image run in a python-docx document.
docx2md Convert a Word .docx document to Markdown using MarkItDown.
xlsx2md Convert Excel .xlsx worksheets to Markdown tables using openpyxl.
pdf2md Convert text-based PDF pages to Markdown using PyMuPDF and MarkItDown.
_cli_input_files Resolve and validate input files for a batch conversion.
_cli_output_files Build safe output paths for batch input files.
_cli_batch Run a registered tool with separate or combined output files.
_cli Run an explicitly registered CLI tool; return an integer exit status.

Office and PDF Utilities

Function Aliases Description
office_pptx_replace_font Replace fonts in a PowerPoint presentation.
office_pptx_replace Replace matching text in a PowerPoint presentation.
office_docx_replace Replace matching text in a Word document.
office_pdf_from_docx Convert a Word document or folder of documents to PDF.
office_pdf_from Convert documents to PDF using headless LibreOffice.
office_doc2docx Convert documents to DOCX using headless LibreOffice.
office_pdf_unlock Remove PDF password restrictions using Ghostscript.
office_pdf_crop Crop PDF page margins.
office_pdf_bookmarkrm Remove PDF bookmarks.
office_pdf_compress Compress PDF files using Ghostscript.
office_pdf_merge Merge PDF files in the requested order.
office_pdf_autoname Rename PDFs using bibliographic metadata extracted from their text.

AppleScript and Applications

Function Aliases Description
applescript_pages_replace Replace text in the active Pages document using AppleScript.
applescript_pages_pdfactive Export the active Pages document to PDF using AppleScript.
applescript_preview_moveactive Save and move the active Preview PDF using AppleScript.
applescript_finder_alias Create a Finder alias using AppleScript.
applescript_mail Compose or send an Apple Mail message using AppleScript.
applescript_outlook Compose or send an Outlook message using AppleScript.

Clipboard

Function Aliases Description
SetClip setclip Copy content to the clipboard.
GetClip getclip Read and return the current clipboard contents.

Mail and Microsoft Authentication

Function Aliases Description
Mail mail, gmail Send an email using yagmail.
imapget Retrieve emails via IMAP using imap-tools.
gmailget Retrieve Gmail messages matching the supplied filters.
ymailget Retrieve Yahoo Mail messages matching the supplied filters.
o365auth Authenticate to Microsoft 365 and return the account.
outlook Send or draft a Microsoft 365 email.
outlookget Retrieve Microsoft 365 messages from a folder.

OneDrive

Function Aliases Description
onedrive_connect Resolve a OneDrive file or folder path to its remote object.
onedrive_move Move a OneDrive file or folder to another folder.
onedrive_copy Copy a OneDrive file or folder to another folder.
onedrive_delete Move a OneDrive file or folder to the recycle bin.
onedrive_rename Rename a OneDrive file or folder.
onedrive_download Download a OneDrive file or folder.
onedrive_upload Upload a local file to OneDrive, replacing an existing file if needed.
onedrive_share Create a sharing link for a OneDrive item.
onedrive_lsfile List files in a OneDrive folder.
onedrive_lsfolder List subfolders in a OneDrive folder.
onedrive_excel Open a OneDrive Excel workbook session and select a worksheet.
onedrive_readx Read the used range of a OneDrive Excel worksheet.

Password Management

Function Aliases Description
setpassword Store an account password in the km.py keyring service.
getpassword Read an account password from the km.py keyring service.
getpasswordbw Retrieve a Bitwarden item or selected field using its CLI.

Keyboard and Mouse

Function Aliases Description
send Send keyboard keys or text with configurable delays and repetitions.
_mouse Return randomized Bezier path points between two mouse positions.
_move Move the mouse along a randomized Bezier path.
moveclick Move to a point or randomized area location, then click the left mouse button.
click Click the left mouse button the requested number of times.
rclick Click the right mouse button the requested number of times.
scroll Scroll the mouse by the supplied horizontal and vertical amounts.
position Return the current mouse position.
size Return the available display monitor bounds.

Dropbox

Function Aliases Description
dropbox_spaceusage Return available, used, and total Dropbox space in gigabytes.
upload Upload a local file to Dropbox.
download Download a Dropbox file to a local path.
_files_list_folder_all List all Dropbox entries in a folder, following continuation cursors.
lsfile List Dropbox files in a folder, optionally recursively.
lsfolder List Dropbox subfolders, optionally recursively.
delfilefolder Delete a Dropbox file or folder.

Google Drive

Function Aliases Description
_gdrive_save_private Atomically save private Google credentials with owner-only permissions.
_gdrive_authenticate Return an authorized v3 service, caching credentials beside the package.
_gdrive_query_literal Quote a Drive query literal without treating names as query syntax.
_gdrive_list_files Collect all Drive result pages in server order.
_gdrive_parent_changes Translate a Drive destination into v3 parent update parameters.
gauth Authenticate to Google Drive and return the native Drive API v3 service.
gdownload Export or download a Google Drive file.
gupload Upload a file to Google Drive, updating the first matching title if present.
gdelete Permanently delete a Drive file by its ID or name.
gcopy Copy a Google Drive file.
gmove Move a Google Drive file to another folder.
gls List non-trashed files and folders in a Google Drive folder.
gfileid Return the Drive file ID for the first file matching filename in folderid.

Images and PDF Annotations

Function Aliases Description
gif Create an animated GIF from image files.
pdf2gif Create an animated GIF from cropped PDF pages.
_pdfannot_extract Extract annotation Markdown and optionally mark the source PDF.
_pdfannot_cli Extract one PDF's annotations with standard command output options.
pdfannot Extract PDF highlights and annotations into Markdown.

Usage Details

Installing Packages

import ez

ez.require("gTTS", confirm=False, quiet=True)
ez.require("ffmpeg", confirm=False, channel="conda", quiet=True)

quiet=True hides routine helper and installer output; confirmation prompts and errors remain visible, including captured installer output on failure. The default quiet=False preserves live output. Use confirm=False to skip prompts.

Documents to Markdown

import ez

markdown = ez.pptx2md('talk.pptx')  # prints to stdout and returns the Markdown
markdown = ez.pptx2md(
    'talk.pptx', note=True, slides='1-3,5', markers=False,
    images=True, output='talk.md', overwrite=True,
)

All four converters return the Markdown string and print it when output=None. With output='file.md', they write UTF-8 instead of printing. Existing output files raise FileExistsError unless overwrite=True; replacing the input document is always rejected, including symlink/hard-link aliases.

OCR for embedded Office images

pptx2md, docx2md, and xlsx2md can recognize text inside embedded images using Apple's local Vision framework. OCR is disabled by default, requires macOS, and sends no document data to a network service. It is supported on Intel macOS Big Sur and Apple Silicon macOS Sonoma.

The Vision binding is installed as a platform-specific project dependency on macOS only. Use ocr=True in Python or --ocr on the command line. Recognition defaults to Apple's more comprehensive accurate mode; choose fast explicitly when speed matters more. OCR recognizes English by default; use ocr_language="chinese" / --ocr-language chinese for Simplified Chinese, or "both" for both English and Simplified Chinese:

markdown = ez.pptx2md(
    "talk.pptx", ocr=True, ocr_language="english", ocr_mode="accurate",
)
docx2md report.docx --ocr --ocr-mode fast
xlsx2md data.xlsx --ocr --ocr-language chinese
xlsx2md data.xlsx --ocr

The --ocr-mode and --ocr-language CLI options require --ocr. Supported language names are english, chinese (Simplified Chinese), and both. OCR text is added after its slide in PowerPoint, after its image in Word, and under a worksheet heading labeled with the nearest image anchor in Excel. Charts and other non-image objects are not OCRed. On non-macOS systems, conversions without ocr=True / --ocr are unchanged; explicitly requesting OCR reports that Apple Vision is unavailable. A document with no embedded images is converted normally; invalid or zero-sized embedded images produce a warning and are skipped rather than failing the whole conversion. PowerPoint pictures linked to external files rather than embedded in the presentation cannot be OCRed and produce a warning.

PowerPoint

Argument (default) CLI flag Effect
note=False -n, --note Include speaker notes.
slides=None -s, --slides Select 1-based slides, e.g. 1-3,5; default is all non-hidden slides.
markers=True --no-markers Remove <!-- Slide number: N --> comments and separate slides with ---.
images=False --images Embed images as base64 data URIs instead of filename references.
skip_hidden=True --include-hidden Skip hidden slides, even when selected; set False to include them.
ocr=False --ocr Recognize text in embedded images with local Apple Vision.
ocr_mode="accurate" --ocr-mode {accurate,fast} Recognition mode when OCR is enabled.
ocr_language="english" --ocr-language {english,chinese,both} OCR language when OCR is enabled.
output=None -o, --output-file Write UTF-8 Markdown to a file instead of stdout.
overwrite=False --overwrite Allow replacing an existing output file.

Selections retain presentation order, ignore duplicates, and preserve original slide numbers. Invalid or out-of-range selections raise ValueError. Existing output files raise FileExistsError unless overwriting is enabled. The input presentation is never overwritten. Filename image references do not extract image files; use images=True for self-contained Markdown.

pptx2md talk.pptx -n -s 1-3,5 --images -o talk.md
pptx2md --help

Word

markdown = ez.docx2md('report.docx', images=True, output='report.md')

ocr=False is the default. Pass ocr=True or --ocr to recognize text in embedded images; OCR text is inserted after its image in the converted document. ocr_mode accepts "accurate" (default) or "fast" when OCR is enabled. ocr_language accepts "english" (default), "chinese" (Simplified Chinese), or "both".

images=False is the default. Use --images to preserve complete base64 image URIs; otherwise MarkItDown abbreviates them into unusable image references. Supported headings, lists, tables, and links are converted, not exact page layout.

docx2md report.docx --images -o report.md

Excel

markdown = ez.xlsx2md(
    'data.xlsx', sheets=['Annual Sales', 'Summary'], formulas=False,
    headers=True, output='data.md',
)
Argument (default) CLI flag Effect
sheets=None -s, --sheets NAME [NAME ...] Select exact worksheet names; default is all worksheets, including hidden ones.
formulas=False --formulas Emit formula strings instead of cached values.
headers=True --no-headers Generate column-letter headers and include the first row as data.
ocr=False --ocr Recognize text in embedded worksheet images with local Apple Vision.
ocr_mode="accurate" --ocr-mode {accurate,fast} Recognition mode when OCR is enabled.
ocr_language="english" --ocr-language {english,chinese,both} OCR language when OCR is enabled.

Selections retain workbook order and ignore duplicates; unknown names raise ValueError. Each worksheet has a heading and a Markdown table; empty worksheets have only a heading. Trailing empty rows/columns are omitted. Embedded images are not otherwise extracted; charts, styles, and macros are not extracted; merged cells are not expanded.

Formulas are not evaluated. Missing cached formula values produce a warning and empty cells. Calculate and save the workbook in Excel/LibreOffice first, or use formulas=True to emit the formulas themselves.

xlsx2md data.xlsx --sheets "Annual Sales" Summary -o data.md
xlsx2md data.xlsx --formulas --no-headers

PDF

markdown = ez.pdf2md('paper.pdf', pages='1-3,5', markers=False, output='paper.md')
Argument (default) CLI flag Effect
pages=None -p, --pages Select 1-based page ranges; default is all pages.
markers=True --no-markers Remove <!-- Page number: N --> comments and separate pages with ---.

Selections retain document order and original page numbers. This is text-based extraction, not OCR: scanned/blank pages can have no text. Images are not extracted; exact layout/table reconstruction is not guaranteed. Decrypt PDFs requiring a password before conversion.

pdf2md paper.pdf -p 1-3,5 -o paper.md

PDF annotations

Extract highlights and annotations with the installed command:

pdfannot paper.pdf
pdfannot first.pdf second.pdf --output-file notes.md
pdfannot papers --recursive --output-dir notes --no-update-metadata
Python argument (default) CLI flag Effect
pdfpath INPUT [INPUT ...] PDF path or folder; the CLI also supports multiple inputs and wildcard patterns.
mdpath=None -o, --output-file Python prepends to this file and still prints; the CLI uses the shared output rules below instead.
mode=2 --mode {0,1,2} Control the legacy skip rules; default is always to extract.
update_metadata=True --update-metadata, --no-update-metadata Mark extracted PDFs as processed, or opt out to leave them unchanged.
  • Mode 0 applies the timestamp heuristic and skips PDFs whose producer metadata already contains PyHighlightExtractor.
  • Mode 1 applies only the timestamp heuristic: skip when modification time minus creation time is greater than -5 and less than 60 seconds.
  • Mode 2 bypasses both skip rules. PDFs without annotations produce no content.

Both interfaces update PDF metadata by default. The processed marker is added after successful extraction, before output writing. A later output-write failure does not roll back this change. No metadata write occurs for skipped PDFs, PDFs without annotations, or PDFs already carrying the marker. Use --no-update-metadata (Python: update_metadata=False) for read-only extraction. Other metadata fields are retained. Metadata-write errors are reported; in CLI batches, other inputs continue and the command exits nonzero.

ez.pdfannot('paper.pdf', mode=2, update_metadata=False)
ez.pdfannot('paper.pdf', mdpath='notes.md')  # prepends and also prints

The Python function still returns None, accepts PDF folders, and prepends new Markdown to an existing mdpath. Its old force argument is now named mode, and its default changed from 1 to 2; update calls using force= to mode=. The third positional argument still selects the mode. The CLI never prepends to an existing output file.

Batch commands and output

All five installed commands accept multiple files, folders, and wildcard patterns. The four Python document converters still process one file per call; Python pdfannot retains its existing folder support.

pptx2md *.pptx --output-dir markdown --images
docx2md first.docx second.docx --output-dir markdown
docx2md first.docx second.docx --output-file combined.md --overwrite
pdf2md reports --recursive --output-dir markdown
pdf2md "reports/**/*.pdf" --recursive --output-dir markdown
xlsx2md workbooks --output-dir markdown --sheets Summary
pdfannot first.pdf second.pdf -o annotations.md --no-update-metadata
  • More than one distinct input requires either -o/--output-file FILE or --output-dir DIR. These destinations are mutually exclusive.
  • --output-file combines multiple documents in discovery order, adding a source heading (relative to the common input root) before each document. The destination is written once with all successful results, not overwritten for each input. Single-document output retains its existing formatting, without an extra heading.
  • --output-dir writes one .md file per input. A single input without either destination prints to stdout.
  • Folders are scanned for the command's file extension, case-insensitively. Use --recursive for nested folders or quoted ** patterns. Recursive discovery does not follow directory symlinks.
  • Output folders are created as needed. Structure is preserved relative to the common ancestor of input roots: a folder's root is itself, an explicit file's root is its parent, and a wildcard's root is its non-wildcard directory prefix. For example, pdf2md reports --recursive --output-dir markdown maps reports/a/report.pdf to markdown/a/report.md. Explicit inputs a/report.pdf b/report.pdf map to markdown/a/report.md and markdown/b/report.md, rather than overwriting one another.
  • Repeated files and file aliases are converted once. Outputs colliding with another destination or any source document are rejected, even with --overwrite. This protects output destinations; pdfannot may separately update source metadata unless explicitly disabled.
  • All command options apply to every input. Discovery/extraction/output failures are reported to stderr while other files continue. A final summary reports succeeded/failed counts; any failure produces a nonzero exit status. Unmatched patterns and folders with no matching files are errors, not silent successes. Combined output includes successful inputs even if others fail; the exit status is still nonzero. If all inputs fail, no combined file is created or replaced. An empty extraction is a success, not a failure. Unexpected programming errors remain visible.
  • --overwrite permits replacing existing output files; otherwise they are reported as failures. It does not append. Batch output goes to files, not stdout.
  • Breaking CLI change: --output, --force, and -f are no longer accepted. Use -o/--output-file and --overwrite.

Installed commands and future CLI tools

Install this version of the package to generate all five commands:

python -m pip install .
# Or, for local development:
python -m pip install -e .

Activate the Python environment containing ez, or put its executable directory on PATH. All commands support -o/--output-file, --output-dir, --overwrite, and -h/--help. Running any registered command without arguments shows help and exits successfully. Importing or directly running ez/ez.py does not launch a CLI. An existing shell function named pdfannot shadows the installed command; remove it from your shell configuration and run unset -f pdfannot in the current Bash session, or invoke the installed executable by its full path.

_CLI_COMMANDS in ez/ez.py is the single registry of exported tools. To expose another tool, add its function and a registry entry with function, description, arguments, notes, and examples; optional groups reuse argument groups. Each argument is a pair of flag/name strings and an argparse-options dictionary. Use the strings "str", "int", or "float" for typed arguments. Unspecified options are omitted from the function call, preserving the function's defaults. Command names may differ from function names; registered targets must accept the parsed arguments as keywords. The dispatcher ignores normal return values and exits successfully after a successful call.

Batch support is opt-in: these command entries also specify batch metadata (input parameter, supported extension, and expected document errors) and use the shared batch argument group. Tools without this metadata retain normal single-call dispatch. Batch entries specify an output_format from _CLI_OUTPUT_FORMATS, which supplies the output extension and combination function. Current commands use the Markdown format; the shared discovery, registration, output paths, staging, and error reporting are not named or hard-coded as Markdown-only infrastructure. Combination formatting belongs to the configured format.

ez/prepublish.py reads the literal registry without importing runtime dependencies and generates the marked [project.scripts] table in pyproject.toml, targeting the general _cli dispatcher. Run the hook and reinstall the package after adding a command. No new wrapper files or shell functions are needed. Extraction dependencies (MarkItDown, openpyxl, PyMuPDF, pdfannots, and python-pptx) are already declared in the package.

Google Drive

The Drive helpers use Google's native Drive API v3 client and OAuth libraries:

file_id = ez.gfileid("book.docx", folderid)
ez.gdownload(file_id, "/tmp/book.docx")  # download the existing file; no remote creation
name, uploaded_id = ez.gupload("/tmp/book.docx", folderid)
files = ez.gls(folderid)                # [(name, id), ...], across all result pages

Uploaded files are downloaded unchanged. Native Google Docs/Sheets/Slides are exported according to the destination extension supported by gdownload. gdelete deletes permanently; gcopy copies files and gmove moves them between folders. Upload updates the first non-trashed matching name anywhere in Drive, rather than limiting that search to the destination folder.

ez.gauth() returns Google's native v3 service. Use ez.gdownload(file_id, path) for downloads, or access the service directly:

service = ez.gauth()
metadata = service.files().get(fileId=file_id, fields="id,name,mimeType").execute()

ez.gauth() requires GDRIVE_KEY imported from the package's private pysecrets.py module. It does not read or write gdrive_secrets.json. Client settings must contain an installed/web Google OAuth client configuration. The cached token is reused and refreshed as needed. Authentication does not change the working directory.

When no token is cached, the call opens your browser for Google login and consent, then saves gdrive_token.json beside the installed module after authorization succeeds. It does not create a separate token.json. Browser authorization requires OAuth client settings in GDRIVE_KEY. Subsequent calls reuse the token and refresh it when expired. Malformed tokens, failed refreshes, and write errors are reported rather than silently restarting authorization. New credential files are written atomically with owner-only permissions; never commit or share them. Credential cache files are excluded from built source archives and wheels. The helper requests full Drive access for its read/write/delete operations. The package directory must be writable for credential caching.

Existing Web application OAuth clients can be reused without creating a new client. Browser login selects the first supported entry in the client's redirect_uris: http://localhost:PORT/ or http://127.0.0.1:PORT/ (also supported without the trailing slash). The host, port, and trailing slash are preserved exactly. The selected port must be free. If no supported entry exists, add one such as http://localhost:8080/ to the same client's authorized redirect URIs in Google Cloud Console, then update GDRIVE_KEY to include it. Desktop clients continue to use an automatically selected local port.

ez.GCal() similarly requires GCAL_KEY imported from pysecrets.py and passes the settings directly to Google's OAuth flow without writing gcal_secrets.json. It preserves the existing gcal_token.pickle cache beside the installed module, reuses valid tokens, and saves refreshed tokens. Initial Calendar login uses local port 8080. Neither helper changes the working directory. Existing client-settings files are left untouched but are no longer used. Only load Calendar pickle tokens from a trusted source.

Classes

JDict() is a custom ordered dictionary with convenient attribute access and helper methods. Use help(JDict) for details. Moment(timezone) returns the current datetime in a specified timezone, or a local naive datetime if omitted.

Pipe

Chain operations using a pipe-style syntax:

# Basic pipe
[1, 2, 3, 0] > ez.pipe | len | str

# Countdown example
ez.pipe | (range, -1) | reversed | ez.pipetools.foreach('{0}...') | ' '.join | '{0} boom'

General Notes

  • Almost all path-related commands support ~, .., ., ?, and *. The exceptions are ls and fls, which use regular expressions for filtering.
  • File operations target symbolic links themselves, not the underlying files they point to.

Maintaining the Function Catalog

The topic regions and function docstrings in ez/ez.py are the source of truth for both the generated ez.__doc__ catalog and the README function catalog. Both list the same topics, functions, aliases, and summaries. Each module-level function belongs to a topic region and a nested region named after the function. Keep a concise description on the first line of its docstring; existing parameter details and examples follow it. Keep function aliases inside the same topic. Imported convenience functions inside topic regions are listed with their source module. Class methods and nested functions are not cataloged.

ez/prepublish.py reads this structure using only the Python standard library, without importing ez or its optional runtime dependencies. It replaces only the module's __doc__ assignment and the README section between the BEGIN GENERATED FUNCTION CATALOG and END GENERATED FUNCTION CATALOG markers. Do not edit either generated catalog by hand; edit the source docstrings or regions instead. Usage details outside the markers remain hand-maintained.

python ez/prepublish.py --docs-only   # regenerate both catalogs; do not bump the version
python ez/prepublish.py --check-docs  # exit nonzero if either catalog is stale or invalid
python ez/prepublish.py --help

Running the hook without flags, or with --docs-only, regenerates both catalogs and the marked CLI entry points in pyproject.toml. --check-docs checks all three generated outputs without writing. All outputs are validated before any are written. The hook no longer increments the version; version changes belong to the publishing workflow below. Paths are relative to the hook, not the current working directory.

Run the focused maintenance tests with:

python -m unittest discover -s tests

Building and Publishing

Install uv for release work. The project uses the native uv_build backend; setuptools and Twine are no longer needed. Package identity, dependencies, and the authoritative release version live in pyproject.toml. ez/version.py reads that version in a source checkout or uses installed distribution metadata.

To regenerate metadata and build without publishing or bumping the version:

uv run --no-project --python '>=3.11' ez/prepublish.py --docs-only
uv build

The build produces an sdist (.tar.gz) and a py3-none-any.whl wheel. The wheel contains pure Python and works with supported Python versions on Windows, Linux, Intel Macs, and ARM Macs, regardless of the build machine. Dependencies still need compatible releases for the target platform, and OS-specific features retain their existing limitations and dependency markers.

PyPI authentication

The publishers prefer UV_PUBLISH_TOKEN. Otherwise they automatically read ~/.pypirc, using only its [pypi] section:

[pypi]
username = __token__
password = YOUR_PYPI_API_TOKEN
repository = https://upload.pypi.org/legacy/

Reuse an existing PyPI API token, preferably scoped to the ez project, or create one in your PyPI account settings. repository is optional but must point to the standard PyPI upload URL if set. Protect the file with chmod 600 ~/.pypirc; never commit it or the token. Missing, malformed, or non-token credentials stop the release before preparation. The token is passed to uv through its process environment, not command-line arguments or release logs.

This .pypirc support is implemented by the shared scripts/publish.py helper, not by uv itself. Standalone uv publish needs uv-native credentials, such as UV_PUBLISH_TOKEN. A Finder-launched app normally does not inherit terminal exports; the .pypirc fallback lets both entry points use the same existing credentials.

Full release

From a terminal, run:

bash publish.sh
bash publish.sh --help

The shell script can also be run directly as ./publish.sh.

Alternatively, open Publish.app in the checkout. Its resource script contains a separate, manually maintained copy of publish.sh, matching the original app arrangement. It does not invoke or require the external shell script, but still requires the checkout's publishing helper, prepublish hook, and package files. When changing either shell script, update the other copy too; a regression test checks their current parity. Both resolve the checkout from their own locations, including paths with spaces.

The publisher loads ~/.bash_profile if present and checks for uv, Git, and your existing ezupdate command/function/alias before preparing a release. It then:

  1. Validates credentials and the version format.
  2. Runs prepublish to update catalogs and CLI entry points.
  3. Uses uv version --bump COMPONENT --frozen, preserving the existing numbering: patch normally (11.8.8 → 11.8.9), minor when patch is 9 (11.8.9 → 11.9.0), and major when both minor and patch are 9 (11.9.9 → 12.0.0). Minor and patch must remain single digits. uv updates the version without locking or syncing.
  4. Builds both artifacts in a fresh temporary directory, retains them in dist/, then deletes older ez source archives and wheels so only the latest release's two artifacts remain. Cleanup occurs only after a successful build and copy; unrelated files and directories are not removed. It publishes only the new files with uv publish. Existing files for the new version are not overwritten.
  5. Stages all checkout changes, commits, pushes to origin master, then runs ezupdate three times, preserving the existing post-release workflow.
  6. Prints the distribution version and files in dist/, followed by the locally installed ez version and installation location from uv pip show ez. This reports uv's selected Python environment, which may differ from the environment your ezupdate command upgrades. A missing package or reporting error is shown explicitly and stops the script rather than reporting success.

Errors stop subsequent steps. A failed build/upload may leave the version bumped and artifacts in dist/; inspect the state before retrying. A partial upload or a later Git/upgrade failure does not undo files already published to PyPI. To retry an upload, use the exact retained artifacts and provide uv-native authentication; do not blindly rerun the full publisher, which bumps again. Use prepublish's check/docs-only modes, not the publisher, for documentation work.


License

GNU Affero General Public License v3 or later (AGPLv3+)

Metadata

Release files for ez 12.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ez 12.0.1
File Size Uploaded Provenance
ez-12.0.1.tar.gz 162.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for ez 12.0.1
File Interpreter ABI Platform
ez-12.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 280.3 kB

Release files / ez-12.0.1.tar.gz

Download URL ez-12.0.1.tar.gz
Size 162.6 kB
Tags Source
SHA-256 checksum
How to use checksums
89162668a63336682f2022a581182f769eeb5898fe0f0f17ec8ef306900c766f
BLAKE2b-256 checksum
How to use checksums
5704a9b6515fe43f927c6da12219eab11a263e99ae021a292253adbb69ba3266
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / ez-12.0.1-py3-none-any.whl

Download URL ez-12.0.1-py3-none-any.whl
Size 117.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
10bfd1096ecfc36b14b312f1d36243f6d847a9086b69d2163f3726ddd447ff92
BLAKE2b-256 checksum
How to use checksums
6431ed243be9c11a52dfb25e5e45fd537bd6a8022ab0e3b5c9d7db2e5ab73096
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for ez 12.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

12.0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page