Skip to main content

AI-powered test automation framework that automatically heals broken element locators in Selenium and Playwright tests

Project description

AutoHeal Locator — Python

PyPI version Python License: MIT

AI-powered test automation library that automatically heals broken locators for Selenium and Playwright.

When element selectors break due to UI changes, AutoHeal finds the elements using DOM analysis, visual recognition, and smart fallback strategies — with results cached so AI is called only once per broken selector.

Python port of the original Java AutoHeal Locator.


Quick Start

New here? See START_HERE.md first — it guides you to the right place based on what you want to do.

# Selenium project
pip install autoheal-locator

# Playwright project
pip install autoheal-locator[playwright]

# With Redis cache support
pip install autoheal-locator[redis]

# Everything
pip install autoheal-locator[all]

Demo Projects

Project Framework Link
Selenium demo Selenium + Python autoheal-selenium-python-demo
Playwright demo Playwright + Python playwright-autoheal-python-demo

Both demos include a START_HERE.md with full step-by-step setup instructions.


Table of Contents


How It Works

Test calls find_element("#selector", "description")
         │
         ▼
┌─────────────────────┐
│  1. Try Original    │──── Found ────► Return element
│     Selector        │
└─────────────────────┘
         │ Not found (quick timeout)
         ▼
┌─────────────────────┐
│  2. Check Cache     │──── Hit ──────► Try cached selector ──► Return element
└─────────────────────┘
         │ Cache miss
         ▼
┌─────────────────────┐
│  3. AI Healing      │
│  · DOM Analysis     │──── Found ────► Cache result ──► Return element
│  · Visual Analysis  │
└─────────────────────┘
         │ All failed
         ▼
    ElementNotFoundException
  1. Try Original — attempts the selector with a short timeout (default 500ms)
  2. Check Cache — looks up previously healed selectors to avoid repeat AI calls
  3. AI Healing — DOM analysis reads the page HTML; Visual analysis reads a screenshot
  4. Cache Result — stores the healed selector so future runs skip the AI step

Key Features

  • AI-Powered Healing — DOM analysis and visual recognition to find relocated elements
  • Multiple AI Providers — Groq (free), Gemini, OpenAI, Anthropic, DeepSeek, Local (Ollama/LM Studio)
  • Flexible Strategies — SMART_SEQUENTIAL, DOM_ONLY, VISUAL_FIRST, SEQUENTIAL, PARALLEL
  • Smart Caching — Persistent file, in-memory, Redis, or no cache
  • Sync and Async APIsfind_element() and find_element_async()
  • Selenium Support — CSS, XPath, ID, Name, Class, Tag selectors
  • Playwright Support — CSS selectors, XPath, and native Playwright Locators (get_by_role, get_by_text, etc.)
  • HTML / JSON / Text Reports — Detailed healing reports per test session
  • pytest-Ready — Drop-in fixture pattern, no test rewrites needed

Framework Comparison

Feature Selenium Playwright
Locator types CSS, XPath, ID, Name, Class, Tag, Link Text CSS, XPath, get_by_role, get_by_text, get_by_placeholder, etc.
Native objects WebElement Locator
Sync API find_element()
Async API find_element_async() find_element_async(), find_async()
Visual analysis Supported Supported
DOM analysis Supported Supported
Caching Unified cache Unified cache

Selenium Quick Start

1. Set Your API Key

# .env — Groq is FREE and the fastest option to get started
GROQ_API_KEY=gsk_your_api_key_here

Get a free key at console.groq.com — no credit card required.

2. Before and After

Before AutoHeal — breaks when UI changes:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://example.com/login")
button = driver.find_element(By.ID, "submit-btn")  # breaks if renamed
button.click()

After AutoHeal — self-healing:

from selenium import webdriver
from autoheal import AutoHealLocator
from autoheal.impl.adapter import SeleniumWebAutomationAdapter

driver = webdriver.Chrome()
adapter = SeleniumWebAutomationAdapter(driver)
locator = AutoHealLocator.builder().with_web_adapter(adapter).build()

driver.get("https://example.com/login")
button = locator.find_element("#submit-btn", "Submit button")  # heals automatically
button.click()

3. All Supported Locator Types

AutoHeal detects the locator type from the string format — no By.X needed:

# CSS selectors
locator.find_element("#submit-btn", "Submit button")
locator.find_element(".btn-primary", "Primary button")
locator.find_element("button[type='submit']", "Submit button")
locator.find_element("form > input.email", "Email input")

