Skip to content

Releases: JeanExtreme002/pymacos

v1.19.1

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 01 Oct 17:45
bfcbc92
pip install --upgrade pymacos

A bug fix release.

Fixed

  • vision and PDFs. The docs listed PDF among the formats the vision functions read, but they failed on one: lines, text, faces and the others with a cryptic "zero-dimensioned image (0 x 0)", smart_crop and scan_document with "not an image macOS can read". A PDF now raises a clear ValueError that shows the way: draw the pages you want with pdf.render first.
    macos.vision.text(macos.pdf.render("scan.pdf", page=1, size=2048))

Docs: https://macos.readthedocs.io

v1.19.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 01 Oct 05:29
22a4d94
pip install --upgrade pymacos

1.19 removes sensitive text from PDFs for good, fixes two defaults bugs, and comes with reviewed docs and ready-to-use recipes. Still no dependencies.

New

  • pdf.redact() blacks out text in a PDF and removes it, rather than drawing a box over text that can still be copied. It takes texts, matched as whole words across line breaks, or regular expressions. It also clears the matches from form fields, comments, the title and the bookmarks, and tells how many it found on each page.
    done = macos.pdf.redact("contract.pdf", ["Jane Doe", re.compile(r"\b\d{3}-\d{2}-\d{4}\b")], "public.pdf")
    done.matches   # {'Jane Doe': 3, '\\b\\d{3}-\\d{2}-\\d{4}\\b': 1}
    done.pages     # {1: 2, 4: 2}: the pages to look over before sharing
    If a target isn't found anywhere, it raises and writes nothing.

Fixes

  • defaults.delete() no longer reports True for a key that only the global domain sets.
  • defaults.restored() no longer writes a key into an app's domain on exit when that key was only set globally. It also puts back an app's own key whose value equals the global one.

Docs

  • Recipes: short scripts for everyday jobs.
    • Tidy the Downloads folder as files arrive.
    • Black out Social Security numbers in a folder of PDFs.
    • Get warned before the AirPods run out.
    • A window manager in a few lines.
    • Turn Word documents into PDFs.
    • Know when a long job ends.
    • Copy the text out of the last screenshot.
  • A full review against the code.
    • Corrected:
      • which exceptions derive from MacOSError;
      • when hotkeys stop shortcuts from reaching the front app;
      • the mouse permissions;
      • the Wi-Fi quality thresholds;
      • the two meanings of display in screen;
      • 16-bit audio edits.
    • Added: the parameters, return values, errors and macOS versions that were missing.
  • The API reference is now in alphabetical order, and the examples are written for a global audience.
  • Doc pages shared on social networks or in chat now show a link preview.

Docs: https://macos.readthedocs.io

v1.18.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 30 Sep 23:27
351c830
pip install --upgrade pymacos

1.18 automates everyday chores: scripts that run when a folder changes or a disk is plugged in, documents converted without Word, apps uninstalled with their leftovers, VPNs switched from code, windows tiled in a grid. Still no dependencies.

New

  • Run a script when something happens: when a folder changes, or when a disk is mounted. It keeps working after a restart, with no Python left running.
    macos.schedule.add("tidy", "tidy_downloads.py", when_changed="~/Downloads")
    macos.schedule.add("backup", "copy_photos.py", at_mount=True)
  • macos.document converts between Word, RTF, HTML, OpenDocument, plain text and PDF, and reads a document's text, with no Word or LibreOffice needed.
    macos.document.convert("report.docx", "report.pdf")
    macos.document.text("minutes.docx")
  • apps.uninstall() moves an app to the Trash with the files it left in your Library. dry_run=True shows what would go.
    macos.apps.uninstall("Slack", dry_run=True)
  • VPNs: list the ones set up in System Settings, connect and disconnect them.
    macos.network.connect_vpn("Office")
  • power.sleep_blockers() tells what keeps the Mac from sleeping.
  • network.bandwidth() tells how fast data is going through each network interface right now.
  • Time Machine exclusions: leave a folder out of the backups, or back it up again.
    macos.time_machine.exclude("~/code/app/node_modules")
  • windows.tile_all() arranges the windows side by side in a grid; windows.tile() does the same with the windows you pick.

