Tests and docs only, no behaviour change. TLS was the last untested surface and it came back clean. Previous coverage stopped at the handshake; everything after it ran only over plain sockets. That gap mattered because SSLSocket.recv is not socket.recv: it returns at most one TLS record's worth of plaintext however much you ask for, can return fewer bytes than are available, and plaintext buffered inside the SSL object is invisible to the OS. The Phase 39 buffered reader asks for up to 64 KB per call and loops until satisfied — that loop had never been pressured the same way. tests/test_tls_traffic.py runs real SQLI through a TLS-terminating proxy: the framing-bug types end to end, payloads at 1/4096/16383/16384/ 16385/32000 bytes (straddling the ~16 KB record boundary), 500- and 5000-row bulk fetches, error recovery, concurrent TLS sessions, and three negative cases — TLS client against a plaintext port, plaintext client against a TLS port (must raise, not hang), and a verifying context rejecting a self-signed cert. All clean on all three servers. The buffered reader handles SSLSocket semantics correctly. Scope, stated plainly: the proxy supplies the TLS half, so this covers the driver's TLS path — the half we own. It does NOT cover IBM's server-side TLS listener. Setting one up on the developer-edition image was attempted and abandoned: Informix 15 wants a PKCS#12 keystore (onkstash takes a .p12, not the older CMS .kdb) and the engine kept rejecting the stash with GSK_ERROR_BAD_KEYFILE_PASSWORD even with a keystore GSKit itself could open. That half is IBM's code; everything below ssl.wrap_socket is identical either way. 414/414 integration on 15, 14.10 and 12.10 (was 399). Every surface is now fuzzed: type framing, fetch batching, error recovery, cursor lifecycle, threads, pooling, transactions, async cancellation, executemany partial failure, scrollable cursors, smart LOBs, and TLS.
112 lines
3.8 KiB
TOML
112 lines
3.8 KiB
TOML
[project]
|
|
name = "informix-driver"
|
|
version = "2026.09.01"
|
|
description = "Pure-Python driver for IBM Informix IDS — speaks the SQLI wire protocol over raw sockets. No CSDK, no JVM, no native libraries."
|
|
readme = "README.md"
|
|
license = { text = "MIT" }
|
|
authors = [{ name = "Ryan Malloy", email = "ryan@supported.systems" }]
|
|
requires-python = ">=3.10"
|
|
keywords = ["informix", "database", "sqli", "db-api", "pep-249", "asyncio", "async"]
|
|
classifiers = [
|
|
"Development Status :: 5 - Production/Stable",
|
|
"Framework :: AsyncIO",
|
|
"Intended Audience :: Developers",
|
|
"License :: OSI Approved :: MIT License",
|
|
"Operating System :: OS Independent",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3 :: Only",
|
|
"Programming Language :: Python :: 3.10",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Programming Language :: Python :: 3.14",
|
|
"Topic :: Database",
|
|
"Topic :: Database :: Front-Ends",
|
|
"Typing :: Typed",
|
|
]
|
|
dependencies = []
|
|
|
|
[project.urls]
|
|
Homepage = "https://informix-driver.warehack.ing"
|
|
Documentation = "https://informix-driver.warehack.ing"
|
|
Source = "https://git.supported.systems/warehack.ing/informix-db"
|
|
Issues = "https://git.supported.systems/warehack.ing/informix-db/issues"
|
|
Changelog = "https://git.supported.systems/warehack.ing/informix-db/src/branch/main/CHANGELOG.md"
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"pytest>=8.0",
|
|
"ruff>=0.6",
|
|
]
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["src/informix_db"]
|
|
|
|
[tool.hatch.build.targets.sdist]
|
|
# Defense in depth: exclude operator-private and dev-only artifacts from the sdist
|
|
# (the wheel doesn't ship these by default, but the sdist would).
|
|
# See ~/.claude/rules/python.md for the full pre-publish PII audit playbook.
|
|
exclude = [
|
|
"CLAUDE.md", # operator-private context
|
|
".env", ".env.local", ".env.*",
|
|
".mcp.json", # may contain local filesystem paths
|
|
"build/", # decompiled JDBC, downloaded JARs
|
|
"audits/",
|
|
"docs/**", # protocol notes / decision log / captures — go to GitHub for the depth
|
|
"docs-site/**", # Astro/Starlight site sources — published at informix-driver.warehack.ing
|
|
"tests/reference/**", # Java reference client — spike infra
|
|
"tests/benchmarks/.results/**", # pytest-benchmark cache (gitignored, but still ships unless excluded)
|
|
".pytest_cache/", ".ruff_cache/", ".mypy_cache/",
|
|
"dist/", "*.egg-info/",
|
|
]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py310"
|
|
src = ["src", "tests"]
|
|
|
|
[tool.ruff.lint]
|
|
select = [
|
|
"E", # pycodestyle errors
|
|
"W", # pycodestyle warnings
|
|
"F", # pyflakes
|
|
"I", # isort (import sorting)
|
|
"B", # flake8-bugbear
|
|
"C4", # flake8-comprehensions
|
|
"UP", # pyupgrade
|
|
"SIM", # flake8-simplify
|
|
"PTH", # flake8-use-pathlib
|
|
"RUF", # ruff-specific
|
|
]
|
|
ignore = [
|
|
"E501", # line too long — handled by formatter
|
|
]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/**" = ["B011"] # allow assert False in tests
|
|
|
|
[tool.pytest.ini_options]
|
|
minversion = "8.0"
|
|
testpaths = ["tests"]
|
|
asyncio_mode = "auto" # pytest-asyncio: auto-detect ``async def`` tests
|
|
addopts = [
|
|
"-ra", # short summary for non-passing
|
|
"--strict-markers",
|
|
"--strict-config",
|
|
"-m", "not integration and not benchmark", # default: unit-only. Override with: pytest -m integration / -m benchmark
|
|
]
|
|
markers = [
|
|
"integration: requires a running Informix container (docker compose up); skipped by default",
|
|
"benchmark: pytest-benchmark performance test; skipped by default. Run with `make bench`.",
|
|
]
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"pytest-asyncio>=1.3.0",
|
|
"pytest-benchmark>=5.2.3",
|
|
]
|