# XPath
locator.find_element("//button[@id='submit']", "Submit button")
locator.find_element("//input[@placeholder='Username']", "Username field")
locator.find_element("//a[contains(text(),'Login')]", "Login link")

# Bare ID / Name / Class
locator.find_element("submit-btn", "Submit button")
locator.find_element("username", "Username field")

# Multiple elements
items = locator.find_elements(".product-card", "Product cards")

# Presence check — no exception if missing
if locator.is_element_present("#promo-banner", "Promo banner"):
    locator.find_element("#promo-banner", "Promo banner").click()

4. Full Login Example

import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from autoheal import AutoHealLocator
from autoheal.impl.adapter import SeleniumWebAutomationAdapter
from dotenv import load_dotenv

load_dotenv()


@pytest.fixture
def driver():
    options = Options()
    options.add_argument("--start-maximized")
    service = Service(ChromeDriverManager().install())
    d = webdriver.Chrome(service=service, options=options)
    d.implicitly_wait(10)
    yield d
    d.quit()


@pytest.fixture
def autoheal(driver):
    adapter = SeleniumWebAutomationAdapter(driver)
    return AutoHealLocator.builder().with_web_adapter(adapter).build()


def test_login(driver, autoheal):
    driver.get("https://www.saucedemo.com")
    autoheal.find_element("#user-name", "Username field").send_keys("standard_user")
    autoheal.find_element("#password", "Password field").send_keys("secret_sauce")
    autoheal.find_element("#login-button", "Login button").click()
    assert "/inventory.html" in driver.current_url


def test_login_broken_selectors(driver, autoheal):
    """AutoHeal heals intentionally broken selectors."""
    driver.get("https://www.saucedemo.com")
    autoheal.find_element("#user-name-wrong", "Username field").send_keys("standard_user")
    autoheal.find_element("#password-wrong", "Password field").send_keys("secret_sauce")
    autoheal.find_element("#login-button-wrong", "Login button").click()
    assert "/inventory.html" in driver.current_url  # still passes!

Playwright Quick Start

1. Install with Playwright Extra

pip install autoheal-locator[playwright]
playwright install chromium

2. Usage

AutoHeal for Playwright is fully async. It supports both CSS string selectors and native Playwright Locators:

import pytest
from playwright.async_api import async_playwright
from autoheal.impl.adapter import PlaywrightWebAutomationAdapter
from autoheal.reporting.reporting_autoheal_locator import ReportingAutoHealLocator

# Using a broken CSS selector — AI heals it
async def test_heal_broken_selector(page, autoheal_locator):
    await page.goto("https://www.saucedemo.com")

    username = await autoheal_locator.find_element_async(
        "#user-name-BROKEN",
        "Username input field on the SauceDemo login page"
    )
    await username.fill("standard_user")
    assert await username.input_value() == "standard_user"


# Using a broken native Playwright Locator — AI heals it
async def test_heal_broken_native_locator(page, autoheal_locator):
    await page.goto("https://www.saucedemo.com")

    username = await autoheal_locator.find_async(
        page.get_by_role("textbox", name="Username-BROKEN"),
        "Username input field on the SauceDemo login page"
    )
    await username.fill("standard_user")
    assert await username.input_value() == "standard_user"

3. pytest Fixture

import pytest
from playwright.async_api import async_playwright
from autoheal.impl.adapter import PlaywrightWebAutomationAdapter
from autoheal.reporting.reporting_autoheal_locator import ReportingAutoHealLocator
from config.autoheal_config import get_autoheal_config


@pytest.fixture(scope="function")
async def browser():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        yield browser
        await browser.close()


@pytest.fixture(scope="function")
async def page(browser):
    page = await browser.new_page()
    yield page
    await page.close()


@pytest.fixture(scope="function")
def autoheal_locator(page):
    config = get_autoheal_config()
    adapter = PlaywrightWebAutomationAdapter(page)
    locator = ReportingAutoHealLocator(adapter, config)
    yield locator
    locator.shutdown()  # generates HTML/JSON/text reports

4. Native Locator Types Supported

# Role-based
await autoheal_locator.find_async(page.get_by_role("button", name="Login"), "Login button")
await autoheal_locator.find_async(page.get_by_role("textbox", name="Username"), "Username field")

