Skip to content

Commit 8991505

Browse files
committed
Use unicode-slugify to generate Version.slug
1 parent 05cc76b commit 8991505

File tree

3 files changed

+51
-8
lines changed

3 files changed

+51
-8
lines changed

readthedocs/builds/version_slug.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
from django.db import models
2828
from django.utils.encoding import force_text
2929

30+
from slugify import slugify
31+
3032

3133
def get_fields_with_model(cls):
3234
"""
@@ -53,13 +55,15 @@ def get_fields_with_model(cls):
5355

5456
class VersionSlugField(models.CharField):
5557

56-
"""Inspired by ``django_extensions.db.fields.AutoSlugField``."""
58+
"""
59+
Inspired by ``django_extensions.db.fields.AutoSlugField``.
5760
58-
invalid_chars_re = re.compile('[^-._a-z0-9]')
59-
leading_punctuation_re = re.compile('^[-._]+')
60-
placeholder = '-'
61-
fallback_slug = 'unknown'
61+
Uses ``unicode-slugify`` to generate the slug.
62+
"""
63+
64+
ok_chars = '-._' # dash, dot, underscore
6265
test_pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX))
66+
fallback_slug = 'unknown'
6367

6468
def __init__(self, *args, **kwargs):
6569
kwargs.setdefault('db_index', True)
@@ -78,13 +82,37 @@ def get_queryset(self, model_cls, slug_field):
7882
return model._default_manager.all()
7983
return model_cls._default_manager.all()
8084

85+
def _normalize(self, content):
86+
"""
87+
Normalize some invalid characters to become a dash (``-``).
88+
89+
For example, ``release/1.0`` will become ``release-1.0``.
90+
"""
91+
return re.sub('[/%!?]', '-', content)
92+
8193
def slugify(self, content):
94+
"""
95+
Make ``content`` a valid slug.
96+
97+
It uses ``unicode-slugify`` behind the scenes which works properly with
98+
Unicode characters.
99+
"""
82100
if not content:
83101
return ''
84102

85-
slugified = content.lower()
86-
slugified = self.invalid_chars_re.sub(self.placeholder, slugified)
87-
slugified = self.leading_punctuation_re.sub('', slugified)
103+
normalized = self._normalize(content)
104+
slugified = slugify(
105+
normalized,
106+
only_ascii=True,
107+
spaces=False,
108+
lower=True,
109+
ok=self.ok_chars,
110+
space_replacement='-',
111+
)
112+
113+
# Remove first character wile it's an invalid character for the
114+
# beginning of the slug
115+
slugified = slugified.lstrip(self.ok_chars)
88116

89117
if not slugified:
90118
return self.fallback_slug

readthedocs/rtd_tests/tests/test_version_slug.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,15 @@ def test_uniquifying_suffix(self):
105105
self.assertEqual(field.uniquifying_suffix(25), '_z')
106106
self.assertEqual(field.uniquifying_suffix(26), '_ba')
107107
self.assertEqual(field.uniquifying_suffix(52), '_ca')
108+
109+
def test_unicode(self):
110+
version = Version.objects.create(
111+
verbose_name='camión',
112+
project=self.pip,
113+
)
114+
self.assertEqual(version.slug, 'camion')
115+
version = Version.objects.create(
116+
verbose_name='ŭñíč°də-branch',
117+
project=self.pip,
118+
)
119+
self.assertEqual(version.slug, 'unicd-branch')

requirements/pip.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ django-kombu==0.9.4
7070
mock==2.0.0
7171
stripe==2.18.0
7272

73+
# unicode-slugify==0.1.5 is not released on PyPI yet
74+
git+https://github.com/mozilla/unicode-slugify@b696c37#egg=unicode-slugify
75+
7376
django-formtools==2.1
7477

7578
# docker is pinned to 3.1.3 because we found some strange behavior

0 commit comments

Comments
 (0)