Skip to main content

abap-adt-py

tests

abap-adt-py is a Python client library to interact with SAP systems via the ABAP Development Tools (ADT) REST API. It allows you to programmatically manage ABAP artifacts and workflows directly from Python.

Features

  • Authenticate and log in to SAP systems securely
  • Create, read, edit, and activate ABAP objects, including dictionary and RAP objects
  • Search ABAP repositories and metadata
  • Browse packages and the repository tree
  • Run ABAP SQL queries (data preview)
  • Run classes and capture their console output
  • Run ATC checks and read the findings' documentation
  • Navigate code: go to definition, where-used list, code completion
  • Read runtime errors (short dumps)
  • Run ABAP unit tests and retrieve results
  • Create, list, and release transport requests
  • Seamless integration with Python for automation and scripting

Installation

Install the package from PyPI:

pip install abap-adt-py

Or install from source:

git clone https://github.com/timkoehne/abap-adt-py.git
cd abap-adt-py
pip install .

Usage

# Test Report
report_name = "z_test"
report_uri = "/sap/bc/adt/programs/programs/z_test"

# establish connection to SAP system
client = AdtClient(
    sap_host="http://localhost:50000",
    username="DEVELOPER",
    password="<password>",
    client="001",
    language="EN",
)

# login
response = client.login()

# search
results: list = client.search_object(report_name, 50)
print(results)

# create a local package (transportable packages also need transport="<request number>")
client.create_package("$Z_DEMO", "Demo package", parent="$TMP")

# browse a package (subpackages appear as DEVC/K entries)
objects: list = client.package_contents("$TMP")
all_objects: list = client.package_contents("$TMP", recursive=True)

# package hierarchy of an object, top-level package first
packages: list = client.object_package_path(report_uri)

# run an ABAP SQL query, rows come back as dicts keyed by column name
result = client.run_query("SELECT carrid, connid, price FROM sflight", max_rows=10)
print(result["total_rows"], result["rows"])

# run a class implementing IF_OO_ADT_CLASSRUN and get its console output
output: str = client.run_class("ZCL_MY_CLASSRUN")

# run the ABAP Test Cockpit on objects or packages (system default variant unless given)
result = client.run_atc("/sap/bc/adt/packages/z_demo", check_variant="ZABAP_CLOUD_DEVELOPMENT")
for finding in result["findings"]:  # priority 1 = error, 2 = warning, 3 = information
    print(finding["priority"], finding["object_name"], finding["line"], finding["message"])
print(client.atc_documentation(result["findings"][0]["documentation_uri"]))
print(client.list_check_variants("Z*"))

# code navigation (lines start at 1, columns at 0; the source may be unsaved)
source_uri = "/sap/bc/adt/programs/programs/z_demo/source/main"
target = client.find_definition(source_uri, source, line=3, column=18)  # {"uri", "line", "column"} or None
usages = client.where_used("/sap/bc/adt/oo/classes/zcl_demo")  # one entry per usage with its code line
usages = client.where_used("/sap/bc/adt/oo/classes/zcl_demo/source/main", line=3, column=12)  # e.g. one method
proposals = client.code_completion(source_uri, source, line=6, column=17)

# runtime errors (short dumps, as in ST22)
from datetime import datetime, timedelta
for dump in client.list_dumps(user="DEVELOPER", since=datetime.now() - timedelta(days=1)):
    print(dump["datetime"], dump["runtime_error"], dump["program"], dump["short_text"])
dump = client.get_dump(dump["id"])
print(dump["chapters"]["Error analysis"])  # also "What happened?", "Information on where terminated", ...

# create report object
client.create(
    object_type="PROG/P",
    name=report_name,
    description="Test Program",
    parent="$TMP",
)

# more object types: structures (TABL/DS), service definitions (SRVD/SRV) and behavior
# definitions (BDEF/BDO, named after their root CDS entity) are created with create()
# and filled with set_object_source like a class. These are created with their settings:
client.create_domain("Z_DEMO_DOMAIN", "$TMP", "Demo domain", data_type="CHAR", length=10)
client.create_table_type("Z_DEMO_TT", "$TMP", "Demo table type", row_type="SCARR")  # or data_type/length
client.create_service_binding("Z_DEMO_SB", "$TMP", "Demo binding", service_definition="Z_DEMO_SD")  # OData V4 UI

# read source code
src: str = client.get_object_source(f"{report_uri}/source/main")
print(src)

# configure pretty printing
pretty_print_settings: PrettyPrintSettings = {"indentation": True, "style": "keywordUpper"}
client.prettyprint_settings(pretty_print_settings)

# pretty print source code
src: str = f"Report {report_name}. Write 'test'."
pretty_src = client.prettyprint(src)

