Skip to main content

🐦 sapsucker

License: MIT Python Version PyPI version Unittests Linting Formatting Coverage

Typed Python wrapper for the SAP GUI Scripting API. For a Go client around the SAP ADT API, check adtler.

sapsucker gives you typed, IDE-friendly access to SAP GUI for Windows. Instead of working with raw COM objects and guessing method names, you get Python classes with autocomplete, type hints, and docstrings for every SAP GUI element.

Named after the sapsucker — a woodpecker that taps into trees to drink their sap. This library taps into SAP GUI to extract your data.

Quickstart

from sapsucker import SapGui

# Connect to running SAP GUI
app = SapGui.connect()
session = app.connections[0].sessions[0]

# Read session info
print(session.info.system_name)   # → "S4H"
print(session.info.user)          # → "DEVELOPER"

# Navigate to a transaction
session.find_by_id("wnd[0]/tbar[0]/okcd").text = "/nSE16"
session.find_by_id("wnd[0]").send_v_key(0)  # Enter

# Read the status bar
print(session.find_by_id("wnd[0]/sbar").text)

Why sapsucker?

  • Read a whole screen in one call — container.dump_tree() walks any screen recursively and returns typed ElementInfo, so you can discover element IDs instead of guessing them (the whole subtree in a single COM round trip on SAP GUI >= 7.70 PL3)
  • 40+ typed wrapper classes — GuiGridView.get_cell_value(), GuiTree.expand_node(), not generic element.read("cell", row, col)
  • IDE autocomplete & type hints on every method and property
  • 430+ unit tests, 50+ integration tests verified against real SAP S/4 HANA
  • API verified against the SAP GUI Scripting API 6.40 PDF (2969 pages)
  • MIT licensed — no GPL restrictions

Installation

pip install sapsucker

Prerequisites

  • SAP GUI for Windows (7.x or 8.x)
  • SAP GUI Scripting enabled — ask your SAP Basis team to set sapgui/user_scripting = TRUE in transaction RZ11, and enable scripting in your SAP GUI options (Customize Local Layout → Accessibility & Scripting)
  • Python 3.11+ on Windows

Usage Examples

Read an entire screen

Don't know a screen's element IDs? Walk it. dump_tree() recurses through every child container and returns validated ElementInfo objects — id, type, name, text, tooltips, accessibility text, geometry and more (sapsucker.models.ElementInfo).

from sapsucker import SapGui
from sapsucker.models import ElementInfo

app = SapGui.connect()
session = app.connections[0].sessions[0]

window = session.find_by_id("wnd[0]")

def walk(elements: list[ElementInfo], depth: int = 0) -> None:
    for element in elements:
        print("  " * depth, element.id, element.type, element.text)
        walk(element.children, depth + 1)   # ElementInfo nests its children

walk(window.dump_tree())                    # full depth by default (safety cap: 200)
# walk(window.dump_tree(max_depth=2))       # or bound it

On SAP GUI for Windows >= 7.70 PL3 dump_tree() uses GuiSession.GetObjectTree under the hood — the whole subtree in a single COM round trip — and falls back automatically to per-property reads on older releases.

Feeding a screen to an LLM? Call session.get_object_tree() directly and ask for only the properties you need instead of the full element record. Unlike dump_tree(), this call has no fallback — it raises on SAP GUI older than 7.70 PL3.

import json

# A JSON *string*, one COM call, only the properties you list
raw = session.get_object_tree("wnd[0]", props=["Id", "Type", "Text"])
tree = json.loads(raw)          # the queried element is tree["children"][0]

# props=None returns Id only — the cheapest possible screen dump
ids_only_json = session.get_object_tree("wnd[0]")

Read an ALV grid

from sapsucker import SapGui
from sapsucker.components.grid import GuiGridView

app = SapGui.connect()
session = app.connections[0].sessions[0]

# Find the grid on the current screen
grid = session.find_by_id("wnd[0]/shellcont/shell")

# Read all rows
for row in range(grid.row_count):
    for col in grid.column_order:
        print(grid.get_cell_value(row, col), end="\t")
    print()

Navigate a tree control

from sapsucker.components.tree import GuiTree

tree = session.find_by_id("wnd[0]/shellcont/shell/shellcont[1]/shell/shellcont[2]/shell")

key = tree.top_node
print(tree.get_node_text_by_key(key))

if tree.is_folder(key):
    tree.expand_node(key)

Fill a form

# Set a text field value
session.find_by_id("wnd[0]/usr/ctxtRS38M-PROGRAMM").text = "RSPARAM"

# Press F8 (Execute)
session.find_by_id("wnd[0]").send_v_key(8)

Context manager

with SapGui.connect() as app:
    session = app.connections[0].sessions[0]
    print(session.info.user)
# All connections closed automatically

More examples

The examples/sapsucker/ directory contains complete runnable scripts, all tested against a real SAP system:

Monitoring a session while a human records

sapsucker.monitor samples a live session so a recording made with SAP GUI's own recorder (Alt+F12 → Script Recording and Playback) can be paired with timestamps. A recorded .vbs has none, so it cannot tell you where the person paused — which is usually where they were deciding — nor that they went back to re-check a field, nor whether the save actually worked.

pip install sapsucker[cli]

