tamash-selenium (Python)
Plug-and-play self-healing for Selenium + Python. Wrap your WebDriver once and every
find_element through it — in Page Objects, helper/util layers, inside a WebDriverWait —
recovers automatically when a locator breaks. Nothing else changes.
from selenium import webdriver
from tamash_selenium import SelfHealingDriver
driver = SelfHealingDriver.wrap(webdriver.Chrome())
That's the whole integration.
Also available for Selenium Java (
com.vibetestq.qtpsudhakar:tamash-seleniumon Maven Central) and for Playwright — TypeScript & Python (tamash-playwright). Same idea, one package per ecosystem.
Why you need this
Websites change. A button gets renamed or moved and your test can't find it anymore — even though the app works fine for real users. Normally that's a broken test.
tamash-selenium fixes it: when find_element can't find an element, it locates it on the live
page — with a rule-based matcher (default, no key, no network) or an AI model — and retries. If it
can't, the test fails exactly as it would have without the package. Healing never masks a real
failure.
Step 1: Install
pip install tamash-selenium
Pulls in Selenium 4 (which provisions the browser drivers itself since 4.6). Requires Python 3.9+.
Optional AI-provider extras:
pip install 'tamash-selenium[anthropic]' # HEALER_PROVIDER=anthropic | claude-subscription
pip install 'tamash-selenium[copilot-subscription]' # Python 3.11+ ; also needs the `copilot` CLI
The openai / gemini / ollama / ollama-local providers need nothing extra.
Worked examples — every test style and every healing scenario, with locators broken on
purpose — live in
tamash-selenium-python-sample.
Step 2: Wrap the driver
Wherever you create the driver — a fixture, a DriverFactory, a setUp:
from tamash_selenium import SelfHealingDriver
driver = SelfHealingDriver.wrap(my_driver) # Remote / Grid / cloud all fine
Everything downstream is now healing-aware — plain find_element, Page Objects, waits.
Wrapping also pins Selenium's implicit wait to 0 (mixing implicit + explicit waits is an
anti-pattern, and a high implicit wait delays healing) — set TAMASH_KEEP_IMPLICIT_WAIT=true to
keep yours.
Or let an integration own the lifecycle
| Framework | How |
|---|---|
| pytest | The plugin auto-loads. Use the driver (or tamash_driver) fixture — or wrap your own driver fixture with SelfHealingDriver.wrap. |
| pytest-bdd | Same — scenarios are pytest tests; use tamash_driver in step functions. |
| Behave | Delegate the four environment.py hooks to tamash_selenium.integrations.behave (see its docstring). |
| unittest | class LoginTest(TamashSeleniumTestCase): ... — use self.driver. |
Step 3 (optional): connect an AI provider
With no configuration, healing uses the rule-based tamash provider — no key, no network, no
tokens; it text-matches the element's decoded name against the page's accessibility tree and never
guesses. Good for well-named suites.
For stronger healing, set a provider in a .env at your project root (or real env vars, or a
[tool.tamash-selenium] table in pyproject.toml):
| Provider | Auth |
|---|---|
ollama |
OLLAMA_API_KEY + OLLAMA_MODEL (Ollama Cloud — free key) |
ollama-local |
OLLAMA_LOCAL_MODEL (+ OLLAMA_LOCAL_BASE_URL; key optional) |
openai |
OPENAI_API_KEY + OPENAI_MODEL |
anthropic |
ANTHROPIC_API_KEY + ANTHROPIC_MODEL (needs the [anthropic] extra) |
gemini |
GEMINI_API_KEY + GEMINI_MODEL |
claude-subscription |
CLAUDE_CODE_OAUTH_TOKEN (claude setup-token) — bills your Claude subscription |
copilot-subscription |
the [copilot-subscription] extra + the copilot CLI signed in |
tamash |
nothing — the default |
HEALER_PROVIDER=ollama
OLLAMA_MODEL=gpt-oss:120b
OLLAMA_API_KEY=paste_your_key_here
HEALER_ENABLED=false turns healing off entirely (e.g. per-CI-run).
Step 4: Check your setup
tamash-selenium doctor
Provider connectivity (a live call), the implicit-wait note, and a scan for brittle locators bound to non-descriptive names.
How it heals
When find_element can't find its element:
- Cache — a selector already healed this run for that locator (any caller, including a wait's next poll) is reused instantly.
- DOM snapshot — a JS accessibility tree of the page is captured; the provider is shown the relevant slice (matched to the element's decoded name), or the full tree.
ref+ durable derivation — the provider picks the element; a stable locator is derived for it (By.ID→By.NAME→ adata-testid/aria-labelCSS → link text → a structural XPath), verified against the live element before it's trusted.- Action recovery (opt-in,
HEALER_ACTION_RECOVERY_ENABLED=true) — scroll / JS-click / wait / dispatch when the element is found but the action is blocked.
Every heal logs [self-healer] … -> HEALED [provider=…, suggested="(By.ID, \"username\")"].
Writing locators the healer can work with
(By.ID, ...) / (By.NAME, ...) carry their own meaning. A raw (By.CSS_SELECTOR, ...) /
(By.XPATH, ...) doesn't — bind it to a descriptive variable / attribute and the healer decodes
the name: txt_employee_id → "Employee Id (textbox)", submit_button → "Submit (button)"
(deterministic, no AI). Decoding works when the locator is on the same line as its find_element
call, or is a page-object attribute the call references by name.
Explicit hint — for keyword-driven suites or heavy indirection:
from tamash_selenium import hint
def click(locator, name):
with hint(name):
driver.find_element(*locator).click()
What gets healed (and what doesn't)
Intercepted on WebElement: click, send_keys, clear, submit, and the read methods
(get_attribute, get_dom_attribute, get_dom_property, get_property, value_of_css_property,
is_displayed, is_enabled, is_selected). Plus find_element / find_elements on the driver
and on elements.
Not touched: the ActionChains API, WebElement.text (a Python property — can't be intercepted
per-instance; use get_attribute("textContent") if you need it healed), and find_elements
(never healed — use driver.find_elements(by, value) for absence checks).
Assert-absent is never healed: pytest.raises(NoSuchElementException),
EC.invisibility_of_element_located, EC.staleness_of, or a helper whose name contains
absent / not_present / gone. HEALER_ASSERTIONS=strict also refuses to heal a locator
resolved inside an assertion; HEALER_ASSERTIONS=warn heals but prints an end-of-run summary.
Making a heal permanent: apply-heals
A runtime heal fixes the current run; apply-heals writes it into source:
tamash-selenium apply-heals --dry-run # preview
tamash-selenium apply-heals # apply (prompts first)
# (By.CSS_SELECTOR, "#old") → (By.ID, "username")
# driver.find_element(By.CSS_SELECTOR, "#old") → driver.find_element(By.ID, "username")
It rewrites an inline find_element(By.*, "…") call or a LOGIN = (By.*, "…") tuple constant (via
the heal log's recorded declaration line), writes Markdown + JSON reports under
.tamash-selenium/, and generates verify_heals.py that re-runs exactly the affected pytest node
ids with HEALER_ENABLED=false.
HTML step report
pytest --tamash-report=report.html # pytest
TAMASH_REPORT=report.html python my_script.py # plain / Behave / unittest
Per test: step timeline, which steps healed (recovered selector, provider, token cost), the DOM snapshot on an unrecovered failure. Zero overhead when unset.
Agent skill
tamash-selenium init-skill
Copies a coding-agent skill (SKILL.md + references/) into .claude/skills/tamash-selenium/
and .agents/skills/tamash-selenium/ that drives the local run → review → apply-heals → verify
→ land loop.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
HEALER_ENABLED |
true |
Master switch. false / 0 turns healing off. |
HEALER_PROVIDER |
tamash |
ollama | ollama-local | openai | anthropic | gemini | claude-subscription | copilot-subscription | tamash. |
HEALER_ASSERTIONS |
heal |
heal | warn | strict. |
HEALER_ACTION_RECOVERY_ENABLED |
false |
Opt-in scroll / force / wait / dispatch recovery. |
HEALER_PARALLEL |
false |
Race the scoped + full-snapshot provider calls concurrently. |
TAMASH_KEEP_IMPLICIT_WAIT |
false |
true keeps your implicit wait. |
TAMASH_BROWSER |
chrome |
chrome | firefox | edge | safari (integrations only). |
HEADLESS |
true |
false runs headed (integrations only). |
TAMASH_REUSE_DRIVER |
false |
One driver per class / feature instead of per test. |
TAMASH_ACTION_TIMEOUT_MS |
20000 |
Bounds the healer's own snapshot / JS calls. |
TAMASH_REPORT |
unset | Output path for the HTML step report. |
TAMASH_DEBUG |
unset | Print DOM-snapshot capture diagnostics. |
OPENAI_API_KEY / OPENAI_MODEL / OPENAI_BASE_URL |
— | OpenAI. |
ANTHROPIC_API_KEY / ANTHROPIC_MODEL |
— | Anthropic. |
GEMINI_API_KEY / GEMINI_MODEL / GEMINI_THINKING |
— / — / off |
Gemini. |
OLLAMA_API_KEY / OLLAMA_MODEL / OLLAMA_BASE_URL |
— / — / https://ollama.com |
Ollama Cloud. |
OLLAMA_LOCAL_MODEL / OLLAMA_LOCAL_BASE_URL / OLLAMA_LOCAL_API_KEY |
— / http://localhost:11434 / — |
Self-hosted Ollama. |
CLAUDE_CODE_OAUTH_TOKEN / CLAUDE_SUBSCRIPTION_MODEL |
— / claude-haiku-4-5 |
claude-subscription. |
COPILOT_SUBSCRIPTION_MODEL |
mai-code-1-flash-picker |
copilot-subscription. |
What's tested
Every push runs in CI:
- Python 3.9 / 3.11 / 3.13 on Linux, plus 3.11 on Windows
- Browsers — a broken locator heals on Chrome, Firefox and Edge
- Providers — a real end-to-end heal per provider (informational jobs, gated on the key being
configured):
openai,anthropic,gemini,ollama,ollama-local(CI starts a realollama serve),claude-subscription,copilot-subscription - CLI —
doctor,apply-heals(AST rewrite of inline calls and tuple constants),init-skill - Integrations — pytest plugin, pytest-bdd, Behave,
unittest,pytest-seleniuminterop, plainwrap() tamash-selenium-python-sampleruns all of the above as broken-locator scenarios against bundled fixtures and a live OrangeHRM app
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
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 tamash_selenium-0.1.1.tar.gz.
File metadata
- Download URL: tamash_selenium-0.1.1.tar.gz
- Upload date:
- Size: 96.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
098aa445a149a4f4695c9c6bbd0043813193ad87fa6c2f972b988a1e62fbfae6
|
|
| MD5 |
4190f04d70380088f8f96075326f0612
|
|
| BLAKE2b-256 |
eea39cb2ddf6223478245eb9c84a9e3c27246e28c7a6cbf2baf1612d5a9ec744
|
File details
Details for the file tamash_selenium-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tamash_selenium-0.1.1-py3-none-any.whl
- Upload date:
- Size: 108.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5fe1f128b4fb933ccab09e616cc5df180f4820f6bb5568966a67b190e66c4c10
|
|
| MD5 |
ab7b003f6700da7ccb01846e8573f59a
|
|
| BLAKE2b-256 |
b4e65b142a565d110f28319edb0327cbe32da6ea18ab7d656053e197b0b5a10d
|