v0.1 shipped ~15 hand-audited methods across sites/dns/mail/databases/clients. That's enough for daily ops but every new Tort Host / cWHO feature has been hitting the wall at the edge of that coverage. This extends the SDK to wrap every method the panel exposes — 312 of them as of Rackham 2026-04-22, verified against the live list_functions() introspection call with only one name-mismatch (``__construct``, a PHP lifecycle artifact, not a real API method). The hand-audited helpers stay where they are. Every module now has two clearly-delimited sections: an auto-generated block at the top (emitted by tools/gen_methods.py from tools/method_inventory.json), and a HAND-EDIT ONLY BELOW block at the bottom that survives regeneration. Name collisions between auto and hand always resolve in favor of the hand version — the generator emits a ``# skipped foo: hand-audited helper takes precedence`` comment in the auto block for traceability. Pipeline: - tools/extract_inventory.py reads remote.*.inc.php + remoting.inc.php, pulls docblocks + param defaults, dumps one JSON record per method. Regex is balanced-paren aware so ``$params = array()`` defaults don't truncate the signature at the wrong close-paren (that footgun hid three methods from the first run — sites_aps_available_packages_list, sites_aps_instance_delete, openvz_vm_add_from_template). - tools/method_inventory.json is the committed inventory — future ISPConfig upgrades diff against this file to see scope at a glance. - tools/gen_methods.py groups by method-name prefix onto the module classes listed in the README table, emits a 1:1 Python wrapper per method with the original PHP filename + line number in the docstring, and ensures ``from typing import Any`` is present in preexisting modules before emitting ``Any`` type annotations. New submodules (all auto-generated, wired into ISPConfigClient.__init__): admin, aps, backups, cron, domains, ftp, misc, monitor, openvz, server, shell, webdav. Existing modules (sites, dns, mail, databases, clients) got their auto block filled in and their hand-audited helpers preserved. Escape hatches on the top-level client: - raw_call(method, *args) routes an arbitrary method name through the same session-management + retry + fault-mapping pipeline the typed wrappers use. Fix for "panel shipped a new method, SDK hasn't caught up" — callers don't have to reach back into _soap. - list_functions() wraps get_function_list() for panel introspection. Fault mapping widened: ``no_client_found`` and "no user account" messages now map to NotFoundError instead of FaultError, matching the existing ``no_domain_found`` convention. Older code that caught raw FaultError there will still work (NotFoundError extends ISPConfigError) but callers can now catch the specific type. Testing: - tests/test_unit.py — 12 existing pure-unit tests pass unchanged. - tests/test_smoke.py — extended from 4 read-only calls to 21. One probe per new auto-generated module plus raw_call and list_functions smoke tests. Methods gated behind admin permission skip gracefully with a documented reason (kayos is a reseller, not admin): monitor_jobqueue_count, sites_cron_get, sites_ftp_user_get, openvz_get_free_ip, quota_get_by_user. Results against Rackham 2026-04-22: 28 passed, 5 skipped (all documented admin-only), 0 failed. - ISPCONFIG_TEST_VERIFY_SSL=0 env-var knob added to conftest for panels with self-signed or mismatched certs. Version bump 0.1.0 -> 0.2.0. README restructured into Hand-audited / Auto-generated / Escape hatch / Footguns sections with a regeneration recipe for future ISPConfig upgrades. Ruff per-file SLF001 ignore extended to every submodule (submodules are all authorized callers of the client's private ``_call`` dispatcher by design). mypy strict passes; ruff check passes; ruff format applied across src / tools / tests.
109 lines
3.1 KiB
TOML
109 lines
3.1 KiB
TOML
[build-system]
|
|
requires = ["setuptools>=68", "wheel"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "ispconfig"
|
|
version = "0.2.0"
|
|
description = "Python SDK for the ISPConfig remote SOAP API — Sulkta Coop internal tooling."
|
|
readme = "README.md"
|
|
license = { text = "MIT" }
|
|
requires-python = ">=3.10"
|
|
authors = [
|
|
{ name = "Sulkta Coop" },
|
|
]
|
|
# Zero runtime deps on purpose. ISPConfig's SOAP endpoint disables WSDL
|
|
# generation (?wsdl returns a fault), so zeep can't help us anyway — we
|
|
# hand-roll envelopes with the stdlib.
|
|
dependencies = []
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"pytest>=7",
|
|
"mypy>=1.8",
|
|
"ruff>=0.3",
|
|
]
|
|
|
|
[project.urls]
|
|
Homepage = "http://192.168.0.5:3001/Sulkta-Coop/ispconfig-py"
|
|
|
|
[tool.setuptools.packages.find]
|
|
where = ["src"]
|
|
|
|
[tool.setuptools.package-data]
|
|
ispconfig = ["py.typed"]
|
|
|
|
# ---- mypy -----------------------------------------------------------
|
|
|
|
[tool.mypy]
|
|
python_version = "3.10"
|
|
packages = ["ispconfig"]
|
|
mypy_path = "src"
|
|
strict_optional = true
|
|
warn_unused_ignores = true
|
|
warn_redundant_casts = true
|
|
warn_return_any = true
|
|
disallow_untyped_defs = true
|
|
disallow_incomplete_defs = true
|
|
check_untyped_defs = true
|
|
no_implicit_optional = true
|
|
# We intentionally don't enable `disallow_any_expr` — SOAP responses are
|
|
# dicts of Any and forcing typing everywhere would create fake certainty.
|
|
|
|
[[tool.mypy.overrides]]
|
|
module = "ispconfig.types"
|
|
disallow_untyped_defs = false
|
|
|
|
# ---- ruff -----------------------------------------------------------
|
|
|
|
[tool.ruff]
|
|
line-length = 110
|
|
target-version = "py310"
|
|
src = ["src"]
|
|
|
|
[tool.ruff.lint]
|
|
select = [
|
|
"E", # pycodestyle errors
|
|
"F", # pyflakes
|
|
"W", # pycodestyle warnings
|
|
"I", # isort
|
|
"B", # bugbear
|
|
"UP", # pyupgrade
|
|
"N", # pep8-naming
|
|
"SLF", # flake8-self (private access)
|
|
"RUF",
|
|
]
|
|
ignore = [
|
|
"E501", # line length — handled by formatter when it cares
|
|
"N818", # exception suffix — our hierarchy predates this rule
|
|
"B008", # function calls in argument defaults — not our style anyway
|
|
]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/*" = ["SLF001", "N802"]
|
|
# Submodules call back into the client's dispatcher (`_call`) by design —
|
|
# it's the single chokepoint for session management and retry logic.
|
|
# Every submodule needs this waiver; generator output follows the same pattern.
|
|
"src/ispconfig/admin.py" = ["SLF001"]
|
|
"src/ispconfig/aps.py" = ["SLF001"]
|
|
"src/ispconfig/backups.py" = ["SLF001"]
|
|
"src/ispconfig/clients.py" = ["SLF001"]
|
|
"src/ispconfig/cron.py" = ["SLF001"]
|
|
"src/ispconfig/databases.py" = ["SLF001"]
|
|
"src/ispconfig/dns.py" = ["SLF001"]
|
|
"src/ispconfig/domains.py" = ["SLF001"]
|
|
"src/ispconfig/ftp.py" = ["SLF001"]
|
|
"src/ispconfig/mail.py" = ["SLF001"]
|
|
"src/ispconfig/misc.py" = ["SLF001"]
|
|
"src/ispconfig/monitor.py" = ["SLF001"]
|
|
"src/ispconfig/openvz.py" = ["SLF001"]
|
|
"src/ispconfig/server.py" = ["SLF001"]
|
|
"src/ispconfig/shell.py" = ["SLF001"]
|
|
"src/ispconfig/sites.py" = ["SLF001"]
|
|
"src/ispconfig/webdav.py" = ["SLF001"]
|
|
|
|
# ---- pytest ---------------------------------------------------------
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
addopts = "-ra"
|