{"id":39879824,"url":"https://github.com/l1asis/pyansistring","last_synced_at":"2026-04-06T00:11:22.224Z","repository":{"id":319695059,"uuid":"1078938969","full_name":"l1asis/pyansistring","owner":"l1asis","description":"A Python library for string color styling using ANSI escape sequences.","archived":false,"fork":false,"pushed_at":"2026-03-28T08:34:38.000Z","size":645,"stargazers_count":1,"open_issues_count":14,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-28T13:05:26.940Z","etag":null,"topics":["ansi","ansi-colors","ansi-escape","ansi-escape-code","ansi-escape-sequence","cli","color","colored-text","colorize","console","debugging","logging","python","rich-text","string-formatting","terminal","terminal-colors","text-formatting","text-styling","tui"],"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/l1asis.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-10-18T18:36:43.000Z","updated_at":"2026-03-28T08:34:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"73a40148-5b2a-48a5-9291-b339045584dc","html_url":"https://github.com/l1asis/pyansistring","commit_stats":null,"previous_names":["l1asis/pyansistring"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/l1asis/pyansistring","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1asis%2Fpyansistring","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1asis%2Fpyansistring/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1asis%2Fpyansistring/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1asis%2Fpyansistring/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/l1asis","download_url":"https://codeload.github.com/l1asis/pyansistring/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1asis%2Fpyansistring/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31454234,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"ssl_error","status_checked_at":"2026-04-05T21:22:51.943Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["ansi","ansi-colors","ansi-escape","ansi-escape-code","ansi-escape-sequence","cli","color","colored-text","colorize","console","debugging","logging","python","rich-text","string-formatting","terminal","terminal-colors","text-formatting","text-styling","tui"],"created_at":"2026-01-18T14:29:12.878Z","updated_at":"2026-04-06T00:11:22.219Z","avatar_url":"https://github.com/l1asis.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pyansistring\n![pyansistring Banner](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/banner.svg)\n\n[![CI Build](https://github.com/l1asis/pyansistring/actions/workflows/test.yml/badge.svg)](https://github.com/l1asis/pyansistring/actions)\n![Coverage](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fl1asis%2F9f30f8200703cf6886ab323e86b9f0a1%2Fraw%2Fb11b5516d18e6cf01332906bb9bcf3a4ebf14389%2Fpyansistring_coverage.json)\n[![PyPI Version](https://img.shields.io/pypi/v/pyansistring.svg)](https://pypi.org/project/pyansistring/)\n[![PyPI Python version](https://img.shields.io/pypi/pyversions/pyansistring.svg)](https://pypi.org/project/pyansistring/)\n![PyPI downloads](https://img.shields.io/pypi/dm/pyansistring)\n[![License](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n\nANSI-aware string formatting for Python CLIs.\n\n`pyansistring` gives you a string type that keeps styling attached while you keep using familiar string operations. You can color text, apply SGR attributes, generate gradients, color ASCII art, and export styled output to SVG.\n\n## Why pyansistring\n\n- Keeps styling aligned with text during common string operations.\n- Supports 4-bit, 8-bit, and 24-bit (truecolor) foreground/background/underline colors.\n- Works well for CLI tools, logs, dashboards, and terminal UX.\n- Includes practical APIs for gradients, ASCII art coloring, and SVG export.\n\n## Features\n\n- **ANSIString class** that subclasses Python `str`.\n- **Style-preserving operations** for concatenation, slicing, splitting, joining, replacing, stripping, case transforms, and formatting (f-strings, `.format()`).\n- **SGR styling and attributes:** bold, dim, italic, underline, strikethrough, invert, and advanced underline modes (single, double, curly, dotted, dashed).\n- **Color channels:** 4-bit ANSI, 8-bit palette, and 24-bit RGB for foreground, background, and underlines.\n- **Targeting modes:** apply to full strings, slice ranges, or word matches (case-sensitive or insensitive).\n- **Gradient engine:** RGB or HSL interpolation, coordinate-based gradients for multiline text, and out-of-bounds handling.\n- **SVG export:** render text or path modes with per-character coloring and optional custom fonts.\n- **Art registries:** built-in ASCII art, load from TOML, custom color generators, and an optional cowsay adapter.\n- **ANSI parsing:** convert raw ANSI-encoded strings back into `ANSIString` instances with styles intact.\n- **Large constants base:** extensive predefined color constants and palettes, plus SGR/regex helpers for easy access.\n- **Terminal-oriented formatting controls:** supports modern and compatibility SGR formatting modes to improve behavior across ANSI-capable terminals.\n- **Performance-minded internals:** cached line-start indexing, style object caching, and change-tracked re-rendering to reduce repeated work.\n- **Comprehensive test suite** covering edge cases around string operations, style preservation, gradient calculations, and terminal rendering.\n\n## Requirements\n\n- Python 3.11+\n\n## Installation\n\nInstall the base package:\n\n```bash\npip install pyansistring\n```\n\nInstall extras when needed:\n\n```bash\npip install pyansistring[img]            # For SVG export (installs fontTools)\npip install pyansistring[adapter-cowsay] # For the cowsay adapter\npip install pyansistring[adapters]       # All adapters\npip install pyansistring[all]            # Install everything\n```\n\n## Quick Start\n\n```python\nfrom pyansistring import ANSIString, Foreground, Background, SGR\n\ntext = (\n    ANSIString(\"Hello, World!\")\n    .fg_4b(Foreground.YELLOW)\n    .bg_4b(Background.BLUE)\n    .style(SGR.BOLD)\n)\n\nprint(text)\n```\n\n## Usage Examples\n\nThe examples below are generated by [examples/generate_usage_svg.py](examples/generate_usage_svg.py).\n\n### Unstyled text\n\n```python\nfrom pyansistring import ANSIString\n\nprint(ANSIString(\"Hello, World!\"))\n```\n\n![unstyled](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/unstyled.svg)\n\n### Whole-string styling\n\n```python\nfrom pyansistring import ANSIString, Foreground, Background, SGR\n\nprint(\n    ANSIString(\"Hello, World!\")\n    .fg_4b(Foreground.YELLOW)\n    .bg_4b(Background.BLUE)\n    .style(SGR.BOLD)\n)\n```\n\n![whole](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/whole.svg)\n\n### Style by slice\n\n```python\nfrom pyansistring import ANSIString, Foreground, Background, SGR\n\nprint(\n    ANSIString(\"Hello, World!\")\n    .fg_4b(Foreground.YELLOW, (0, 5), (7, 12))\n    .bg_4b(Background.BLUE, (7, 12))\n    .style(SGR.BOLD, (7, 12))\n)\n```\n\n![slice](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/slice.svg)\n\n### Style by words\n\n```python\nfrom pyansistring import ANSIString, Foreground, Background, SGR\n\nprint(\n    ANSIString(\"Hello, World!\")\n    .fg_4b_words(Foreground.YELLOW, \"Hello\", \"World\")\n    .bg_4b_words(Background.BLUE, \"World\")\n    .style_words(SGR.BOLD, \"Hello\", \"World\")\n)\n```\n\n![words](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/words.svg)\n\n### SGR attributes\n\n```python\nfrom pyansistring import ANSIString, SGR\n\nprint(ANSIString(\"Hello, World!\").style(SGR.BOLD).style(SGR.UNDERLINE))\n```\n\n![sgr](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/sgr.svg)\n\n### 4-bit, 8-bit, and 24-bit colors\n\n```python\nfrom pyansistring import ANSIString, Foreground, Background\n\nprint(ANSIString(\"Hello, World!\").fg_4b(Foreground.YELLOW).bg_4b(Background.BLUE))\nprint(ANSIString(\"Hello, World!\").fg_8b(11).bg_8b(4).ul_8b(74))\nprint(ANSIString(\"Hello, World!\").fg_24b(255, 255, 0).bg_24b(0, 0, 238).ul_24b(135, 175, 215))\n```\n\n![4bit](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/4bit.svg)\n![8bit](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/8bit.svg)\n![rgb](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/rgb.svg)\n\n### Underline modes\n\n```python\nfrom pyansistring import ANSIString, UnderlineMode\n\nprint(\n    ANSIString(\"Hello, World!\")\n    .bg_24b(255, 255, 255)\n    .ul_24b(255, 0, 0)\n    .style(UnderlineMode.DOUBLE)\n)\n```\n\n![underline](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/underline.svg)\n\n### Rainbow\n\n```python\nfrom pyansistring import ANSIString\n\nprint(ANSIString(\"Hello, World! This is rainbow text!\").rainbow(fg=True))\n```\n\n![rainbow](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/rainbow.svg)\n\n### Gradient APIs\n\n```python\nfrom pyansistring import ANSIString\n\nprint(\n    ANSIString(\"Hello, World! This is gradient text!\")\n    .gradient([(84, 161, 255), (233, 200, 216)], fg=True)\n)\n\nprint(\n    ANSIString(\"Hello, colorful gradient world!\")\n    .gradient_words([(255, 99, 71), (255, 215, 0)], \"Hello\", \"world\", case_sensitive=False, fg=True)\n)\n\nprint(\n    ANSIString(\"HELLO\\nworld\")\n    .gradient_coordinates(\n        [(255, 0, 120), (0, 200, 255)],\n        (1, 1),\n        (2, 1),\n        (3, 1),\n        (4, 1),\n        (5, 1),\n        index_base=1,\n        fg=True,\n    )\n)\n```\n\n![gradient](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/gradient.svg)  \n![gradient_words](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/gradient_words.svg)  \n![gradient_coordinates](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/gradient_coordinates.svg)\n\n### ArtRegistry and custom generators\n\n```python\nfrom pyansistring import (\n    ArtRegistry,\n    ColorGeneratorContext,\n    register_color_generator,\n    unregister_color_generator,\n)\n\n\ndef zigzag_generator(context: ColorGeneratorContext) -\u003e list[tuple[int, int, int]]:\n    palette = [\n        (84, 161, 255),\n        (255, 99, 71),\n        (255, 215, 0),\n        (120, 220, 160),\n    ]\n    return [palette[i % len(palette)] for i in range(max(2, context[\"step_count\"]))]\n\n\nregister_color_generator(\"zigzag_readme_v1\", zigzag_generator)\ntry:\n    registry = ArtRegistry()\n    registry.register(\n        \"ZIGZAG\",\n        \" /\\\\/\\\\/\\\\/\\\\\\n \\\\/\\\\/\\\\/\\\\/\",\n        colorings=(\n            {\n                \"mode\": \"gradient\",\n                \"colors\": {\n                    \"generator\": \"zigzag_readme_v1\",\n                    \"mode\": \"seeded\",\n                    \"seed\": 42,\n                },\n                \"skip_whitespace\": True,\n                \"fg\": True,\n            },\n        ),\n    )\n    print(registry.get_colored_art(\"ZIGZAG\"))\nfinally:\n    unregister_color_generator(\"zigzag_readme_v1\")\n```\n\n![art_registry_zigzag](https://raw.githubusercontent.com/l1asis/pyansistring/refs/heads/main/images/usage/art_registry_zigzag.svg)\n\n### Parse ANSI text back into ANSIString\n\n```python\nfrom pyansistring import ANSIString\n\nraw = \"\\x1b[31mError\\x1b[0m: file not found\"\nparsed = ANSIString.from_ansi(raw)\n\nprint(parsed.plain_text)\nprint(parsed)\n```\n\n### Export ANSIString to SVG\n\n```python\nfrom fontTools.ttLib import TTFont\nfrom pyansistring import ANSIString, SGR\n\nfont = TTFont(\"path/to/font.ttf\")\nstyled = ANSIString(\"SVG output\").style(SGR.BOLD).fg_24b(90, 170, 255)\n\nsvg_code = styled.to_svg(\n    font=font,\n    font_size_px=16,\n    convert_text_to_path=False,\n)\n```\n\nFor a complete terminal tour, run [examples/showcase.py](examples/showcase.py).  \nFor a focused ArtRegistry walkthrough, run [examples/art_registry_demo.py](examples/art_registry_demo.py).\n\n## Contributing\n\nContributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.\n\n1.  Fork the Project\n2.  Create your Feature Branch (`git checkout -b feature/AmazingFeature`)\n3.  Commit your Changes (`git commit -m 'Add some AmazingFeature'`)\n4.  Push to the Branch (`git push origin feature/AmazingFeature`)\n5.  Open a Pull Request\n\n## License\n\nDistributed under the MIT License. See `LICENSE` for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl1asis%2Fpyansistring","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fl1asis%2Fpyansistring","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl1asis%2Fpyansistring/lists"}