Notes

  • document.convert converts as TextEdit would: the page layout of Word documents (columns, headers and footers, text boxes) is lost, and tables become lines when writing .docx or .doc.
  • apps.uninstall only touches your own Library, not /Library, and refuses apps that are running or that come with macOS.
  • network.vpns() lists the VPNs in System Settings; VPN apps that don't add theirs there, and possibly IKEv2 VPNs, aren't listed.

Docs: https://macos.readthedocs.io

v1.17.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 30 Sep 06:54
9309ceb
pip install --upgrade pymacos

1.17 helps diagnose a Mac: the Wi-Fi signal, what starts by itself, the crashes and the system log, what's plugged in, the network setup and what fills the disk. PDFs get a table of contents, their pictures, and signatures placed by a label. Still no dependencies.

New

  • Place a signature or a text beside a label of the page, instead of computing points.
    macos.pdf.sign("contract.pdf", "signature.png", "signed.pdf", near="Signature:")
    macos.pdf.add_text("form.pdf", "Ana Souza", "filled.pdf", near="Name:")
  • PDF tables of contents and pictures:
    • read and write a PDF's bookmarks;
    • save the images a PDF holds.
      macos.pdf.set_bookmarks("book.pdf", [("Intro", 1), ("Chapter 1", 3), ("1.1", 4, 1)], "book-toc.pdf")
      macos.pdf.images("brochure.pdf", "brochure-images")
  • network.wifi_signal() tells the signal, noise, quality, speed and channel, without the Location permission.
  • Network setup: every interface with its addresses, the DNS servers and the proxies in use.
    macos.network.wifi_signal().quality   # 'good'
    macos.network.dns_servers()           # ['192.168.0.1', '8.8.8.8']
  • What starts by itself: launch agents and daemons, with what they run and whether they're running.
    [item.label for item in macos.system.startup_items() if item.running]
  • Crashes and the log: the crash reports macOS recorded, and the system log, filtered by process, text or level.
    macos.system.crash_reports(since=datetime.now() - timedelta(days=7))
    macos.system.logs(process="Safari", level="error", last="1h")
  • What's installed and plugged in:
    • every installed app, with its version;
    • the USB devices;
    • the charger's wattage.
  • finder.largest() finds the largest files in a folder, quickly, through Spotlight.

Fixes

  • The video tests skip, instead of failing, when screencapture hangs on a CI machine.

Notes

  • system.logs() stops at limit messages (1000 by default): the log holds thousands a minute.
  • system.startup_items() leaves out Apple's own, in /System. For the apps opened at login, see apps.login_items().
  • The Wi-Fi signal has no network name: macOS keeps it behind the Location permission, which Python run from a terminal can't get.

Docs: https://macos.readthedocs.io

v1.16.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 30 Sep 05:19
6e18fdb
pip install --upgrade pymacos

1.16 tells how fast the internet is and what uses the network, the battery and the GPU, warns before a disk fails, and fills in, signs and annotates PDFs. Still no dependencies.

New

  • network.speed_test() measures download and upload speed, and latency idle and under load, against Apple's servers.
    result = macos.network.speed_test()
    result.download, result.upload   # (43.61, 39.42): megabits per second
  • What uses what:
    • how much each process sent and received over the network;
    • the power each process draws, in watts, with its disk reads and writes.
      macos.system.network_usage(interval=2)[:5]
      macos.system.energy_usage()[0]   # EnergyUsage(process='Xcode', watts=4.2, ...)
  • system.gpu_usage() tells how busy the GPU is, and system.disk_health() gives each disk's SMART status, which warns when a disk is about to fail.
  • PDF forms: read a form's fields and fill them in by name.
    macos.pdf.fill_form("form.pdf", {"Full name": "Ana Souza", "Agree": True, "Plan": "Pro"}, "filled.pdf")
  • pdf.sign() puts a signature image on a page, in a corner or at a point, with filled-in fields still showing.
  • pdf.add_text() writes text on a page as a text box, still editable in Preview, rotated pages included.
    macos.pdf.sign("filled.pdf", "signature.png", "signed.pdf")
    macos.pdf.add_text("contract.pdf", "Received on 29/09/2026", "stamped.pdf")

