Add a max_length option to slugify
Done- Plan (done)
- Code (done)
- Test (done)
- Review (done)
- Finish (done)
Cost
$0.0062
Tests
Passed
Iterations
1
Re-plans
0
LLM calls
11
Prompt read from cache
73%
Usage by agent
- Cache read
- Cache write
- Input
- Output
View as table
| Agent | Cost | Cache read | Cache write | Input | Output |
|---|---|---|---|---|---|
| planner | $0.0012 | 5,769 | 766 | 8 | 2,116 |
| coder | $0.0022 | 15,118 | 4,487 | 10 | 2,918 |
| tester | $0.0017 | 13,136 | 4,974 | 8 | 1,954 |
| reviewer | $0.0011 | 1,727 | 3,264 | 4 | 1,273 |
Timeline
-
planner iteration 1 Finished 3 calls · 8,659 tokens · $0.0012 · 11.6s
Loading…
-
coder iteration 1 Finished 4 calls · 22.5K tokens · $0.0022 · 15.3s
Loading…
-
tester iteration 1 Finished 3 calls · 20.1K tokens · $0.0017 · 11.1s
Loading…
-
verify iteration 1 Passed 0.6s
............. [100%] 13 passed in 0.07s
-
reviewer iteration 1 Finished 1 calls · 6,268 tokens · $0.0011 · 6.4s
Loading…
Final diff
diff --git a/slug.py b/slug.py index c4d3700..108d6fd 100644 --- a/slug.py +++ b/slug.py @@ -1,5 +1,17 @@ import re +from typing import Optional -def slugify(text: str) -> str: - return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-") +def slugify(text: str, max_length: Optional[int] = None) -> str: + slug = re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-") + if max_length is None or len(slug) <= max_length: + return slug + if max_length < 0: + raise ValueError("max_length must be non-negative") + # Look one character past max_length so a word ending exactly at + # max_length is kept whole. + idx = slug[:max_length + 1].rfind("-") + if idx == -1: + # The first word alone is longer than max_length: cut it. + return slug[:max_length] + return slug[:idx] diff --git a/test_slug.py b/test_slug.py index 73209ea..fa8fb14 100644 --- a/test_slug.py +++ b/test_slug.py @@ -1,5 +1,96 @@ +import random +import re + +import pytest + from slug import slugify def test_basic(): assert slugify("Hello, World!") == "hello-world" + + +def test_no_max_length_unchanged(): + assert slugify("Hello, World!", None) == "hello-world" + assert slugify("Supercalifragilistic word") == "supercalifragilistic-word" + + +def test_max_length_cuts_at_word_boundary(): + assert slugify("Hello, World!", max_length=5) == "hello" + assert slugify("Hello, World!", max_length=6) == "hello" + assert slugify("Hello, World!", max_length=11) == "hello-world" + + +def test_first_word_longer_than_max_length(): + assert slugify("Supercalifragilistic word", max_length=5) == "super" + + +def test_no_trailing_hyphen(): + assert slugify("a-b-c", max_length=2) == "a" + assert slugify("a-b-c", max_length=3) == "a-b" + + +def test_length_never_exceeds_max_length(): + text = "The quick brown fox jumps over the lazy dog" + for n in range(1, len(text) + 2): + result = slugify(text, max_length=n) + assert len(result) <= n + assert not result.endswith("-") + + +def test_empty_slug_with_max_length(): + assert slugify("!!!", max_length=5) == "" + + +def test_zero_max_length(): + assert slugify("hello world", max_length=0) == "" + + +def test_negative_max_length_raises(): + with pytest.raises(ValueError): + slugify("hello world", max_length=-1) + + +def test_word_ending_exactly_at_max_length_is_kept(): + assert slugify("hello-world", max_length=5) == "hello" + assert slugify("abcde-f", max_length=5) == "abcde" + assert slugify("abcde", max_length=5) == "abcde" + + +def test_first_word_one_char_over_limit_is_cut(): + assert slugify("abcdef-g", max_length=5) == "abcde" + + +def test_result_unchanged_when_within_limit(): + assert slugify("hello world", max_length=11) == "hello-world" + assert slugify("hello world", max_length=100) == "hello-world" + + +def _reference(text, max_length): + slug = re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-") + if len(slug) <= max_length: + return slug + words = slug.split("-") + if len(words[0]) > max_length: + return words[0][:max_length] + kept = [] + total = 0 + for w in words: + extra = len(w) if not kept else len(w) + 1 + if total + extra > max_length: + break + kept.append(w) + total += extra + return "-".join(kept) + + +def test_matches_reference_on_random_inputs(): + rng = random.Random(1234) + alphabet = "abc XYZ-!019é_" + for _ in range(2000): + text = "".join(rng.choice(alphabet) for _ in range(rng.randint(0, 30))) + n = rng.randint(0, 35) + result = slugify(text, max_length=n) + assert result == _reference(text, n), (text, n) + assert len(result) <= n + assert not result.endswith("-")