AI/LLM-friendly Python client for SAP OData V2 and V4 APIs - single generic function for all operations
Project description
SAP OData Python
Simple Python client for OData V2 and V4 services.
Installation
pip install sap-odata-python
Quick Start
from sap_odata import ODataClient
# Public OData service
client = ODataClient("https://services.odata.org")
# GET data
data = client.get("V4/Northwind/Northwind.svc", "Products", top=5)
for product in data["value"]:
print(product["ProductName"])
# With filters
data = client.get(
"V4/Northwind/Northwind.svc",
"Products",
filter="UnitPrice gt 20",
select="ProductID,ProductName,UnitPrice",
top=10
)
SAP OData V4 (RAP Services)
client = ODataClient(
host="https://sap-system.company.com",
username="user",
password="pass",
client="100"
)
# Simple query
data = client.get(
service="zmy_product_api",
entity="Products",
version="v4",
namespace="zsb_product_api",
filter="ProductID eq '12345'"
)
# Complex nested $expand
data = client.get(
service="zmy_order_api",
entity="Orders",
version="v4",
namespace="zsb_order_api",
filter="OrderID eq '12345'",
expand="Customer($expand=Contacts),LineItems($expand=Product,Discounts($expand=Details)),Payments($expand=BankAccount)"
)
# Access nested data
for order in data["value"]:
print(f"Order: {order['OrderID']}")
for line in order.get("LineItems", []):
print(f" Line Item: {line['ProductName']}")
SAP OData V2 (Gateway Services)
# Simple query
data = client.get(
service="ZMY_SALESORDER_SRV",
entity="SalesOrderSet",
version="v2",
top=10,
filter="Status eq 'OPEN'"
)
# Entity with key in path
data = client.get(
service="ZMY_CUSTOMER_SRV",
entity="CustomerSet(CustomerID='CUST001',Region='US')",
version="v2"
)
# Complex nested $expand (V2 style with /)
data = client.get(
service="ZMY_ORDER_SRV",
entity="OrderSet(OrderID='12345')",
version="v2",
expand="OrderToCustomer/CustomerToContacts,OrderToItems/ItemToProduct,OrderToItems/ItemToDiscounts,OrderToPayments/PaymentToBankAccount"
)
# V2 nested results are in "results" arrays
for order in data["value"]:
print(f"Order: {order['OrderID']}")
for item in order.get("OrderToItems", {}).get("results", []):
print(f" Item: {item['ProductName']}")
Write Operations
# POST - Create
new_order = client.post(
service="ZMY_SALESORDER_SRV",
entity="SalesOrderSet",
data={"CustomerID": "CUST001", "Amount": 1000},
version="v2"
)
# PATCH - Update
client.patch(
service="ZMY_SALESORDER_SRV",
entity="SalesOrderSet('12345')",
data={"Status": "APPROVED"},
version="v2"
)
# DELETE
client.delete(
service="ZMY_SALESORDER_SRV",
entity="SalesOrderSet('12345')",
version="v2"
)
Get Service Metadata
# V4 metadata
xml = client.metadata(service="zmy_product_api", namespace="zsb_product_api")
# V2 metadata
xml = client.metadata(service="ZMY_SALESORDER_SRV", version="v2")
print(xml) # Returns XML string with entity definitions
API Reference
ODataClient
client = ODataClient(
host="https://sap-system.com", # Base URL
username="user", # Optional: for auth
password="pass", # Optional: for auth
client="100", # Optional: SAP client
verify_ssl=True, # Optional: SSL verification
timeout=60 # Optional: request timeout
)
Methods
| Method | Description |
|---|---|
get(service, entity, version="v4", namespace="", **params) |
Read data |
post(service, entity, data, version="v4", namespace="") |
Create record |
patch(service, entity, data, version="v4", namespace="") |
Update record |
delete(service, entity, version="v4", namespace="") |
Delete record |
metadata(service, version="v4", namespace="") |
Get XML metadata |
Query Parameters
| Param | Example | Description |
|---|---|---|
top |
top=10 |
Limit results |
skip |
skip=20 |
Skip records (pagination) |
filter |
filter="Price gt 100" |
Filter expression |
select |
select="ID,Name" |
Select fields |
expand |
expand="Orders" |
Expand navigation properties |
orderby |
orderby="Name asc" |
Sort results |
Response Format
All responses are normalized to:
{"value": [{"field": "value"}, ...]}
- V4: Native format, returned as-is
- V2: Converted from
{"d": {"results": [...]}}to{"value": [...]}
Context Manager
with ODataClient("https://sap-system.com", "user", "pass", client="100") as client:
data = client.get("ZMY_SALESORDER_SRV", "SalesOrderSet", version="v2")
# Session automatically closed
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
sap_odata_python-1.0.1.tar.gz
(12.8 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sap_odata_python-1.0.1.tar.gz.
File metadata
- Download URL: sap_odata_python-1.0.1.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef455c220ebb16248a07f4c56094c9be2ca2a711895a5986c6126b5eb5d2e1fa
|
|
| MD5 |
0a97674a49d533d310b269228dacd50f
|
|
| BLAKE2b-256 |
c7e6aac50a6a558b7d3762b8704282d92ace4b1fc13824eb2e8e534c6a1d2880
|
File details
Details for the file sap_odata_python-1.0.1-py3-none-any.whl.
File metadata
- Download URL: sap_odata_python-1.0.1-py3-none-any.whl
- Upload date:
- Size: 10.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c24543c1067c274441fcc6d12248e5761c6e8dd9ffefcc2e3305f23528fb0ff4
|
|
| MD5 |
7d2f78738b66e06b8f335e5629ebf4a7
|
|
| BLAKE2b-256 |
ba83cd08bec3af053a2080a7773ac4d6cb79bf53a18eeaaaf61f0ec1eef93da1
|