We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
UTF8 characters on version slugging -- or slugging in general
This ticket came up as part of #1407. We should make sure version slugging is handling UTF8 characters in a sane way.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `readthedocs/builds/version_slug.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 """
4 Contains logic for handling version slugs.
5 
6 Handling slugs for versions is not too straightforward. We need to allow some
7 characters which are uncommon in usual slugs. They are dots and underscores.
8 Usually we want the slug to be the name of the tag or branch corresponding VCS
9 version. However we need to strip url-destroying characters like slashes.
10 
11 So the syntax for version slugs should be:
12 
13 * Start with a lowercase ascii char or a digit.
14 * All other characters must be lowercase ascii chars, digits or dots.
15 
16 If uniqueness is not met for a slug in a project, we append a dash and a letter
17 starting with ``a``. We keep increasing that letter until we have a unique
18 slug. This is used since using numbers in tags is too common and appending
19 another number would be confusing.
20 """
21 
22 import math
23 import re
24 import string
25 from operator import truediv
26 
27 from django.db import models
28 from django.utils.encoding import force_text
29 
30 
31 def get_fields_with_model(cls):
32     """
33     Replace deprecated function of the same name in Model._meta.
34 
35     This replaces deprecated function (as of Django 1.10) in Model._meta as
36     prescrived in the Django docs.
37     https://docs.djangoproject.com/en/1.11/ref/models/meta/#migrating-from-the-old-api
38     """
39     return [(f, f.model if f.model != cls else None)
40             for f in cls._meta.get_fields()
41             if not f.is_relation or f.one_to_one or
42             (f.many_to_one and f.related_model)]
43 
44 
45 # Regex breakdown:
46 #   [a-z0-9] -- start with alphanumeric value
47 #   [-._a-z0-9] -- allow dash, dot, underscore, digit, lowercase ascii
48 #   *? -- allow multiple of those, but be not greedy about the matching
49 #   (?: ... ) -- wrap everything so that the pattern cannot escape when used in
50 #                regexes.
51 VERSION_SLUG_REGEX = '(?:[a-z0-9A-Z][-._a-z0-9A-Z]*?)'
52 
53 
54 class VersionSlugField(models.CharField):
55 
56     """Inspired by ``django_extensions.db.fields.AutoSlugField``."""
57 
58     invalid_chars_re = re.compile('[^-._a-z0-9]')
59     leading_punctuation_re = re.compile('^[-._]+')
60     placeholder = '-'
61     fallback_slug = 'unknown'
62     test_pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX))
63 
64     def __init__(self, *args, **kwargs):
65         kwargs.setdefault('db_index', True)
66 
67         populate_from = kwargs.pop('populate_from', None)
68         if populate_from is None:
69             raise ValueError("missing 'populate_from' argument")
70         else:
71             self._populate_from = populate_from
72         super().__init__(*args, **kwargs)
73 
74     def get_queryset(self, model_cls, slug_field):
75         # pylint: disable=protected-access
76         for field, model in get_fields_with_model(model_cls):
77             if model and field == slug_field:
78                 return model._default_manager.all()
79         return model_cls._default_manager.all()
80 
81     def slugify(self, content):
82         if not content:
83             return ''
84 
85         slugified = content.lower()
86         slugified = self.invalid_chars_re.sub(self.placeholder, slugified)
87         slugified = self.leading_punctuation_re.sub('', slugified)
88 
89         if not slugified:
90             return self.fallback_slug
91         return slugified
92 
93     def uniquifying_suffix(self, iteration):
94         """
95         Create a unique suffix.
96 
97         This creates a suffix based on the number given as ``iteration``. It
98         will return a value encoded as lowercase ascii letter. So we have an
99         alphabet of 26 letters. The returned suffix will be for example ``_yh``
100         where ``yh`` is the encoding of ``iteration``. The length of it will be
101         ``math.log(iteration, 26)``.
102 
103         Examples::
104 
105             uniquifying_suffix(0) == '_a'
106             uniquifying_suffix(25) == '_z'
107             uniquifying_suffix(26) == '_ba'
108             uniquifying_suffix(52) == '_ca'
109         """
110         alphabet = string.ascii_lowercase
111         length = len(alphabet)
112         if iteration == 0:
113             power = 0
114         else:
115             power = int(math.log(iteration, length))
116         current = iteration
117         suffix = ''
118         for exp in reversed(list(range(0, power + 1))):
119             digit = int(truediv(current, length ** exp))
120             suffix += alphabet[digit]
121             current = current % length ** exp
122         return '_{suffix}'.format(suffix=suffix)
123 
124     def create_slug(self, model_instance):
125         """Generate a unique slug for a model instance."""
126         # pylint: disable=protected-access
127 
128         # get fields to populate from and slug field to set
129         slug_field = model_instance._meta.get_field(self.attname)
130 
131         slug = self.slugify(getattr(model_instance, self._populate_from))
132         count = 0
133 
134         # strip slug depending on max_length attribute of the slug field
135         # and clean-up
136         slug_len = slug_field.max_length
137         if slug_len:
138             slug = slug[:slug_len]
139         original_slug = slug
140 
141         # exclude the current model instance from the queryset used in finding
142         # the next valid slug
143         queryset = self.get_queryset(model_instance.__class__, slug_field)
144         if model_instance.pk:
145             queryset = queryset.exclude(pk=model_instance.pk)
146 
147         # form a kwarg dict used to implement any unique_together constraints
148         kwargs = {}
149         for params in model_instance._meta.unique_together:
150             if self.attname in params:
151                 for param in params:
152                     kwargs[param] = getattr(model_instance, param, None)
153         kwargs[self.attname] = slug
154 
155         # increases the number while searching for the next valid slug
156         # depending on the given slug, clean-up
157         while not slug or queryset.filter(**kwargs).exists():
158             slug = original_slug
159             end = self.uniquifying_suffix(count)
160             end_len = len(end)
161             if slug_len and len(slug) + end_len > slug_len:
162                 slug = slug[:slug_len - end_len]
163             slug = slug + end
164             kwargs[self.attname] = slug
165             count += 1
166 
167         assert self.test_pattern.match(slug), (
168             'Invalid generated slug: {slug}'.format(slug=slug)
169         )
170         return slug
171 
172     def pre_save(self, model_instance, add):
173         value = getattr(model_instance, self.attname)
174         # We only create a new slug if none was set yet.
175         if not value and add:
176             value = force_text(self.create_slug(model_instance))
177             setattr(model_instance, self.attname, value)
178         return value
179 
180     def deconstruct(self):
181         name, path, args, kwargs = super().deconstruct()
182         kwargs['populate_from'] = self._populate_from
183         return name, path, args, kwargs
184
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/readthedocs/builds/version_slug.py b/readthedocs/builds/version_slug.py
--- a/readthedocs/builds/version_slug.py
+++ b/readthedocs/builds/version_slug.py
@@ -26,6 +26,7 @@
 
 from django.db import models
 from django.utils.encoding import force_text
+from slugify import slugify as unicode_slugify
 
 
 def get_fields_with_model(cls):
@@ -53,13 +54,15 @@
 
 class VersionSlugField(models.CharField):
 
-    """Inspired by ``django_extensions.db.fields.AutoSlugField``."""
+    """
+    Inspired by ``django_extensions.db.fields.AutoSlugField``.
 