sapsucker-monitor -o timing.jsonl \
  --watch "wnd[0]/shellcont/shell:FirstVisibleRow"

Start it, start the recorder, do the task, stop both. --watch takes any element_id:ComProperty pair and is repeatable — the example above timestamps each individual ALV scroll.

A prebuilt Windows .exe is attached to each release for machines without a Python toolchain.

As a library:

from sapsucker.monitor import SessionMonitor, Watch

monitor = SessionMonitor(session, watches=[Watch(element_id="wnd[0]/shellcont/shell", prop="FirstVisibleRow")])
for sample in monitor.samples():      # generator: the caller owns the loop, and the thread
    if sample.changed:
        print(sample.elapsed, sample.changed, sample.gap_since_change)

samples() never starts a thread. COM is STA, so a monitor loop occupies its thread for its whole lifetime — see the sapsucker.monitor module docstring.

Architecture

sapsucker wraps the SAP GUI Scripting COM API as a hierarchy of typed Python classes:

GuiApplication
  └── GuiConnection
       └── GuiSession
            └── GuiMainWindow
                 ├── GuiToolbar
                 ├── GuiMenubar
                 ├── GuiStatusbar
                 └── GuiUserArea
                      ├── GuiTextField, GuiLabel, GuiButton, ...
                      ├── GuiTableControl (classic dynpro tables)
                      ├── GuiGridView (ALV grids)
                      ├── GuiTree (tree controls)
                      ├── GuiTabStrip → GuiTab
                      └── GuiAbapEditor / GuiTextedit

Elements are discovered via session.find_by_id(sap_id), which returns the correct typed wrapper automatically (e.g., GuiGridView for an ALV grid, GuiTree for a tree control). The factory dispatches on TypeAsNumber and SubType COM properties.

Thread Safety

COM objects use the Single-Threaded Apartment (STA) model. All calls to a given SAP GUI session must happen from the same thread that called pythoncom.CoInitialize(). See the _com.py module docstring for details and an asyncio.to_thread() example.

API Overview

Class / method Description
SapGui Entry point — SapGui.connect() returns GuiApplication
GuiApplication Root object, manages connections
GuiConnection A TCP connection to an SAP server
GuiSession A session (mode) within a connection
GuiMainWindow The main SAP window
GuiTextField Single-line input field
GuiButton Push button
GuiCheckBox Checkbox
GuiComboBox Dropdown list
GuiGridView ALV grid (most common data display)
GuiTableControl Classic dynpro table
GuiTree Tree control (simple, list, or column)
GuiAbapEditor ABAP source code editor
GuiStatusbar Status bar at bottom of window
.dump_tree() Method on any visual container (GuiVContainer) — recursive screen dump, returns list[ElementInfo]

Contributing

Contributions are welcome! Please open an issue first to discuss what you'd like to change.

For detailed setup instructions (uv dependency groups, CI, linting, formatting, etc.), see the Hochfrequenz Python Template Repository.

# Clone and install dev dependencies
git clone https://github.com/Hochfrequenz/sapsucker.git
cd sapsucker
uv sync --group dev

# Run unit tests (no SAP required, works on any OS)
uv run pytest unittests/ -v

Integration tests against real SAP

Integration tests run against a real SAP GUI system and are automatically skipped on machines without SAP access. To run them locally:

  1. SAP GUI for Windows must be running with scripting enabled
  2. Create a .env file with your SAP credentials:
    SAP_CONNECTION_NAME=your_connection
    SAP_USER=your_user
    SAP_PASSWORD=your_password
    SAP_MANDANT=your_client
    SAP_LANGUAGE=EN
    
  3. Run:
    uv run pytest unittests/ -k integration -v
    

Integration tests run by default on any local machine and are automatically skipped in CI (GitHub Actions). Set SAP_SKIP_INTEGRATION=1 to skip them locally. They cover SE80, SE16N, SE37, SE38, and SM37 — all read-only, no SAP data is modified.

License

MIT

Metadata

Release files for sapsucker 1.3.0

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

Source distribution (sdist)

Source distribution for sapsucker 1.3.0
File Size Uploaded
sapsucker-1.3.0.tar.gz 159.0 kB Details

Built distribution (wheel)

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

Total release size: 226.8 kB

Release files / sapsucker-1.3.0.tar.gz

Download URL sapsucker-1.3.0.tar.gz
Size 159.0 kB
Tags Source
SHA-256 checksum
How to use checksums
7e4e00ae23efe80e096c13c3f277519b2188b19d9ffc37fbfbeccb5b8e3cfbf7
BLAKE2b-256 checksum
How to use checksums
4d734a05f09b4240095ec70572ec525495ab1bf9fb5f77cbfdd1599aa1e45b4b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / sapsucker-1.3.0-py3-none-any.whl

Download URL sapsucker-1.3.0-py3-none-any.whl
Size 67.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c0905f151112753b2fc635398635c757b2024c5656cdb2c135620b589e4fce0f
BLAKE2b-256 checksum
How to use checksums
eb2d57e74e29538d0094a9812ef291f428788241a760eb04ba5ffc76d38a83f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

1.5.0

2 release files

1.4.0

2 release files

This release

1.3.0 This release

2 release files

1.2.0

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

0.0.1

2 release files

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