Fixes

  • screen.record gives up after the recording plus a minute, instead of waiting for good on a screencapture that hangs.

Notes

  • The speed test takes 15 to 60 seconds and moves a few hundred megabytes.
  • Power is measured on Apple silicon; Intel Macs read 0. Energy use sees this user's processes; network use sees every user's.
  • A signature is an image, not a cryptographic signature. Signing flattens the pages: fill the form first.

Docs: https://macos.readthedocs.io

v1.15.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 30 Sep 02:44
4cb91d8
pip install --upgrade pymacos

1.15 looks into what your Mac is running: the processes and their CPU, the ports they listen on, the connections they have open, and the files they hold, like Activity Monitor and lsof. It also turns addresses into coordinates and back, opens the Maps app, and sets custom Finder icons. Still no dependencies.

New

  • Processes: list them with their memory, processor time and % CPU, and end one.
    busiest = sorted(macos.system.processes(cpu=True), key=lambda p: p.cpu_percent or 0)[-1]
    busiest.name, busiest.cpu_percent   # ('Xcode', 187.5)
  • Ports and connections: what listens on which port, and who each process is talking to.
    macos.system.port_owner(8000).kill()   # free the port
    for connection in macos.system.connections():
        print(connection.process, connection.remote_address, connection.remote_port)
  • Open files: which processes use a file, a folder or a disk. It tells why a disk won't eject.
    macos.system.who_uses("/Volumes/Backup")   # [Process(name='Preview', ...)]
  • macos.maps turns an address into coordinates and back, through Apple's geocoding service, and opens the Maps app on a place or a route.
    macos.maps.geocode("Avenida Paulista, 1578, São Paulo")[0].latitude   # -23.561463
    macos.maps.directions("Aeroporto de Congonhas", by="transit")
  • Custom icons: give a folder, a file or an app an icon, from an image or another item's icon.
    macos.finder.set_icon("~/Projects", "logo.png")

Notes

  • Processes, ports, connections and open files see every detail of this user's processes. Other users' processes, the system's included, show less: macOS keeps it from this user, as lsof without sudo.
  • Geocoding needs the internet but no permission. Apple limits how many requests an app makes in a short time.
  • The pymacos wordmark has some room above its letters now.

Docs: https://macos.readthedocs.io

v1.14.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 29 Sep 22:03
6d7cc09
pip install --upgrade pymacos

1.14 prints, saves your Mac's settings to a file and applies them to another Mac, and changes the resolution of your displays. It also covers the settings people look for most: macOS's own shortcuts, trackpad gestures, pointer acceleration, the menu bar's icons. Still no dependencies.

New

  • macos.settings saves this Mac's settings to JSON and applies them to another: dotfiles for macOS. Only what differs changes, and the Dock and Finder restart once.
    json.dump(macos.settings.export(), open("my-mac.json", "w"), indent=2)
    macos.settings.apply(json.load(open("my-mac.json")))   # on the new Mac
  • macos.printer lists the printers, prints files and follows the queue, through CUPS.
    job = macos.printer.print_file("report.pdf", copies=2, two_sided=True)
    job.cancel()
  • Displays: list and change the resolution and refresh rate, pick the main display, mirror a projector.
    macos.screen.set_display_mode(1728, 1117)
    macos.screen.mirror(projector)
  • Keyboard:
    • turn macOS's shortcuts on and off, to free ⌘Space for Raycast or Alfred;
    • give an app's menu items a shortcut;
    • what the Fn (🌐) key does, inline predictions, the backlight's timeout.
      macos.keyboard.set_system_shortcut("spotlight", False)
      macos.keyboard.set_app_shortcut("Safari", "Export as PDF…", "cmd+shift+e")
  • Trackpad and mouse:
    • every trackpad gesture, and the click pressure;
    • the mouse's pointer acceleration.
      macos.trackpad.set_gesture("mission_control", 4)
      macos.mouse.set_acceleration(False)
  • Dock:
    • folders and stacks;
    • hot corners that wait for a key;
    • dimmed hidden apps, only open apps, the launch animation;
    • window grouping and switching spaces in Mission Control.
  • Menu bar: show or hide Control Center's icons, and space them so more fit beside the notch.
  • Finder: the desktop's icon size, spacing and order, and a Quit menu.
  • Screen: Night Shift's schedule and strength, screenshots' file name and destination.
  • Sound: the alert sound and its volume, and the interface's sound effects.
  • System:
    • units and temperature;
    • Photos opening when an iPhone is connected;
    • window animations, font smoothing.
  • macos.system.security_status() tells whether FileVault, the firewall, Gatekeeper and SIP are on, without an administrator's password.
  • macos.apps.unquarantine() fixes "is damaged and can't be opened" for an app you trust.
  • macos.defaults.restored() changes preferences for the length of a with block, and puts them back exactly.

