Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Unreleased

- Decode valid numeric entities even when malformed numeric entities are present.
- Support Python 3.14.
- Drop support for Python 3.9 and lower.
- Use tox for local test runs and in CI.
Expand Down
24 changes: 16 additions & 8 deletions slugify/slugify.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@
DEFAULT_SEPARATOR = '-'


def _char_from_decimal_codepoint(match: re.Match[str]) -> str:
try:
return chr(int(match.group(1)))
except (OverflowError, ValueError):
return match.group(0)


def _char_from_hex_codepoint(match: re.Match[str]) -> str:
try:
return chr(int(match.group(1), 16))
except (OverflowError, ValueError):
return match.group(0)


def smart_truncate(
string: str,
max_length: int = 0,
Expand Down Expand Up @@ -134,17 +148,11 @@ def slugify(

# decimal character reference
if decimal:
try:
text = DECIMAL_PATTERN.sub(lambda m: chr(int(m.group(1))), text)
except Exception:
pass
text = DECIMAL_PATTERN.sub(_char_from_decimal_codepoint, text)

# hexadecimal character reference
if hexadecimal:
try:
text = HEX_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), text)
except Exception:
pass
text = HEX_PATTERN.sub(_char_from_hex_codepoint, text)

# re normalize text
if allow_unicode:
Expand Down
20 changes: 20 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ def test_html_decimal_on(self):
r = slugify(txt, decimal=True)
self.assertEqual(r, 'z')

def test_html_decimal_mixed_valid_invalid(self):
txt = 'Ž �'
r = slugify(txt, decimal=True)
self.assertEqual(r, 'z-99999999')

def test_html_decimal_off(self):
txt = 'Ž'
r = slugify(txt, entities=False, decimal=False)
Expand All @@ -176,6 +181,11 @@ def test_html_hexadecimal_on(self):
r = slugify(txt, hexadecimal=True)
self.assertEqual(r, 'z')

def test_html_hexadecimal_mixed_valid_invalid(self):
txt = 'Ž �'
r = slugify(txt, hexadecimal=True)
self.assertEqual(r, 'z-x110000')

def test_html_hexadecimal_off(self):
txt = 'Ž'
r = slugify(txt, hexadecimal=False)
Expand Down Expand Up @@ -418,6 +428,11 @@ def test_html_decimal_on(self):
r = slugify(txt, allow_unicode=True, decimal=True)
self.assertEqual(r, 'ž')

def test_html_decimal_mixed_valid_invalid(self):
txt = 'Ž �'
r = slugify(txt, allow_unicode=True, decimal=True)
self.assertEqual(r, 'ž-99999999')

def test_html_decimal_off(self):
txt = 'Ž'
r = slugify(txt, allow_unicode=True, entities=False, decimal=False)
Expand All @@ -428,6 +443,11 @@ def test_html_hexadecimal_on(self):
r = slugify(txt, allow_unicode=True, hexadecimal=True)
self.assertEqual(r, 'ž')

def test_html_hexadecimal_mixed_valid_invalid(self):
txt = 'Ž �'
r = slugify(txt, allow_unicode=True, hexadecimal=True)
self.assertEqual(r, 'ž-x110000')

def test_html_hexadecimal_off(self):
txt = 'Ž'
r = slugify(txt, allow_unicode=True, hexadecimal=False)
Expand Down