# write source code
lock_handle: str = client.lock(report_uri)
client.set_object_source(f"{report_uri}/source/main", pretty_src, lock_handle)
client.unlock(report_uri, lock_handle)

# activate object
client.activate(report_name, report_uri)

# transportable objects: create a transport request and pass it along
transport: str = client.create_transport("/sap/bc/adt/packages/z_demo", "Demo changes", "Z_DEMO")
client.create_package("Z_DEMO", "Demo package", transport=transport)
client.create("PROG/P", "Z_DEMO_REPORT", "Z_DEMO", "Demo report", transport=transport)
info = client.transport_info("/sap/bc/adt/programs/programs/z_demo_report", "Z_DEMO")  # recording needed? usable requests?
print(client.list_transports())  # modifiable requests with their tasks and objects
client.release_transport(transport)  # releases the tasks first, then the request

# delete object
lock_handle: str = client.lock(report_uri)
client.delete(report_uri, lock_handle)
client.unlock(report_uri, lock_handle)

Errors

All errors derive from AdtError (an Exception) and carry status_code, sap_type, sap_message and the raw response_text:

from abap_adt_py.exceptions import AdtError, NotFoundError, ObjectLockedError, ActivationError

try:
    handle = client.lock(report_uri)
except ObjectLockedError as error:  # someone else is editing the object
    print(error.sap_message)         # "User DEVELOPER is currently editing Z_TEST"

try:
    client.activate(report_name, report_uri)
except ActivationError as error:
    for message in error.messages:   # type, text, uri (with position), object
        print(message["type"], message["text"])
Error Raised when
AuthenticationError login failed
SessionError the session expired while an object was locked, or reconnect=False
NotFoundError an object, check variant, transport request, ... doesn't exist
ObjectLockedError the object is locked by another user or session (a LockError)
InvalidLockHandleError writing without a valid lock (a LockError)
ActivationError activation failed, details in .messages
TransportError a transport request couldn't be created, released, deleted, ...
QueryError an SQL query failed
ClassRunError a runtime error occurred while running a class

Expired sessions

When the session expires (e.g. after SAP's idle timeout), the client logs in again and repeats the request once. This only happens while no object is locked: locks belong to the session, so an expired session while holding a lock raises SessionError instead of silently continuing without the lock. Pass reconnect=False to AdtClient to always get the SessionError.

Testing

pip install -e ".[test]"
pytest

The unit tests run offline against responses recorded from a real SAP system (tests/fixtures). The integration tests in tests/integration run against a live system and are skipped unless it is configured:

export ABAP_ADT_HOST=http://localhost:50000 ABAP_ADT_USER=DEVELOPER ABAP_ADT_PASSWORD=...
export ABAP_ADT_CLIENT=001 ABAP_ADT_LANGUAGE=EN  # optional, these are the defaults
pytest -m integration

They create objects with unique names in $TMP and delete them again. The test run stops after a failed login so a wrong password cannot lock the user. Tests that create and release transport requests only run with ABAP_ADT_TEST_TRANSPORTS=1, because released requests stay in the system.

Releasing

GitHub Actions runs the offline tests on Python 3.7 to 3.13 for every push and pull request. To publish a new version to PyPI:

  1. Set the new version in pyproject.toml, commit and push.
  2. Create a GitHub release with the tag v<version>, e.g. v0.2.0.

The publish workflow then runs the tests, checks that the tag matches the version, builds the package and uploads it to PyPI.

Release files for abap-adt-py 0.2.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 abap-adt-py 0.2.0
File Size Uploaded
abap_adt_py-0.2.0.tar.gz 34.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for abap-adt-py 0.2.0
File Interpreter ABI Platform
abap_adt_py-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 75.6 kB

Release files / abap_adt_py-0.2.0.tar.gz

Download URL abap_adt_py-0.2.0.tar.gz
Size 34.0 kB
Tags Source
SHA-256 checksum
How to use checksums
ebe9f0fbfb64348b8e595d3438ad2d0995070b26205bb9df42c2cd26c7dafd28
BLAKE2b-256 checksum
How to use checksums
04fa22d62df4ae8d19686f2ba0cb27abe82ec3217f88be73b1519f68ce2a8177
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 Sep 27, 2026.

Transparency log

Release files / abap_adt_py-0.2.0-py3-none-any.whl

Download URL abap_adt_py-0.2.0-py3-none-any.whl
Size 41.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7d08ef37c568ed817147268f0b6190fa91f320c53e19132a0d0daa76c44dc919
BLAKE2b-256 checksum
How to use checksums
6377c92caba8ef4e969048beccd85fb938d65f420b33c36e58846af9037da98e
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 Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

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