Fixes

  • Tap to click and three-finger drag also write the copy System Settings keeps for this Mac.

Notes

  • Changing the Dock or Finder settings restarts them, to apply the change. Some settings wait for the next login, as the docs say.
  • The pymacos wordmark's "y" is now Python's yellow.

Docs: https://macos.readthedocs.io

v1.13.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 29 Sep 17:17
c83235d
pip install --upgrade pymacos

1.13 puts System Settings in your scripts: the keyboard, trackpad, mouse, Dock, Finder, windows, appearance and menu bar, each with a reader and a setter. Set up a new Mac in a few lines, remap keys without Karabiner, and ask for Touch ID before a function runs. Still no dependencies.

New

  • Keyboard settings: key repeat, press and hold, standard function keys, autocorrect, smart quotes and dashes, auto capitalization, the double-space period and full keyboard access.
    macos.keyboard.set_key_repeat(0.03, delay=0.225)   # System Settings' fastest
    macos.keyboard.set_press_and_hold(False)
  • keyboard.remap() makes a key act as another, on every keyboard, with no app to install.
    macos.keyboard.remap("caps_lock", "escape")
  • macos.trackpad: tap to click, natural scrolling, the pointer speed, three-finger drag and the secondary click. The mouse gets its pointer, scroll and double-click speeds.
  • Dock:
    • hot corners;
    • magnification, the minimize effect, open-app dots, recent apps;
    • the autohide delay and animation;
    • spacers between the apps;
    • Mission Control's spaces.
      macos.dock.set_hot_corner("bottom_right", "lock_screen")
      macos.dock.set_autohide_delay(0)
  • Finder:
    • the default view, the new window folder and the search scope;
    • folders first, the full path in the title;
    • the desktop icons and disks, the Library folder;
    • the extension warning, old Trash items.
  • Windows: what a double click on the title bar does, tiling by dragging to an edge, and clicking the wallpaper to show the desktop.
  • Appearance: the accent color, auto Light/Dark mode, hiding the menu bar and the scroll bars.
    macos.appearance.set_accent_color("purple")
  • System:
    • the menu bar clock, the battery percentage;
    • .DS_Store files on network and USB drives;
    • reopening windows, saving to iCloud by default, the expanded Save dialog.
  • Screen: the screen saver delay and the screenshot thumbnail.
  • @macos.auth.required asks for Touch ID (or the password) each time a function is called, before it runs.
    @macos.auth.required("deploy to production")
    def deploy(): ...
  • macos.defaults takes current_host=True, like defaults -currentHost, for the settings kept per Mac.

Notes

  • Most settings apply at once or when an app is reopened. The key repeat, pointer and scroll speeds, .DS_Store files and spaces per display wait for the next login, as the docs say.
  • Changing the Dock or the Finder settings restarts them, to apply the change.
  • keyboard.remap() lasts until the Mac restarts: run it at login with macos.schedule to keep it.
  • None of these settings needs a permission.

Docs: https://macos.readthedocs.io

v1.12.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 29 Sep 15:02
8bf2b38
pip install --upgrade pymacos

1.12 sets up your Mac from Python: preferences, the Dock, Finder, login items and default apps. It also adds Touch ID in your scripts, window snapping and searchable scanned PDFs. Still no dependencies.