-    invalid_chars_re = re.compile('[^-._a-z0-9]')
-    leading_punctuation_re = re.compile('^[-._]+')
-    placeholder = '-'
-    fallback_slug = 'unknown'
+    Uses ``unicode-slugify`` to generate the slug.
+    """
+
+    ok_chars = '-._'  # dash, dot, underscore
     test_pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX))
+    fallback_slug = 'unknown'
 
     def __init__(self, *args, **kwargs):
         kwargs.setdefault('db_index', True)
@@ -78,13 +81,42 @@
                 return model._default_manager.all()
         return model_cls._default_manager.all()
 
+    def _normalize(self, content):
+        """
+        Normalize some invalid characters (/, %, !, ?) to become a dash (``-``).
+
+        .. note::
+
+            We replace these characters to a dash to keep compatibility with the
+            old behavior and also because it makes this more readable.
+
+        For example, ``release/1.0`` will become ``release-1.0``.
+        """
+        return re.sub('[/%!?]', '-', content)
+
     def slugify(self, content):
+        """
+        Make ``content`` a valid slug.
+
+        It uses ``unicode-slugify`` behind the scenes which works properly with
+        Unicode characters.
+        """
         if not content:
             return ''
 
-        slugified = content.lower()
-        slugified = self.invalid_chars_re.sub(self.placeholder, slugified)
-        slugified = self.leading_punctuation_re.sub('', slugified)
+        normalized = self._normalize(content)
+        slugified = unicode_slugify(
+            normalized,
+            only_ascii=True,
+            spaces=False,
+            lower=True,
+            ok=self.ok_chars,
+            space_replacement='-',
+        )
+
+        # Remove first character wile it's an invalid character for the
+        # beginning of the slug
+        slugified = slugified.lstrip(self.ok_chars)
 
         if not slugified:
             return self.fallback_slug

{"golden_diff": "diff --git a/readthedocs/builds/version_slug.py b/readthedocs/builds/version_slug.py\n--- a/readthedocs/builds/version_slug.py\n+++ b/readthedocs/builds/version_slug.py\n@@ -26,6 +26,7 @@\n \n from django.db import models\n from django.utils.encoding import force_text\n+from slugify import slugify as unicode_slugify\n \n \n def get_fields_with_model(cls):\n@@ -53,13 +54,15 @@\n \n class VersionSlugField(models.CharField):\n \n-    \"\"\"Inspired by ``django_extensions.db.fields.AutoSlugField``.\"\"\"\n+    \"\"\"\n+    Inspired by ``django_extensions.db.fields.AutoSlugField``.\n \n-    invalid_chars_re = re.compile('[^-._a-z0-9]')\n-    leading_punctuation_re = re.compile('^[-._]+')\n-    placeholder = '-'\n-    fallback_slug = 'unknown'\n+    Uses ``unicode-slugify`` to generate the slug.\n+    \"\"\"\n+\n+    ok_chars = '-._'  # dash, dot, underscore\n     test_pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX))\n+    fallback_slug = 'unknown'\n \n     def __init__(self, *args, **kwargs):\n         kwargs.setdefault('db_index', True)\n@@ -78,13 +81,42 @@\n                 return model._default_manager.all()\n         return model_cls._default_manager.all()\n \n+    def _normalize(self, content):\n+        \"\"\"\n+        Normalize some invalid characters (/, %, !, ?) to become a dash (``-``).\n+\n+        .. note::\n+\n+            We replace these characters to a dash to keep compatibility with the\n+            old behavior and also because it makes this more readable.\n+\n+        For example, ``release/1.0`` will become ``release-1.0``.\n+        \"\"\"\n+        return re.sub('[/%!?]', '-', content)\n+\n     def slugify(self, content):\n+        \"\"\"\n+        Make ``content`` a valid slug.\n+\n+        It uses ``unicode-slugify`` behind the scenes which works properly with\n+        Unicode characters.\n+        \"\"\"\n         if not content:\n             return ''\n \n-        slugified = content.lower()\n-        slugified = self.invalid_chars_re.sub(self.placeholder, slugified)\n-        slugified = self.leading_punctuation_re.sub('', slugified)\n+        normalized = self._normalize(content)\n+        slugified = unicode_slugify(\n+            normalized,\n+            only_ascii=True,\n+            spaces=False,\n+            lower=True,\n+            ok=self.ok_chars,\n+            space_replacement='-',\n+        )\n+\n+        # Remove first character wile it's an invalid character for the\n+        # beginning of the slug\n+        slugified = slugified.lstrip(self.ok_chars)\n \n         if not slugified:\n             return self.fallback_slug\n", "issue": "UTF8 characters on version slugging -- or slugging in general\nThis ticket came up as part of #1407. We should make sure version slugging is handling UTF8 characters in a sane way.\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nContains logic for handling version slugs.\n\nHandling slugs for versions is not too straightforward. We need to allow some\ncharacters which are uncommon in usual slugs. They are dots and underscores.\nUsually we want the slug to be the name of the tag or branch corresponding VCS\nversion. However we need to strip url-destroying characters like slashes.\n\nSo the syntax for version slugs should be:\n\n* Start with a lowercase ascii char or a digit.\n* All other characters must be lowercase ascii chars, digits or dots.\n\nIf uniqueness is not met for a slug in a project, we append a dash and a letter\nstarting with ``a``. We keep increasing that letter until we have a unique\nslug. This is used since using numbers in tags is too common and appending\nanother number would be confusing.\n\"\"\"\n\nimport math\nimport re\nimport string\nfrom operator import truediv\n\nfrom django.db import models\nfrom django.utils.encoding import force_text\n\n\ndef get_fields_with_model(cls):\n    \"\"\"\n    Replace deprecated function of the same name in Model._meta.\n\n    This replaces deprecated function (as of Django 1.10) in Model._meta as\n    prescrived in the Django docs.\n    https://docs.djangoproject.com/en/1.11/ref/models/meta/#migrating-from-the-old-api\n    \"\"\"\n    return [(f, f.model if f.model != cls else None)\n            for f in cls._meta.get_fields()\n            if not f.is_relation or f.one_to_one or\n            (f.many_to_one and f.related_model)]\n\n\n# Regex breakdown:\n#   [a-z0-9] -- start with alphanumeric value\n#   [-._a-z0-9] -- allow dash, dot, underscore, digit, lowercase ascii\n#   *? -- allow multiple of those, but be not greedy about the matching\n#   (?: ... ) -- wrap everything so that the pattern cannot escape when used in\n#                regexes.\nVERSION_SLUG_REGEX = '(?:[a-z0-9A-Z][-._a-z0-9A-Z]*?)'\n\n\nclass VersionSlugField(models.CharField):\n\n    \"\"\"Inspired by ``django_extensions.db.fields.AutoSlugField``.\"\"\"\n\n    invalid_chars_re = re.compile('[^-._a-z0-9]')\n    leading_punctuation_re = re.compile('^[-._]+')\n    placeholder = '-'\n    fallback_slug = 'unknown'\n    test_pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX))\n\n    def __init__(self, *args, **kwargs):\n        kwargs.setdefault('db_index', True)\n\n        populate_from = kwargs.pop('populate_from', None)\n        if populate_from is None:\n            raise ValueError(\"missing 'populate_from' argument\")\n        else:\n            self._populate_from = populate_from\n        super().__init__(*args, **kwargs)\n\n    def get_queryset(self, model_cls, slug_field):\n        # pylint: disable=protected-access\n        for field, model in get_fields_with_model(model_cls):\n            if model and field == slug_field:\n                return model._default_manager.all()\n        return model_cls._default_manager.all()\n\n    def slugify(self, content):\n        if not content:\n            return ''\n\n        slugified = content.lower()\n        slugified = self.invalid_chars_re.sub(self.placeholder, slugified)\n        slugified = self.leading_punctuation_re.sub('', slugified)\n\n        if not slugified:\n            return self.fallback_slug\n        return slugified\n\n    def uniquifying_suffix(self, iteration):\n        \"\"\"\n        Create a unique suffix.\n\n        This creates a suffix based on the number given as ``iteration``. It\n        will return a value encoded as lowercase ascii letter. So we have an\n        alphabet of 26 letters. The returned suffix will be for example ``_yh``\n        where ``yh`` is the encoding of ``iteration``. The length of it will be\n        ``math.log(iteration, 26)``.\n\n        Examples::\n\n            uniquifying_suffix(0) == '_a'\n            uniquifying_suffix(25) == '_z'\n            uniquifying_suffix(26) == '_ba'\n            uniquifying_suffix(52) == '_ca'\n        \"\"\"\n        alphabet = string.ascii_lowercase\n        length = len(alphabet)\n        if iteration == 0:\n            power = 0\n        else:\n            power = int(math.log(iteration, length))\n        current = iteration\n        suffix = ''\n        for exp in reversed(list(range(0, power + 1))):\n            digit = int(truediv(current, length ** exp))\n            suffix += alphabet[digit]\n            current = current % length ** exp\n        return '_{suffix}'.format(suffix=suffix)\n\n    def create_slug(self, model_instance):\n        \"\"\"Generate a unique slug for a model instance.\"\"\"\n        # pylint: disable=protected-access\n\n        # get fields to populate from and slug field to set\n        slug_field = model_instance._meta.get_field(self.attname)\n\n        slug = self.slugify(getattr(model_instance, self._populate_from))\n        count = 0\n\n        # strip slug depending on max_length attribute of the slug field\n        # and clean-up\n        slug_len = slug_field.max_length\n        if slug_len:\n            slug = slug[:slug_len]\n        original_slug = slug\n\n        # exclude the current model instance from the queryset used in finding\n        # the next valid slug\n        queryset = self.get_queryset(model_instance.__class__, slug_field)\n        if model_instance.pk:\n            queryset = queryset.exclude(pk=model_instance.pk)\n\n        # form a kwarg dict used to implement any unique_together constraints\n        kwargs = {}\n        for params in model_instance._meta.unique_together:\n            if self.attname in params:\n                for param in params:\n                    kwargs[param] = getattr(model_instance, param, None)\n        kwargs[self.attname] = slug\n\n        # increases the number while searching for the next valid slug\n        # depending on the given slug, clean-up\n        while not slug or queryset.filter(**kwargs).exists():\n            slug = original_slug\n            end = self.uniquifying_suffix(count)\n            end_len = len(end)\n            if slug_len and len(slug) + end_len > slug_len:\n                slug = slug[:slug_len - end_len]\n            slug = slug + end\n            kwargs[self.attname] = slug\n            count += 1\n\n        assert self.test_pattern.match(slug), (\n            'Invalid generated slug: {slug}'.format(slug=slug)\n        )\n        return slug\n\n    def pre_save(self, model_instance, add):\n        value = getattr(model_instance, self.attname)\n        # We only create a new slug if none was set yet.\n        if not value and add:\n            value = force_text(self.create_slug(model_instance))\n            setattr(model_instance, self.attname, value)\n        return value\n\n    def deconstruct(self):\n        name, path, args, kwargs = super().deconstruct()\n        kwargs['populate_from'] = self._populate_from\n        return name, path, args, kwargs\n", "path": "readthedocs/builds/version_slug.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nContains logic for handling version slugs.\n\nHandling slugs for versions is not too straightforward. We need to allow some\ncharacters which are uncommon in usual slugs. They are dots and underscores.\nUsually we want the slug to be the name of the tag or branch corresponding VCS\nversion. However we need to strip url-destroying characters like slashes.\n\nSo the syntax for version slugs should be:\n\n* Start with a lowercase ascii char or a digit.\n* All other characters must be lowercase ascii chars, digits or dots.\n\nIf uniqueness is not met for a slug in a project, we append a dash and a letter\nstarting with ``a``. We keep increasing that letter until we have a unique\nslug. This is used since using numbers in tags is too common and appending\nanother number would be confusing.\n\"\"\"\n\nimport math\nimport re\nimport string\nfrom operator import truediv\n\nfrom django.db import models\nfrom django.utils.encoding import force_text\nfrom slugify import slugify as unicode_slugify\n\n\ndef get_fields_with_model(cls):\n    \"\"\"\n    Replace deprecated function of the same name in Model._meta.\n\n    This replaces deprecated function (as of Django 1.10) in Model._meta as\n    prescrived in the Django docs.\n    https://docs.djangoproject.com/en/1.11/ref/models/meta/#migrating-from-the-old-api\n    \"\"\"\n    return [(f, f.model if f.model != cls else None)\n            for f in cls._meta.get_fields()\n            if not f.is_relation or f.one_to_one or\n            (f.many_to_one and f.related_model)]\n\n\n# Regex breakdown:\n#   [a-z0-9] -- start with alphanumeric value\n#   [-._a-z0-9] -- allow dash, dot, underscore, digit, lowercase ascii\n#   *? -- allow multiple of those, but be not greedy about the matching\n#   (?: ... ) -- wrap everything so that the pattern cannot escape when used in\n#                regexes.\nVERSION_SLUG_REGEX = '(?:[a-z0-9A-Z][-._a-z0-9A-Z]*?)'\n\n\nclass VersionSlugField(models.CharField):\n\n    \"\"\"\n    Inspired by ``django_extensions.db.fields.AutoSlugField``.\n\n    Uses ``unicode-slugify`` to generate the slug.\n    \"\"\"\n\n    ok_chars = '-._'  # dash, dot, underscore\n    test_pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX))\n    fallback_slug = 'unknown'\n\n    def __init__(self, *args, **kwargs):\n        kwargs.setdefault('db_index', True)\n\n        populate_from = kwargs.pop('populate_from', None)\n        if populate_from is None:\n            raise ValueError(\"missing 'populate_from' argument\")\n        else:\n            self._populate_from = populate_from\n        super().__init__(*args, **kwargs)\n\n    def get_queryset(self, model_cls, slug_field):\n        # pylint: disable=protected-access\n        for field, model in get_fields_with_model(model_cls):\n            if model and field == slug_field:\n                return model._default_manager.all()\n        return model_cls._default_manager.all()\n\n    def _normalize(self, content):\n        \"\"\"\n        Normalize some invalid characters (/, %, !, ?) to become a dash (``-``).\n\n        .. note::\n\n            We replace these characters to a dash to keep compatibility with the\n            old behavior and also because it makes this more readable.\n\n        For example, ``release/1.0`` will become ``release-1.0``.\n        \"\"\"\n        return re.sub('[/%!?]', '-', content)\n\n    def slugify(self, content):\n        \"\"\"\n        Make ``content`` a valid slug.\n\n        It uses ``unicode-slugify`` behind the scenes which works properly with\n        Unicode characters.\n        \"\"\"\n        if not content:\n            return ''\n\n        normalized = self._normalize(content)\n        slugified = unicode_slugify(\n            normalized,\n            only_ascii=True,\n            spaces=False,\n            lower=True,\n            ok=self.ok_chars,\n            space_replacement='-',\n        )\n\n        # Remove first character wile it's an invalid character for the\n        # beginning of the slug\n        slugified = slugified.lstrip(self.ok_chars)\n\n        if not slugified:\n            return self.fallback_slug\n        return slugified\n\n    def uniquifying_suffix(self, iteration):\n        \"\"\"\n        Create a unique suffix.\n\n        This creates a suffix based on the number given as ``iteration``. It\n        will return a value encoded as lowercase ascii letter. So we have an\n        alphabet of 26 letters. The returned suffix will be for example ``_yh``\n        where ``yh`` is the encoding of ``iteration``. The length of it will be\n        ``math.log(iteration, 26)``.\n\n        Examples::\n\n            uniquifying_suffix(0) == '_a'\n            uniquifying_suffix(25) == '_z'\n            uniquifying_suffix(26) == '_ba'\n            uniquifying_suffix(52) == '_ca'\n        \"\"\"\n        alphabet = string.ascii_lowercase\n        length = len(alphabet)\n        if iteration == 0:\n            power = 0\n        else:\n            power = int(math.log(iteration, length))\n        current = iteration\n        suffix = ''\n        for exp in reversed(list(range(0, power + 1))):\n            digit = int(truediv(current, length ** exp))\n            suffix += alphabet[digit]\n            current = current % length ** exp\n        return '_{suffix}'.format(suffix=suffix)\n\n    def create_slug(self, model_instance):\n        \"\"\"Generate a unique slug for a model instance.\"\"\"\n        # pylint: disable=protected-access\n\n        # get fields to populate from and slug field to set\n        slug_field = model_instance._meta.get_field(self.attname)\n\n        slug = self.slugify(getattr(model_instance, self._populate_from))\n        count = 0\n\n        # strip slug depending on max_length attribute of the slug field\n        # and clean-up\n        slug_len = slug_field.max_length\n        if slug_len:\n            slug = slug[:slug_len]\n        original_slug = slug\n\n        # exclude the current model instance from the queryset used in finding\n        # the next valid slug\n        queryset = self.get_queryset(model_instance.__class__, slug_field)\n        if model_instance.pk:\n            queryset = queryset.exclude(pk=model_instance.pk)\n\n        # form a kwarg dict used to implement any unique_together constraints\n        kwargs = {}\n        for params in model_instance._meta.unique_together:\n            if self.attname in params:\n                for param in params:\n                    kwargs[param] = getattr(model_instance, param, None)\n        kwargs[self.attname] = slug\n\n        # increases the number while searching for the next valid slug\n        # depending on the given slug, clean-up\n        while not slug or queryset.filter(**kwargs).exists():\n            slug = original_slug\n            end = self.uniquifying_suffix(count)\n            end_len = len(end)\n            if slug_len and len(slug) + end_len > slug_len:\n                slug = slug[:slug_len - end_len]\n            slug = slug + end\n            kwargs[self.attname] = slug\n            count += 1\n\n        assert self.test_pattern.match(slug), (\n            'Invalid generated slug: {slug}'.format(slug=slug)\n        )\n        return slug\n\n    def pre_save(self, model_instance, add):\n        value = getattr(model_instance, self.attname)\n        # We only create a new slug if none was set yet.\n        if not value and add:\n            value = force_text(self.create_slug(model_instance))\n            setattr(model_instance, self.attname, value)\n        return value\n\n    def deconstruct(self):\n        name, path, args, kwargs = super().deconstruct()\n        kwargs['populate_from'] = self._populate_from\n        return name, path, args, kwargs\n", "path": "readthedocs/builds/version_slug.py"}]}