# Text-based
await autoheal_locator.find_async(page.get_by_text("Add to cart", exact=True), "Add to cart button")

# Placeholder
await autoheal_locator.find_async(page.get_by_placeholder("Email address"), "Email input")

# CSS / XPath
await autoheal_locator.find_async(page.locator("#submit-btn"), "Submit button")

# CSS string shorthand
await autoheal_locator.find_element_async("#submit-btn", "Submit button")

AI Provider Configuration

Configure one provider only. AutoHeal detects which one to use based on which environment variable is set.

Groq — Free, Recommended for Getting Started

GROQ_API_KEY=gsk_your_api_key_here
GROQ_MODEL=llama-3.3-70b-versatile    # optional

Get a free key at console.groq.com.

Google Gemini

GEMINI_API_KEY=AIza_your_api_key_here
GEMINI_MODEL=gemini-2.0-flash          # optional

Free tier has rate limits. Use DOM_ONLY or SMART_SEQUENTIAL strategy to reduce API calls.

OpenAI

OPENAI_API_KEY=sk-proj-your_key
OPENAI_MODEL=gpt-4o-mini               # optional — gpt-4o-mini is cheapest

Local LLM — Ollama / LM Studio (Free, Private)

# Ollama
AUTOHEAL_API_URL=http://localhost:11434/v1/chat/completions
AUTOHEAL_MODEL=deepseek-coder-v2:16b
AUTOHEAL_API_KEY=not-needed

# LM Studio
AUTOHEAL_API_URL=http://localhost:1234/v1/chat/completions
AUTOHEAL_MODEL=your-loaded-model-name
AUTOHEAL_API_KEY=not-needed

Provider Comparison

Provider Visual Cost Speed Notes
Groq Yes Free Fastest Best for getting started
Gemini Yes Low Fast Free tier has rate limits
OpenAI Yes Medium Fast gpt-4o-mini is cost-effective
Anthropic Yes Medium Medium Requires LiteLLM proxy
DeepSeek No Low Fast Good for DOM-only use
Ollama Depends Free Varies Private, no data sent externally

Programmatic Configuration

from autoheal.config import AIConfig
from autoheal.models.enums import AIProvider

ai_config = AIConfig.builder() \
    .provider(AIProvider.GROQ) \
    .api_key(os.getenv("GROQ_API_KEY")) \
    .model("llama-3.3-70b-versatile") \
    .temperature_dom(0.1) \
    .build()

Execution Strategies

# .env
AUTOHEAL_EXECUTION_STRATEGY=SMART_SEQUENTIAL
Strategy Cost Speed Best For
SMART_SEQUENTIAL Low Medium Default — DOM first, visual fallback
DOM_ONLY Lowest Fastest CI/CD, cost-sensitive
VISUAL_FIRST High Medium Complex UIs where DOM structure unreliable
SEQUENTIAL Medium Medium Debugging
PARALLEL Highest Fastest healing Speed-critical scenarios

Cache Configuration

from autoheal.config import CacheConfig
from autoheal.config.cache_config import CacheType
from datetime import timedelta

# Persistent file (default) — survives restarts
cache_config = CacheConfig.builder() \
    .cache_type(CacheType.PERSISTENT_FILE) \
    .maximum_size(500) \
    .expire_after_write(timedelta(hours=24)) \
    .build()

# In-memory — fastest, lost when process ends
cache_config = CacheConfig.builder() \
    .cache_type(CacheType.CAFFEINE) \
    .maximum_size(1000) \
    .build()

# Redis — shared across machines
cache_config = CacheConfig.builder() \
    .cache_type(CacheType.REDIS) \
    .redis_host("localhost") \
    .redis_port(6379) \
    .build()

Cache Strategy via YAML

Create config/cache_strategy.yaml:

cache:
  type: PERSISTENT_FILE     # PERSISTENT_FILE | CAFFEINE | REDIS
  maximum_size: 500
  expire_after_hours: 24

  # Redis-only settings
  redis:
    host: localhost
    port: 6379
    password: null

Cache Management

locator.clear_cache()
locator.remove_cached_selector("#old-btn", "Submit button")
print(locator.get_cache_size())

metrics = locator.get_cache_metrics()
print(f"Hit rate: {metrics.total_hits / (metrics.total_hits + metrics.total_misses):.0%}")

Performance Configuration

