{"id":31581465,"url":"https://github.com/governmentplates/flask-selfheal","last_synced_at":"2026-05-17T15:39:46.015Z","repository":{"id":317112787,"uuid":"1066033908","full_name":"GovernmentPlates/flask-selfheal","owner":"GovernmentPlates","description":"Automatically fix broken URLs in your Flask app using intelligent resolvers","archived":false,"fork":false,"pushed_at":"2025-09-28T23:18:14.000Z","size":0,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-28T23:45:45.146Z","etag":null,"topics":["flask","self-healing-urls","urls"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GovernmentPlates.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-28T23:07:07.000Z","updated_at":"2025-09-28T23:38:58.000Z","dependencies_parsed_at":"2025-09-28T23:45:47.378Z","dependency_job_id":"8a5dad49-6c9e-45e2-8726-1b9f4fd6eb0a","html_url":"https://github.com/GovernmentPlates/flask-selfheal","commit_stats":null,"previous_names":["governmentplates/flask-selfheal"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/GovernmentPlates/flask-selfheal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GovernmentPlates%2Fflask-selfheal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GovernmentPlates%2Fflask-selfheal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GovernmentPlates%2Fflask-selfheal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GovernmentPlates%2Fflask-selfheal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GovernmentPlates","download_url":"https://codeload.github.com/GovernmentPlates/flask-selfheal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GovernmentPlates%2Fflask-selfheal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278526224,"owners_count":26001326,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flask","self-healing-urls","urls"],"created_at":"2025-10-05T21:58:38.586Z","updated_at":"2026-05-17T15:39:45.994Z","avatar_url":"https://github.com/GovernmentPlates.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flask-selfheal 🔗\n\nAutomatically fix broken URLs in your Flask app using intelligent resolvers.\n\n![PyPI - Version](https://img.shields.io/pypi/v/flask-selfheal)\n![PyPI - License](https://img.shields.io/pypi/l/flask-selfheal)\n\n## Features\n- **Multiple Matching Strategies**: Choose from 1:1 exact matching, fuzzy matching, and even hybrid database-backed approaches to find the best matching URL.\n- **Configurable**: Fine-tune matching strategies, thresholds, and normalization rules to suit your needs.\n- **Chainable Resolvers**: Combine multiple resolvers to create a robust URL healing strategy.\n- **Easy Integration**: Plug into any Flask app with minimal setup.\n- **Performant**: Optimized to try fast methods first and only use expensive ones as a last resort.\n\n\n## Installation\n\n```bash\nuv pip install flask-selfheal\n```\n\n## Usage\n\n### Fuzzy Flask Routes Resolver\n\nThis example demonstrates how to use the `FlaskRoutesResolver` to automatically correct common typos in your Flask routes using fuzzy matching (using `difflib` under the hood).\n\n```python\nfrom flask import Flask\nfrom flask_selfheal import SelfHeal\nfrom flask_selfheal.resolvers import FlaskRoutesResolver\n\napp = Flask(__name__)\n\n@app.route(\"/home\")\ndef home():\n    return \"Welcome Home!\"\n\n@app.route(\"/about\")\ndef about():\n    return \"About Us\"\n\n@app.route(\"/contact\")\ndef contact():\n    return \"Contact Us\"\n\n# Configure SelfHeal with fuzzy matching\nSelfHeal(app, resolvers=[FlaskRoutesResolver()])\n\nif __name__ == \"__main__\":\n    app.run(debug=True)\n```\n\n```\nhttps://example.com/hme    --\u003e Redirects to /home\nhttps://example.com/abot   --\u003e Redirects to /about\nhttps://example.com/contat --\u003e Redirects to /contact\n```\n\n### Chaining Multiple Resolvers\n\nYou can combine multiple resolvers to create a more robust URL healing strategy. In this example, we use both `AliasMappingResolver` and `FuzzyMappingResolver` to handle obsolete URLs and common typos.\n\n```python\nfrom flask import Flask\nfrom flask_selfheal import SelfHeal\nfrom flask_selfheal.resolvers import AliasMappingResolver, FuzzyMappingResolver\n\napp = Flask(__name__)\n\n@app.route(\"/home\")\ndef home():\n    return \"Welcome Home!\"\n\n@app.route(\"/new-path\")\ndef new_path():\n    return \"This is the new path!\"\n\n# Define resolvers - will be tried in order defined\nresolvers = [\n    AliasMappingResolver(\n        {\"old-path\": \"new-path\"}  # Handle obsolete URLs\n    ),\n    FuzzyMappingResolver(\n        [\"home\", \"new-path\"]  # Handle typos\n    )\n]\n\nSelfHeal(app, resolvers=resolvers)\n\nif __name__ == \"__main__\":\n    app.run(debug=True)\n```\n\n```\nhttps://example.com/old-path  --\u003e Redirects to /new-path\nhttps://example.com/hme       --\u003e Redirects to /home\nhttps://example.com/new-pth   --\u003e Redirects to /new-path\n```\n\n### Database-Backed Resolver\n\nThe `DatabaseResolver` allows you to resolve URLs based on the slug in your database (using SQLAlchemy).\n\nEnsure that you have a model with a slug field in your database (example below).\n\n```python\nclass Articles(db.Model):\n    id = db.Column(db.Integer, primary_key=True)\n    slug = db.Column(db.String(100), unique=True, nullable=False) # \u003c--\n    title = db.Column(db.String(100), nullable=False)\n    content = db.Column(db.Text, nullable=False)\n```\n\n```python\nfrom flask import Flask\nfrom app.models import Articles  # Your SQLAlchemy model\nfrom flask_selfheal import SelfHeal\nfrom flask_selfheal.resolvers import DatabaseResolver\n\napp = Flask(__name__)\napp.config[\"SQLALCHEMY_DATABASE_URI\"] = \"sqlite:///yourdatabase.db\"\ndb.init_app(app)\n\n@app.route(\"/articles/\u003cslug\u003e\")\ndef article_detail(slug):\n    article = Articles.query.filter_by(slug=slug).first_or_404()\n    return f\"Article: {article.title}\"\n\n# Configure the DatabaseResolver\ndb_resolver = [\n    DatabaseResolver(\n        Articles,\n        slug_field='slug',\n    )\n]\n\nSelfHeal(app, resolvers=db_resolver, redirect_pattern=\"/articles/{slug}\")\n\nif __name__ == \"__main__\":\n    app.run(debug=True)\n```\n\n```\nhttps://example.com/articles/1234         --\u003e Redirects to /articles/hello-world-1234567\nhttps://example.com/articles/hello-world  --\u003e Redirects to /articles/hello-world-1234567\nhttps://example.com/articles/hell-wrl     --\u003e Redirects to /articles/hello-world-1234567\nhttps://example.com/articles/world        --\u003e Redirects to /articles/hello-world-1234567\n```\n\nThe `DatabaseResolver` follows these strategies in order when trying to find a matching slug:\n1. **Exact match:** Fastest, direct database lookup\n2. **SQL `LIKE` matching:** Handle simple variations using SQL `LIKE` wildcards\n3. **Normalized matching:** Handle common typos (`0 -\u003e o`, `1 -\u003e l`, etc.)\n4. **Word-based matching:** Match individual significant words\n5. **Partial matching:** Match meaningful substrings\n6. **Fuzzy matching:** Handle more complex typos by fuzzy matching\n\n\n### Fine-tuning the Database Resolver\n\nThe `DatabaseResolver` can be customized with various parameters to adjust its behavior:\n\n```python\nresolver = DatabaseResolver(\n    model=Product,\n    slug_field='slug',\n    use_fuzzy=True,                # Enable/disable last resort fuzzy matching\n    fuzzy_cutoff=0.7,              # Similarity threshold (0-1)\n    enable_word_matching=True,     # Match individual words\n    enable_partial_matching=True,  # Match partial strings\n    min_word_length=3,             # Minimum word length to consider\n    custom_normalizers={           # Custom normalizer mappings (0 -\u003e o, ph -\u003e f, etc.)\n        '0': 'o', 'ph': 'f'\n    }\n)\n```\n\nYou can take a look at more examples in the `examples/` directory\n\n\n## License\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\n\n## Contributing\nContributions are welcome! Please open an issue or submit a pull request on GitHub.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgovernmentplates%2Fflask-selfheal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgovernmentplates%2Fflask-selfheal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgovernmentplates%2Fflask-selfheal/lists"}