New

  • macos.defaults reads and changes the preferences of apps and of the system, like the defaults command, but with Python types: no -bool or -int flags to get right.
    macos.defaults.write("com.apple.finder", "ShowPathbar", True)
    macos.defaults.read("NSGlobalDomain", "AppleInterfaceStyle")   # 'Dark'
  • macos.dock hides, sizes and moves the Dock, and chooses the apps kept in it.
    macos.dock.set_autohide(True)
    macos.dock.add_app("Visual Studio Code")
  • Finder and screenshot settings: show hidden files, extensions, the path and status bars, and where and how the screenshot shortcuts save.
  • macos.auth.confirm() asks for Touch ID (or the login password, or an Apple Watch) before a script does something sensitive.
    if macos.auth.confirm("unlock the production credentials"):
        token = macos.keychain.get("deploy", "prod")
  • Window.snap() fits a window to a half, a quarter, a third, or the whole screen, like Rectangle.
    macos.windows.focused().snap("left")
  • pdf.ocr() makes scanned PDFs searchable, adding the text Vision reads invisibly over each word.
  • Apps:
    • login_items(), add_login_item() and remove_login_item();
    • set_default_for("md", "Visual Studio Code");
    • install_from_dmg(), which installs the app from a disk image.
  • System:
    • mount_image() and unmount_image();
    • available_updates();
    • cpu_usage() and memory_usage();
    • macos.time_machine, to start and follow backups.
  • Finder: compress() and extract() (zip, keeping tags and attributes), and quick_look().

Fixes

  • Window.set_frame resizes before moving, so macOS no longer cuts a window moved near the bottom of the screen.
  • macos.events no longer frees its observer twice, which crashed a script calling events.run() inside an autorelease pool.

Notes

  • Since macOS 26, macOS asks the user to confirm a new default app, and set_default_for waits for the answer.
  • Changing the Dock or the Finder settings restarts them, to apply the change.
  • login_items() asks once for the Automation of System Events.

Docs: https://macos.readthedocs.io

v1.11.0

Choose a tag to compare

@JeanExtreme002 JeanExtreme002 released this 29 Sep 06:37
d5dbb8f
pip install --upgrade pymacos

1.11 continues with automation: find and click text on the screen, listen to the keyboard and mouse, and react to the network, USB devices and displays. Still no dependencies.

New

  • mouse.click_text() clicks a text wherever it shows on the screen, in any app, so a script doesn't depend on where a button is. screen.find_text() returns where the text is, and screen.wait_for_text() waits for it to show up. They use Vision's text recognition, and the box surrounds just the matching word.
    macos.mouse.click_text("Accept")
    macos.screen.wait_for_text("Export complete", timeout=120)
  • keyboard.watch() and mouse.watch() yield every key pressed and every click, in any app. They only listen: the input still reaches the apps.
    for key in macos.keyboard.watch():
        print(key.shortcut)            # 'cmd+shift+k'
  • macos.events gains:
    • network_changed: another Wi-Fi network, a cable, offline, back online;
    • usb_connected and usb_disconnected, with the device's name;
    • displays_changed: a monitor connected, removed or rearranged.
      macos.events.on("usb_connected", lambda event: print("Plugged in:", event.device))
      macos.events.run()
  • Windows:
    • Window.screenshot() captures one window, even behind others.
    • windows.wait_for() waits for a window to open.
  • Finder:
    • finder.selection() gives the files selected in Finder, and finder.current_folder() the folder in its front window.
    • finder.watch() takes a pattern ("*.pdf").
  • More:
    • clipboard.watch() yields every copy, for a clipboard history.
    • system.wait_for_idle() and wait_for_activity() wait for you to step away, and come back.
    • screen.color_at(x, y) reads the color of a point on the screen.

Notes

  • The screen functions and Window.screenshot() need the Screen Recording permission. click_text() also needs Accessibility, and keyboard.watch() / mouse.watch() need Input Monitoring. finder.selection() asks once for the Automation of Finder. See https://macos.readthedocs.io/en/latest/permissions.html.
  • keyboard.watch() doesn't see what's typed in password fields: macOS hides it.

Docs: https://macos.readthedocs.io