# .env
AUTOHEAL_QUICK_TIMEOUT_MS=500     # timeout to try original selector before healing
AUTOHEAL_ELEMENT_TIMEOUT_SEC=10   # max time per element lookup
from autoheal.config import PerformanceConfig
from autoheal.models.enums import ExecutionStrategy
from datetime import timedelta

perf_config = PerformanceConfig.builder() \
    .execution_strategy(ExecutionStrategy.SMART_SEQUENTIAL) \
    .quick_check_timeout(timedelta(milliseconds=500)) \
    .element_timeout(timedelta(seconds=10)) \
    .build()

Reporting

from autoheal.config import ReportingConfig
from pathlib import Path

reporting_config = ReportingConfig.builder() \
    .enabled(True) \
    .generate_html(True) \
    .generate_json(True) \
    .generate_text(True) \
    .output_directory(str(Path(__file__).parent.parent / "autoheal-reports")) \
    .report_name_prefix("MyProject") \
    .console_logging(True) \
    .build()

Console output during test run:

[SUCCESS] [DOM]    [1250ms] [820 tokens]  #user-name-wrong  ->  #user-name
[SUCCESS] [VISUAL] [3400ms] [1200 tokens] #btn-wrong        ->  .btn-login
[SUCCESS] [CACHED] [2ms]                  #password-wrong   ->  #password
[FAILED]  [FAIL]   [350ms]                #nonexistent      ->  FAILED

pytest Integration

conftest.py — Selenium

import pytest
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from dotenv import load_dotenv
from autoheal import AutoHealLocator
from autoheal.impl.adapter import SeleniumWebAutomationAdapter
from autoheal.reporting.reporting_autoheal_locator import ReportingAutoHealLocator

load_dotenv(Path(__file__).parent / ".env")


@pytest.fixture(scope="function")
def driver():
    options = Options()
    options.add_argument("--start-maximized")
    service = Service(ChromeDriverManager().install())
    d = webdriver.Chrome(service=service, options=options)
    d.implicitly_wait(10)
    yield d
    d.quit()


@pytest.fixture(scope="function")
def autoheal(driver):
    from config.autoheal_config import get_autoheal_config
    adapter = SeleniumWebAutomationAdapter(driver)
    locator = ReportingAutoHealLocator(adapter, get_autoheal_config())
    yield locator
    metrics = locator.autoheal.get_metrics()
    print(f"\nAutoHeal: {metrics.successful_requests}/{metrics.total_requests} healed")

conftest.py — Playwright

import pytest
from playwright.async_api import async_playwright
from autoheal.impl.adapter import PlaywrightWebAutomationAdapter
from autoheal.reporting.reporting_autoheal_locator import ReportingAutoHealLocator


@pytest.fixture(scope="function")
async def browser():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        yield browser
        await browser.close()


@pytest.fixture(scope="function")
async def page(browser):
    page = await browser.new_page()
    yield page
    await page.close()


@pytest.fixture(scope="function")
def autoheal_locator(page):
    from config.autoheal_config import get_autoheal_config
    adapter = PlaywrightWebAutomationAdapter(page)
    locator = ReportingAutoHealLocator(adapter, get_autoheal_config())
    yield locator
    locator.shutdown()

config/autoheal_config.py

import os
from pathlib import Path
from datetime import timedelta
from autoheal import AutoHealConfiguration
from autoheal.config import AIConfig, CacheConfig, PerformanceConfig, ResilienceConfig, ReportingConfig
from autoheal.config.cache_config import CacheType
from autoheal.models.enums import AIProvider, ExecutionStrategy


