From f6c388e07af0d4be148e0dfe40683e1de7ef597f Mon Sep 17 00:00:00 2001 From: Ian Robinson Date: Tue, 7 Jul 2026 11:04:25 -0400 Subject: [PATCH] feat: PyPI release prep - import rename, 0.3.0, publish workflow - Import package renamed ascii_magic -> asciimagic: PyPI's existing ascii-magic project owns the ascii_magic import name, so co-installs would collide. Done now, before anyone depends on ours. Console script names are unchanged; the dist remains ascii-magic-tools. - Version 0.3.0; pyproject gains urls/keywords/version classifiers. - Tag-triggered publish workflow (v* tags): test -> uv build -> twine check -> PyPI trusted publishing (id-token, no secrets), environment 'pypi'. - Verified: wheel smoke-installed into a clean venv - scripts run and the GUI static files ship. Co-Authored-By: Claude Fable 5 --- .github/workflows/publish.yml | 30 +++++++++++ README.MD | 52 +++++++++++-------- pyproject.toml | 31 +++++++---- src/{ascii_magic => asciimagic}/__init__.py | 2 +- src/{ascii_magic => asciimagic}/animate.py | 0 .../colorize_ascii.py | 0 src/{ascii_magic => asciimagic}/completion.py | 4 +- src/{ascii_magic => asciimagic}/greet.py | 0 .../image_to_ascii.py | 0 src/{ascii_magic => asciimagic}/pipeline.py | 0 src/{ascii_magic => asciimagic}/static/app.js | 0 .../static/index.html | 0 .../static/style.css | 0 .../text_to_ascii.py | 0 .../unified_cli.py | 16 +++--- src/{ascii_magic => asciimagic}/video.py | 0 src/{ascii_magic => asciimagic}/webapp.py | 2 +- tests/test_animate.py | 10 ++-- tests/test_caption.py | 6 +-- tests/test_cli.py | 4 +- tests/test_completion.py | 10 ++-- tests/test_greet.py | 2 +- tests/test_image_to_ascii.py | 4 +- tests/test_matrix_color.py | 4 +- tests/test_pipeline.py | 4 +- tests/test_text_to_ascii.py | 2 +- tests/test_unified_cli.py | 52 +++++++++---------- tests/test_video.py | 10 ++-- tests/test_webapp.py | 8 +-- 29 files changed, 153 insertions(+), 100 deletions(-) create mode 100644 .github/workflows/publish.yml rename src/{ascii_magic => asciimagic}/__init__.py (98%) rename src/{ascii_magic => asciimagic}/animate.py (100%) rename src/{ascii_magic => asciimagic}/colorize_ascii.py (100%) rename src/{ascii_magic => asciimagic}/completion.py (98%) rename src/{ascii_magic => asciimagic}/greet.py (100%) rename src/{ascii_magic => asciimagic}/image_to_ascii.py (100%) rename src/{ascii_magic => asciimagic}/pipeline.py (100%) rename src/{ascii_magic => asciimagic}/static/app.js (100%) rename src/{ascii_magic => asciimagic}/static/index.html (100%) rename src/{ascii_magic => asciimagic}/static/style.css (100%) rename src/{ascii_magic => asciimagic}/text_to_ascii.py (100%) rename src/{ascii_magic => asciimagic}/unified_cli.py (88%) rename src/{ascii_magic => asciimagic}/video.py (100%) rename src/{ascii_magic => asciimagic}/webapp.py (99%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..66760f4 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,30 @@ +name: Publish to PyPI + +on: + push: + tags: ["v*"] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + - run: uv sync --all-extras + - run: uv run --no-sync pytest -q + + publish: + needs: test + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write # PyPI trusted publishing — no API token secrets + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + - name: Build sdist and wheel + run: uv build + - name: Check metadata renders + run: uvx twine check dist/* + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/README.MD b/README.MD index 895e319..8478730 100644 --- a/README.MD +++ b/README.MD @@ -14,10 +14,16 @@ Convert images into colorized ASCII art. This project provides two main tools fo Requires Python 3.10+. ```bash -pip install -e . # CLI tools only -pip install -e ".[web]" # CLI tools + web GUI +pipx install ascii-magic-tools # from PyPI (CLI tools) +pip install "ascii-magic-tools[web]" # + web GUI +pip install "ascii-magic-tools[video]" # + video support + +# from a checkout: +pip install -e ".[web,video]" ``` +The import package is `asciimagic` (the PyPI project `ascii-magic` is an unrelated package that owns the `ascii_magic` import name). + ## Quick Start (Unified CLI) The `ascii-magic` command is the recommended entry point. It provides all tools under one command: @@ -75,6 +81,10 @@ python3 -m venv .venv Always create the virtualenv as `.venv/` inside the repo — never at the repo root — so editors and tools auto-detect it and git ignores it. +### Releasing + +Bump `version` in `pyproject.toml` and `__version__` in `src/asciimagic/__init__.py`, merge, then tag: `git tag v0.3.0 && git push origin v0.3.0`. The publish workflow runs the tests, builds sdist+wheel, and uploads via PyPI trusted publishing (configure once on PyPI: project `ascii-magic-tools` → publisher = this repo, workflow `publish.yml`, environment `pypi`). + ## Usage ### Image to ASCII (`image_to_ascii.py`) @@ -84,7 +94,7 @@ Convert an image directly to ASCII art (preferred: installed console script): ```bash image-to-ascii input.png -o output.txt # or, during development: -python -m ascii_magic.image_to_ascii input.png -o output.txt +python -m asciimagic.image_to_ascii input.png -o output.txt ``` **Options:** @@ -130,14 +140,14 @@ Add colors to existing ASCII art (preferred: installed console script): ```bash colorize-ascii image.png ascii.txt output.ans # or, during development: -python -m ascii_magic.colorize_ascii image.png ascii.txt output.ans +python -m asciimagic.colorize_ascii image.png ascii.txt output.ans ``` If you omit the output file, the tool defaults to ANSI and prints to stdout (convenient for piping). To print HTML to stdout, pass `--format html` and redirect, for example: ```bash -python -m ascii_magic.colorize_ascii image.png ascii.txt -python -m ascii_magic.colorize_ascii image.png ascii.txt --format html > out.html +python -m asciimagic.colorize_ascii image.png ascii.txt +python -m asciimagic.colorize_ascii image.png ascii.txt --format html > out.html ``` **Output formats:** `.ans` (ANSI), `.html` (HTML) @@ -206,7 +216,7 @@ colorize-ascii photo.png art.txt --animate --loops 3 # play in - `--loops N` : Terminal playback repeats; `0` plays until Ctrl-C (default: 3) - `--reveal` : The rain uncovers the colorized image, which persists beneath it — the loop ends (and holds) on the fully revealed picture -All `--matrix-*` knobs (seed, gamma, intensity ranges, mask biasing, `--matrix-color` themes) apply to the rain too, and `--caption*` works in every animation sink — the caption stays static (matching the rain tint by default, or any `--caption-color` including `image`) while the rain falls. The web GUI exposes the same controls under *Matrix mode → Animate*, previews the animation live, and adds a `.gif` download. Programmatic use: `ascii_magic.pipeline.animate(ctx, ...)` returns an object with `frames_ansi()`, `play()`, `to_gif_bytes()`, and `to_html()`. +All `--matrix-*` knobs (seed, gamma, intensity ranges, mask biasing, `--matrix-color` themes) apply to the rain too, and `--caption*` works in every animation sink — the caption stays static (matching the rain tint by default, or any `--caption-color` including `image`) while the rain falls. The web GUI exposes the same controls under *Matrix mode → Animate*, previews the animation live, and adds a `.gif` download. Programmatic use: `asciimagic.pipeline.animate(ctx, ...)` returns an object with `frames_ansi()`, `play()`, `to_gif_bytes()`, and `to_html()`. ### Text to ASCII (`text_to_ascii.py`) @@ -240,14 +250,14 @@ Options: ```bash image-to-ascii photo.png -o art.txt # or during development: - python -m ascii_magic.image_to_ascii photo.png -o art.txt + python -m asciimagic.image_to_ascii photo.png -o art.txt ``` 2. Colorize the output (preferred: console script): ```bash colorize-ascii photo.png art.txt output.ans --max-rows 30 # or during development: - python -m ascii_magic.colorize_ascii photo.png art.txt output.ans --max-rows 30 + python -m asciimagic.colorize_ascii photo.png art.txt output.ans --max-rows 30 ``` 3. View in terminal or browser @@ -258,8 +268,8 @@ You can run image -> ASCII -> colorized output entirely in memory: ```python from PIL import Image -from ascii_magic.pipeline import AsciiPipelineContext, image_to_ascii, colorize -from ascii_magic.colorize_ascii import Options +from asciimagic.pipeline import AsciiPipelineContext, image_to_ascii, colorize +from asciimagic.colorize_ascii import Options img = Image.open("photo.png").convert("RGB") ctx = AsciiPipelineContext(source_image=img) @@ -270,9 +280,9 @@ html_output = colorize(ctx, opt=Options(out_format="html")) ``` There are also lower-level in-memory helpers if you do not want the context object: -- `ascii_magic.image_to_ascii.image_to_text_glyph_from_image(...)` -- `ascii_magic.image_to_ascii.image_to_braille_from_image(...)` -- `ascii_magic.colorize_ascii.colorize_ascii_text(...)` +- `asciimagic.image_to_ascii.image_to_text_glyph_from_image(...)` +- `asciimagic.image_to_ascii.image_to_braille_from_image(...)` +- `asciimagic.colorize_ascii.colorize_ascii_text(...)` ## Video to ASCII (`ascii-magic video`) @@ -365,11 +375,11 @@ ascii-magic web The standalone scripts remain and behave identically: -- `ascii-magic` -> `ascii_magic.unified_cli:main` (subcommands: image, text, colorize, greet, web) -- `image-to-ascii` -> `ascii_magic.image_to_ascii:main` -- `colorize-ascii` -> `ascii_magic.colorize_ascii:main` -- `text-to-ascii` -> `ascii_magic.text_to_ascii:main` -- `ascii-magic-greet` -> `ascii_magic.greet:main` -- `ascii-magic-web` -> `ascii_magic.webapp:main` (requires the `[web]` extra) +- `ascii-magic` -> `asciimagic.unified_cli:main` (subcommands: image, text, colorize, greet, web) +- `image-to-ascii` -> `asciimagic.image_to_ascii:main` +- `colorize-ascii` -> `asciimagic.colorize_ascii:main` +- `text-to-ascii` -> `asciimagic.text_to_ascii:main` +- `ascii-magic-greet` -> `asciimagic.greet:main` +- `ascii-magic-web` -> `asciimagic.webapp:main` (requires the `[web]` extra) -Prefer calling `ascii-magic` or the individual console scripts after `pip install -e .`. You can also invoke modules directly with `python -m ascii_magic.` when developing. +Prefer calling `ascii-magic` or the individual console scripts after `pip install -e .`. You can also invoke modules directly with `python -m asciimagic.` when developing. diff --git a/pyproject.toml b/pyproject.toml index b30b479..6124762 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "ascii-magic-tools" -version = "0.2.0" +version = "0.3.0" description = "Convert images and text to colorized ASCII art - CLI tools, an in-memory pipeline, and a web GUI" requires-python = ">=3.10" dependencies = [ @@ -17,12 +17,25 @@ authors = [ ] readme = "README.MD" license = {text = "Apache-2.0"} +keywords = ["ascii", "ascii-art", "ansi", "terminal", "braille", "matrix", "figlet", "video"] classifiers = [ "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", + "Environment :: Console", + "Topic :: Multimedia :: Graphics", + "Topic :: Text Processing :: Fonts", ] +[project.urls] +Homepage = "https://github.com/irobinson010/ASCII-Magic" +Repository = "https://github.com/irobinson010/ASCII-Magic" +Issues = "https://github.com/irobinson010/ASCII-Magic/issues" + [project.optional-dependencies] web = [ "fastapi>=0.110", @@ -41,13 +54,13 @@ dev = [ ] [project.scripts] -ascii-magic = "ascii_magic.unified_cli:main" -image-to-ascii = "ascii_magic.image_to_ascii:main" -colorize-ascii = "ascii_magic.colorize_ascii:main" -text-to-ascii = "ascii_magic.text_to_ascii:main" -ascii-magic-web = "ascii_magic.webapp:main" -ascii-magic-greet = "ascii_magic.greet:main" -ascii-magic-video = "ascii_magic.video:main" +ascii-magic = "asciimagic.unified_cli:main" +image-to-ascii = "asciimagic.image_to_ascii:main" +colorize-ascii = "asciimagic.colorize_ascii:main" +text-to-ascii = "asciimagic.text_to_ascii:main" +ascii-magic-web = "asciimagic.webapp:main" +ascii-magic-greet = "asciimagic.greet:main" +ascii-magic-video = "asciimagic.video:main" [tool.setuptools] package-dir = {"" = "src"} @@ -56,7 +69,7 @@ package-dir = {"" = "src"} where = ["src"] [tool.setuptools.package-data] -ascii_magic = ["static/*"] +asciimagic = ["static/*"] [tool.pytest.ini_options] diff --git a/src/ascii_magic/__init__.py b/src/asciimagic/__init__.py similarity index 98% rename from src/ascii_magic/__init__.py rename to src/asciimagic/__init__.py index e181f59..e85c0a7 100644 --- a/src/ascii_magic/__init__.py +++ b/src/asciimagic/__init__.py @@ -1,6 +1,6 @@ """ASCII Magic - Convert images to ASCII art.""" -__version__ = "0.2.0" +__version__ = "0.3.0" __author__ = "Ian Robinson" """ diff --git a/src/ascii_magic/animate.py b/src/asciimagic/animate.py similarity index 100% rename from src/ascii_magic/animate.py rename to src/asciimagic/animate.py diff --git a/src/ascii_magic/colorize_ascii.py b/src/asciimagic/colorize_ascii.py similarity index 100% rename from src/ascii_magic/colorize_ascii.py rename to src/asciimagic/colorize_ascii.py diff --git a/src/ascii_magic/completion.py b/src/asciimagic/completion.py similarity index 98% rename from src/ascii_magic/completion.py rename to src/asciimagic/completion.py index bb80427..dfab227 100644 --- a/src/ascii_magic/completion.py +++ b/src/asciimagic/completion.py @@ -99,7 +99,7 @@ def bash_script() -> str: lines = [ "# ascii-magic shell completion - generated by `ascii-magic completion bash`", - "_ascii_magic() {", + "_asciimagic() {", " local cur prev cmd base", ' cur="${COMP_WORDS[COMP_CWORD]}"', ' prev="${COMP_WORDS[COMP_CWORD-1]}"', @@ -153,7 +153,7 @@ def bash_script() -> str: " esac", " return 0", "}", - f"complete -o default -F _ascii_magic {all_names}", + f"complete -o default -F _asciimagic {all_names}", "", ] return "\n".join(lines) diff --git a/src/ascii_magic/greet.py b/src/asciimagic/greet.py similarity index 100% rename from src/ascii_magic/greet.py rename to src/asciimagic/greet.py diff --git a/src/ascii_magic/image_to_ascii.py b/src/asciimagic/image_to_ascii.py similarity index 100% rename from src/ascii_magic/image_to_ascii.py rename to src/asciimagic/image_to_ascii.py diff --git a/src/ascii_magic/pipeline.py b/src/asciimagic/pipeline.py similarity index 100% rename from src/ascii_magic/pipeline.py rename to src/asciimagic/pipeline.py diff --git a/src/ascii_magic/static/app.js b/src/asciimagic/static/app.js similarity index 100% rename from src/ascii_magic/static/app.js rename to src/asciimagic/static/app.js diff --git a/src/ascii_magic/static/index.html b/src/asciimagic/static/index.html similarity index 100% rename from src/ascii_magic/static/index.html rename to src/asciimagic/static/index.html diff --git a/src/ascii_magic/static/style.css b/src/asciimagic/static/style.css similarity index 100% rename from src/ascii_magic/static/style.css rename to src/asciimagic/static/style.css diff --git a/src/ascii_magic/text_to_ascii.py b/src/asciimagic/text_to_ascii.py similarity index 100% rename from src/ascii_magic/text_to_ascii.py rename to src/asciimagic/text_to_ascii.py diff --git a/src/ascii_magic/unified_cli.py b/src/asciimagic/unified_cli.py similarity index 88% rename from src/ascii_magic/unified_cli.py rename to src/asciimagic/unified_cli.py index 573b0ad..1c09f1c 100644 --- a/src/ascii_magic/unified_cli.py +++ b/src/asciimagic/unified_cli.py @@ -6,13 +6,13 @@ from typing import Sequence, List, Optional COMMANDS = { - "colorize": "ascii_magic.colorize_ascii", - "image": "ascii_magic.image_to_ascii", - "text": "ascii_magic.text_to_ascii", - "greet": "ascii_magic.greet", - "web": "ascii_magic.webapp", - "video": "ascii_magic.video", - "completion": "ascii_magic.completion", + "colorize": "asciimagic.colorize_ascii", + "image": "asciimagic.image_to_ascii", + "text": "asciimagic.text_to_ascii", + "greet": "asciimagic.greet", + "web": "asciimagic.webapp", + "video": "asciimagic.video", + "completion": "asciimagic.completion", } @@ -60,7 +60,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int: return 0 if argv[0] in ("-V", "--version"): - from ascii_magic import __version__ + from asciimagic import __version__ print(f"ascii-magic {__version__}") return 0 diff --git a/src/ascii_magic/video.py b/src/asciimagic/video.py similarity index 100% rename from src/ascii_magic/video.py rename to src/asciimagic/video.py diff --git a/src/ascii_magic/webapp.py b/src/asciimagic/webapp.py similarity index 99% rename from src/ascii_magic/webapp.py rename to src/asciimagic/webapp.py index b61022b..c7dabae 100644 --- a/src/ascii_magic/webapp.py +++ b/src/asciimagic/webapp.py @@ -5,7 +5,7 @@ and HTML renders in one response. The single-page GUI in ``static/`` is served from the same app. -Run locally: ascii-magic-web (or: uvicorn ascii_magic.webapp:app) +Run locally: ascii-magic-web (or: uvicorn asciimagic.webapp:app) """ from __future__ import annotations diff --git a/tests/test_animate.py b/tests/test_animate.py index 9bd3529..b4398de 100644 --- a/tests/test_animate.py +++ b/tests/test_animate.py @@ -4,9 +4,9 @@ import pytest from PIL import Image -from ascii_magic.animate import AnimationOptions, generate -from ascii_magic.colorize_ascii import MatrixOptions -from ascii_magic.pipeline import AsciiPipelineContext, animate +from asciimagic.animate import AnimationOptions, generate +from asciimagic.colorize_ascii import MatrixOptions +from asciimagic.pipeline import AsciiPipelineContext, animate def _image(): @@ -114,7 +114,7 @@ def test_pipeline_animate(): def test_caption_in_all_animation_sinks(): - from ascii_magic.colorize_ascii import CaptionOptions + from asciimagic.colorize_ascii import CaptionOptions cap = CaptionOptions(text="Cat", style="box", position="bottom") anim = generate( @@ -147,7 +147,7 @@ def test_caption_in_all_animation_sinks(): def test_caption_image_colors_in_animation(): - from ascii_magic.colorize_ascii import CaptionOptions + from asciimagic.colorize_ascii import CaptionOptions cap = CaptionOptions(text="Cat", style="box", color="image") anim = generate( diff --git a/tests/test_caption.py b/tests/test_caption.py index db369e8..138f5d5 100644 --- a/tests/test_caption.py +++ b/tests/test_caption.py @@ -2,8 +2,8 @@ from PIL import Image -from ascii_magic.colorize_ascii import CaptionOptions, Options, colorize_ascii_text -from ascii_magic.text_to_ascii import caption_lines, compose_caption +from asciimagic.colorize_ascii import CaptionOptions, Options, colorize_ascii_text +from asciimagic.text_to_ascii import caption_lines, compose_caption ART = "\n".join("#" * 40 for _ in range(10)) STRIP = lambda s: re.sub(r"\x1b\[[0-9;]*m", "", s) @@ -133,7 +133,7 @@ def test_caption_figlet_downscales_to_fit(): def test_wrap_html_default_text_contrasts_background(): - from ascii_magic.colorize_ascii import wrap_html + from asciimagic.colorize_ascii import wrap_html doc = wrap_html(["hello"]) assert "background: #000" in doc diff --git a/tests/test_cli.py b/tests/test_cli.py index 3ed4b27..2dd6b4b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -7,8 +7,8 @@ import pytest from PIL import Image -from ascii_magic.unified_cli import COMMANDS, main as cli_main -from ascii_magic.colorize_ascii import parse_args +from asciimagic.unified_cli import COMMANDS, main as cli_main +from asciimagic.colorize_ascii import parse_args def test_new_subcommands_registered(): diff --git a/tests/test_completion.py b/tests/test_completion.py index 8816e2b..c8eae7a 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -3,7 +3,7 @@ import pytest -from ascii_magic.completion import bash_script, main as completion_main, zsh_script +from asciimagic.completion import bash_script, main as completion_main, zsh_script def test_bash_script_contents(): @@ -26,7 +26,7 @@ def test_bash_script_contents(): def test_zsh_script_wraps_bash(): z = zsh_script() assert "bashcompinit" in z - assert "_ascii_magic()" in z + assert "_asciimagic()" in z def test_completion_command(capsys): @@ -36,10 +36,10 @@ def test_completion_command(capsys): def test_completion_dispatch_via_unified_cli(capsys): - from ascii_magic.unified_cli import main as cli_main + from asciimagic.unified_cli import main as cli_main assert cli_main(["completion", "bash"]) == 0 - assert "complete -o default -F _ascii_magic" in capsys.readouterr().out + assert "complete -o default -F _asciimagic" in capsys.readouterr().out BASH = shutil.which("bash") @@ -68,7 +68,7 @@ def test_bash_completion_behaves(tmp_path, words, expected, not_expected): source "{script_file}" COMP_WORDS=({comp_words}) COMP_CWORD={len(words) - 1} -_ascii_magic +_asciimagic printf '%s\\n' "${{COMPREPLY[@]}}" """ r = subprocess.run([BASH, "-c", harness], capture_output=True, text=True, timeout=30) diff --git a/tests/test_greet.py b/tests/test_greet.py index ec21072..9b7fe08 100644 --- a/tests/test_greet.py +++ b/tests/test_greet.py @@ -4,7 +4,7 @@ import pytest -from ascii_magic import greet +from asciimagic import greet # greet writes POSIX shell rc blocks; install is gated off on Windows. pytestmark = pytest.mark.skipif(os.name == "nt", reason="greet targets POSIX shells") diff --git a/tests/test_image_to_ascii.py b/tests/test_image_to_ascii.py index b19eba1..1a25515 100644 --- a/tests/test_image_to_ascii.py +++ b/tests/test_image_to_ascii.py @@ -1,7 +1,7 @@ import numpy as np from PIL import Image, ImageDraw -from ascii_magic.image_to_ascii import ( +from asciimagic.image_to_ascii import ( _floyd_steinberg_dots, image_to_braille_from_image, image_to_text_glyph_from_image, @@ -59,7 +59,7 @@ def test_vectorized_matches_per_cell_reference(): art_best = _glyph(img, "best", cols=cols) # Reference: replicate preprocessing, then match cell-by-cell. - from ascii_magic.image_to_ascii import preprocess_image, sobel_gradients + from asciimagic.image_to_ascii import preprocess_image, sobel_gradients g = preprocess_image(img.convert("L"), autocontrast=False, gamma=1.0, invert=False) W, H = g.size diff --git a/tests/test_matrix_color.py b/tests/test_matrix_color.py index 571d7c0..adb473f 100644 --- a/tests/test_matrix_color.py +++ b/tests/test_matrix_color.py @@ -1,8 +1,8 @@ import pytest from PIL import Image -from ascii_magic.animate import AnimationOptions, generate -from ascii_magic.colorize_ascii import ( +from asciimagic.animate import AnimationOptions, generate +from asciimagic.colorize_ascii import ( MatrixOptions, Options, colorize_ascii_text, diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index 76af99b..5b7fa88 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -1,8 +1,8 @@ import pytest from PIL import Image -from ascii_magic.colorize_ascii import Options -from ascii_magic.pipeline import AsciiPipelineContext, colorize, image_to_ascii, load_image, text_to_ascii +from asciimagic.colorize_ascii import Options +from asciimagic.pipeline import AsciiPipelineContext, colorize, image_to_ascii, load_image, text_to_ascii def test_context_text_to_ascii_then_colorize_ansi(): diff --git a/tests/test_text_to_ascii.py b/tests/test_text_to_ascii.py index c42921a..081a650 100644 --- a/tests/test_text_to_ascii.py +++ b/tests/test_text_to_ascii.py @@ -2,7 +2,7 @@ import pytest from PIL import Image -from ascii_magic.text_to_ascii import ( +from asciimagic.text_to_ascii import ( text_to_box, text_to_banner, text_to_ascii_art, diff --git a/tests/test_unified_cli.py b/tests/test_unified_cli.py index 422f45e..d76bf10 100644 --- a/tests/test_unified_cli.py +++ b/tests/test_unified_cli.py @@ -3,7 +3,7 @@ import sys from unittest.mock import Mock, patch, MagicMock import pytest -from ascii_magic.unified_cli import main, usage, _call_entry, COMMANDS +from asciimagic.unified_cli import main, usage, _call_entry, COMMANDS # --- Fixtures --- @@ -64,7 +64,7 @@ def test_call_entry_with_argv_param(self): mock_entry.__signature__ = Mock() mock_entry.__signature__.parameters = {"argv": None} - with patch("ascii_magic.unified_cli.inspect.signature") as mock_sig: + with patch("asciimagic.unified_cli.inspect.signature") as mock_sig: mock_sig.return_value.parameters = {"argv": None} result = _call_entry(mock_entry, ["arg1", "arg2"]) @@ -75,7 +75,7 @@ def test_call_entry_without_argv_param(self): """Test calling entry function that doesn't accept argv parameter.""" mock_entry = Mock(return_value=0) - with patch("ascii_magic.unified_cli.inspect.signature") as mock_sig: + with patch("asciimagic.unified_cli.inspect.signature") as mock_sig: mock_sig.return_value.parameters = {} original_argv = sys.argv[:] result = _call_entry(mock_entry, ["arg1", "arg2"], module_prog="test-prog") @@ -89,7 +89,7 @@ def test_call_entry_with_system_exit(self): """Test handling SystemExit exception.""" mock_entry = Mock(side_effect=SystemExit(5)) - with patch("ascii_magic.unified_cli.inspect.signature") as mock_sig: + with patch("asciimagic.unified_cli.inspect.signature") as mock_sig: mock_sig.return_value.parameters = {"argv": None} result = _call_entry(mock_entry, []) @@ -99,7 +99,7 @@ def test_call_entry_with_system_exit_none(self): """Test handling SystemExit with None code.""" mock_entry = Mock(side_effect=SystemExit(None)) - with patch("ascii_magic.unified_cli.inspect.signature") as mock_sig: + with patch("asciimagic.unified_cli.inspect.signature") as mock_sig: mock_sig.return_value.parameters = {"argv": None} result = _call_entry(mock_entry, []) @@ -109,7 +109,7 @@ def test_call_entry_with_exception(self, capsys): """Test handling general exceptions.""" mock_entry = Mock(side_effect=RuntimeError("Test error")) - with patch("ascii_magic.unified_cli.inspect.signature") as mock_sig: + with patch("asciimagic.unified_cli.inspect.signature") as mock_sig: mock_sig.return_value.parameters = {"argv": None} result = _call_entry(mock_entry, []) @@ -159,34 +159,34 @@ def test_main_unknown_command(self, capsys): def test_main_valid_command_colorize(self, mock_module_with_main): """Test main with valid 'colorize' command.""" - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.return_value = mock_module_with_main result = main(["colorize", "arg1", "arg2"]) assert result == 0 - mock_import.assert_called_once_with("ascii_magic.colorize_ascii") + mock_import.assert_called_once_with("asciimagic.colorize_ascii") def test_main_valid_command_image(self, mock_module_with_main): """Test main with valid 'image' command.""" - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.return_value = mock_module_with_main result = main(["image", "file.jpg"]) assert result == 0 - mock_import.assert_called_once_with("ascii_magic.image_to_ascii") + mock_import.assert_called_once_with("asciimagic.image_to_ascii") def test_main_valid_command_text(self, mock_module_with_main): """Test main with valid 'text' command.""" - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.return_value = mock_module_with_main result = main(["text", "Hello"]) assert result == 0 - mock_import.assert_called_once_with("ascii_magic.text_to_ascii") + mock_import.assert_called_once_with("asciimagic.text_to_ascii") def test_main_import_error(self, capsys): """Test main when module import fails.""" - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.side_effect = ImportError("Module not found") result = main(["colorize"]) @@ -199,7 +199,7 @@ def test_main_no_main_function(self, capsys): """Test main when module has no main function.""" mock_module = Mock(spec=[]) # Module without 'main' - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.return_value = mock_module result = main(["colorize"]) @@ -212,7 +212,7 @@ def test_main_non_callable_main(self, capsys): mock_module = Mock() mock_module.main = "not callable" - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.return_value = mock_module result = main(["colorize"]) @@ -227,7 +227,7 @@ def test_main_none_argv_uses_sys_argv(self, mock_module_with_main): sys.argv = ["ascii-magic", "colorize", "test"] with patch( - "ascii_magic.unified_cli.importlib.import_module" + "asciimagic.unified_cli.importlib.import_module" ) as mock_import: mock_import.return_value = mock_module_with_main result = main(None) @@ -242,8 +242,8 @@ def test_main_passes_args_to_subcommand(self): mock_entry = Mock(return_value=0) mock_module.main = mock_entry - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: - with patch("ascii_magic.unified_cli.inspect.signature") as mock_sig: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.inspect.signature") as mock_sig: mock_import.return_value = mock_module mock_sig.return_value.parameters = {"argv": None} @@ -258,8 +258,8 @@ def test_main_subcommand_returns_error_code(self): mock_module = Mock() mock_module.main = Mock(return_value=42) - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: - with patch("ascii_magic.unified_cli.inspect.signature") as mock_sig: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.inspect.signature") as mock_sig: mock_import.return_value = mock_module mock_sig.return_value.parameters = {"argv": None} result = main(["colorize", "test"]) @@ -268,7 +268,7 @@ def test_main_subcommand_returns_error_code(self): def test_main_with_sequence_type(self, mock_module_with_main): """Test that main accepts different sequence types.""" - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.return_value = mock_module_with_main # Test with tuple @@ -291,9 +291,9 @@ def test_commands_dict_has_expected_entries(self): assert "colorize" in COMMANDS assert "image" in COMMANDS assert "text" in COMMANDS - assert COMMANDS["colorize"] == "ascii_magic.colorize_ascii" - assert COMMANDS["image"] == "ascii_magic.image_to_ascii" - assert COMMANDS["text"] == "ascii_magic.text_to_ascii" + assert COMMANDS["colorize"] == "asciimagic.colorize_ascii" + assert COMMANDS["image"] == "asciimagic.image_to_ascii" + assert COMMANDS["text"] == "asciimagic.text_to_ascii" def test_all_commands_are_importable(self): """Test that all command modules can be imported.""" @@ -328,7 +328,7 @@ def test_command_with_many_args(self, mock_module_with_main): """Test command with many arguments.""" args = ["arg" + str(i) for i in range(100)] - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.return_value = mock_module_with_main result = main(["colorize"] + args) @@ -338,7 +338,7 @@ def test_command_with_special_characters(self, mock_module_with_main): """Test command arguments with special characters.""" special_args = ["--file=test.jpg", "arg with spaces", "unicode→test"] - with patch("ascii_magic.unified_cli.importlib.import_module") as mock_import: + with patch("asciimagic.unified_cli.importlib.import_module") as mock_import: mock_import.return_value = mock_module_with_main result = main(["colorize"] + special_args) diff --git a/tests/test_video.py b/tests/test_video.py index 2f4005f..41770f2 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -3,8 +3,8 @@ pytest.importorskip("imageio") -from ascii_magic import video as video_mod -from ascii_magic.greet import read_frames_file +from asciimagic import video as video_mod +from asciimagic.greet import read_frames_file @pytest.fixture @@ -90,7 +90,7 @@ def test_video_glyph_mode(clip): def test_video_matrix_render_deterministic_and_tinted(clip): - from ascii_magic.colorize_ascii import MatrixOptions, parse_matrix_color + from asciimagic.colorize_ascii import MatrixOptions, parse_matrix_color m = MatrixOptions(enabled=True, seed=7, tint=parse_matrix_color("amber")) a = video_mod.video_to_ascii(str(clip), cols=20, max_frames=3, matrix=m) @@ -106,7 +106,7 @@ def test_video_matrix_render_deterministic_and_tinted(clip): def test_video_caption_in_sinks(clip): - from ascii_magic.colorize_ascii import CaptionOptions + from asciimagic.colorize_ascii import CaptionOptions cap = CaptionOptions(text="Cat", style="box", position="bottom") v = video_mod.video_to_ascii(str(clip), cols=24, max_frames=3, caption=cap) @@ -143,7 +143,7 @@ def test_video_matrix_cli_flags(clip, tmp_path): "--matrix", "--matrix-seed", "5", "--matrix-color", "cyan", ]) assert rc == 0 - from ascii_magic.greet import read_frames_file + from asciimagic.greet import read_frames_file frames, _, _ = read_frames_file(out) assert len(frames) == 3 diff --git a/tests/test_webapp.py b/tests/test_webapp.py index 15b6ae4..600dd6a 100644 --- a/tests/test_webapp.py +++ b/tests/test_webapp.py @@ -7,7 +7,7 @@ pytest.importorskip("fastapi") from fastapi.testclient import TestClient -from ascii_magic.webapp import app +from asciimagic.webapp import app client = TestClient(app) @@ -299,7 +299,7 @@ def test_render_video_bad_suffix_400(): def test_render_video_size_cap_413(monkeypatch): - from ascii_magic import webapp + from asciimagic import webapp pytest.importorskip("imageio") monkeypatch.setattr(webapp, "MAX_VIDEO_UPLOAD_BYTES", 100) @@ -312,7 +312,7 @@ def test_render_video_size_cap_413(monkeypatch): def test_upload_size_cap_413(monkeypatch): - from ascii_magic import webapp + from asciimagic import webapp monkeypatch.setattr(webapp, "MAX_UPLOAD_BYTES", 100) r = _render({"source": "image", "cols": 10}) @@ -320,7 +320,7 @@ def test_upload_size_cap_413(monkeypatch): def test_pixel_cap_400(monkeypatch): - from ascii_magic import webapp + from asciimagic import webapp monkeypatch.setattr(webapp, "MAX_IMAGE_PIXELS", 500) # 32x32 png = 1024 px r = _render({"source": "image", "cols": 10})