def get_autoheal_config() -> AutoHealConfiguration:
    # Auto-detect AI provider from env vars
    if os.getenv("GROQ_API_KEY"):
        ai = AIConfig.builder() \
            .provider(AIProvider.GROQ) \
            .api_key(os.getenv("GROQ_API_KEY")) \
            .api_url("https://api.groq.com/openai/v1/chat/completions") \
            .model(os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")) \
            .build()
    elif os.getenv("GEMINI_API_KEY"):
        ai = AIConfig.builder() \
            .provider(AIProvider.GOOGLE_GEMINI) \
            .api_key(os.getenv("GEMINI_API_KEY")) \
            .model(os.getenv("GEMINI_MODEL", "gemini-2.0-flash")) \
            .build()
    elif os.getenv("OPENAI_API_KEY"):
        ai = AIConfig.builder() \
            .provider(AIProvider.OPENAI) \
            .api_key(os.getenv("OPENAI_API_KEY")) \
            .api_url("https://api.openai.com/v1/chat/completions") \
            .model(os.getenv("OPENAI_MODEL", "gpt-4o-mini")) \
            .build()
    elif os.getenv("AUTOHEAL_API_URL"):
        ai = AIConfig.builder() \
            .provider(AIProvider.OPENAI) \
            .api_key(os.getenv("AUTOHEAL_API_KEY", "not-needed")) \
            .api_url(os.getenv("AUTOHEAL_API_URL")) \
            .model(os.getenv("AUTOHEAL_MODEL", "deepseek-coder-v2:16b")) \
            .build()
    else:
        raise ValueError("No AI provider configured. Set one of: GROQ_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY")

    strategy_map = {
        "SMART_SEQUENTIAL": ExecutionStrategy.SMART_SEQUENTIAL,
        "DOM_ONLY":         ExecutionStrategy.DOM_ONLY,
        "VISUAL_FIRST":     ExecutionStrategy.VISUAL_FIRST,
        "SEQUENTIAL":       ExecutionStrategy.SEQUENTIAL,
        "PARALLEL":         ExecutionStrategy.PARALLEL,
    }
    strategy = strategy_map.get(
        os.getenv("AUTOHEAL_EXECUTION_STRATEGY", "SMART_SEQUENTIAL").upper(),
        ExecutionStrategy.SMART_SEQUENTIAL
    )

    project_root = Path(__file__).parent.parent

    return AutoHealConfiguration.builder() \
        .ai(ai) \
        .cache(
            CacheConfig.builder()
                .cache_type(CacheType.PERSISTENT_FILE)
                .maximum_size(500)
                .expire_after_write(timedelta(hours=24))
                .build()
        ) \
        .performance(
            PerformanceConfig.builder()
                .execution_strategy(strategy)
                .quick_check_timeout(timedelta(milliseconds=500))
                .element_timeout(timedelta(seconds=10))
                .build()
        ) \
        .resilience(ResilienceConfig.builder().retry_max_attempts(3).build()) \
        .reporting(
            ReportingConfig.builder()
                .enabled(True)
                .generate_html(True)
                .generate_json(True)
                .generate_text(True)
                .output_directory(str(project_root / "autoheal-reports"))
                .report_name_prefix("AutoHeal")
                .console_logging(True)
                .build()
        ) \
        .build()

Full Configuration Reference

.env File

# AI Provider — configure ONLY ONE
GROQ_API_KEY=gsk_your_key
GROQ_MODEL=llama-3.3-70b-versatile

# GEMINI_API_KEY=AIza_your_key
# GEMINI_MODEL=gemini-2.0-flash

# OPENAI_API_KEY=sk-proj-your_key
# OPENAI_MODEL=gpt-4o-mini

# AUTOHEAL_API_URL=http://localhost:11434/v1/chat/completions
# AUTOHEAL_MODEL=deepseek-coder-v2:16b
# AUTOHEAL_API_KEY=not-needed

# Execution Strategy
# Options: SMART_SEQUENTIAL | DOM_ONLY | VISUAL_FIRST | SEQUENTIAL | PARALLEL
AUTOHEAL_EXECUTION_STRATEGY=SMART_SEQUENTIAL

# Performance
AUTOHEAL_QUICK_TIMEOUT_MS=500
AUTOHEAL_ELEMENT_TIMEOUT_SEC=10

Builder Reference

Builder Method Default Purpose
AIConfig .provider(AIProvider.X) Required
AIConfig .api_key(str) Required
AIConfig .model(str) Provider default Model name
AIConfig .temperature_dom(float) 0.1 DOM analysis temperature
CacheConfig .cache_type(CacheType.X) PERSISTENT_FILE Cache backend
CacheConfig .maximum_size(int) 500 Max cached entries
CacheConfig .expire_after_write(timedelta) 24h TTL
PerformanceConfig .execution_strategy(X) SMART_SEQUENTIAL Healing strategy
PerformanceConfig .quick_check_timeout(timedelta) 500ms Original selector timeout
PerformanceConfig .element_timeout(timedelta) 10s Full lookup timeout

API Reference

# Find single element (sync)
element = locator.find_element(selector, description)

# Find single element (async)
element = await locator.find_element_async(selector, description)

# Playwright only — pass native Locator
element = await locator.find_async(page.get_by_role("button"), description)

# Find multiple elements
elements = locator.find_elements(selector, description)

# Presence check — no exception if missing
if locator.is_element_present(selector, description):
    ...

# Cache management
locator.clear_cache()
locator.remove_cached_selector(selector, description)
locator.get_cache_size()

# Metrics
metrics = locator.get_metrics()
cache = locator.get_cache_metrics()
health = locator.get_health_status()

# Shutdown — flush cache, close connections, generate reports
locator.shutdown()
await locator.shutdown()  # async version

Best Practices

Use descriptive element names — the description is sent to the AI when healing is needed:

# Vague — AI has little context
locator.find_element("#btn-1", "button")

# Specific — AI understands exactly what to find
locator.find_element("#btn-1", "Submit payment button on checkout page")

Match strategy to environment:

SMART_SEQUENTIAL  # production and CI — cost-effective
DOM_ONLY          # local dev when you want fast runs
VISUAL_FIRST      # highly visual UIs where DOM structure is unreliable

Use absolute paths for reports so they always land in the project root regardless of where pytest is run from:

.output_directory(str(Path(__file__).parent.parent / "autoheal-reports"))

Scope fixtures to function — each test should get its own driver and locator instance to avoid cache collisions between tests.


Troubleshooting

No AI provider configured Set exactly one API key in your .env. See AI Provider Configuration.

Multiple AI providers configured Comment out all but one provider block in .env.

Gemini 404 The correct Gemini base URL is https://generativelanguage.googleapis.com/v1 — without a trailing /models.

Gemini 429 rate limited Switch to AUTOHEAL_EXECUTION_STRATEGY=DOM_ONLY or SMART_SEQUENTIAL to reduce screenshot-based calls.

Visual analysis returns wrong selector Visual AI infers selectors from screenshots — it cannot read actual HTML attributes. Use SMART_SEQUENTIAL so DOM analysis runs first; visual is the fallback.

Reports landing in wrong folder Use Path(__file__).parent.parent / "autoheal-reports" instead of "./autoheal-reports". Relative paths depend on where pytest is launched from.


Project Structure

autoheal/
├── autoheal_locator.py          # Main AutoHealLocator class
├── __init__.py
├── config/                      # AIConfig, CacheConfig, PerformanceConfig, etc.
├── impl/
│   ├── adapter/                 # SeleniumWebAutomationAdapter, PlaywrightWebAutomationAdapter
│   ├── ai/providers/            # GeminiProvider, OpenAIProvider, GroqProvider, ...
│   ├── cache/                   # FileSelectorCache, RedisCache, CachetoolsCache
│   └── locator/                 # DOMElementLocator, VisualElementLocator, HybridLocator
├── models/
│   └── enums.py                 # AIProvider, ExecutionStrategy, CacheType
├── reporting/                   # ReportingAutoHealLocator, HTML/JSON/text reporters
└── utils/                       # Locator type detection, helpers

License

MIT License. See LICENSE.


Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

autoheal_locator-1.0.10.tar.gz (119.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

autoheal_locator-1.0.10-py3-none-any.whl (169.9 kB view details)

Uploaded Python 3

File details

Details for the file autoheal_locator-1.0.10.tar.gz.

File metadata

  • Download URL: autoheal_locator-1.0.10.tar.gz
  • Upload date:
  • Size: 119.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for autoheal_locator-1.0.10.tar.gz
Algorithm Hash digest
SHA256 e427732e647e1aed1532fd42ed4f6ccdd300e9da77ed1617404db768067ca776
MD5 48b74e0c4c126d91621e49bd860a58a4
BLAKE2b-256 eec4c92b8850b21d69a186fed301fb539ec5e843c811aabad10657de74cf5f3a

See more details on using hashes here.

File details

Details for the file autoheal_locator-1.0.10-py3-none-any.whl.

File metadata

File hashes

Hashes for autoheal_locator-1.0.10-py3-none-any.whl
Algorithm Hash digest
SHA256 4df292b5e514f56f4b575574c740ba04d8717d8eace8e5841004660db53fe43a
MD5 3e3e3f8b814184dc6d3f240caeef4199
BLAKE2b-256 1740bc8c4be1656a3178381493ebe31d9753199567a95bc0a149c79fe0f6cd89

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page