Skip to content

Commit

Permalink
feat: Allow compatibility with other ShortUUID libraries (#104) (#107)
Browse files Browse the repository at this point in the history
* refactor: Add should_sort arg to set_alphabet

* feat: add should_sort kwarg to Django Field

* refactor: changes should_sort kwarg name and default value

* docs: Update README.md
  • Loading branch information
Thiagomrfs authored Dec 10, 2024
1 parent a031e79 commit 6843c12
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 5 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ consistency:
'0123abcdefgh'
```

You can prevent the alphabet from being sorted by passing the `dont_sort_alphabet`
keyword argument to `set_alphabet()`. This option ensures compatibility with different
implementations of ShortUUID:

```python
>>> shortuuid.set_alphabet("aaaaabcdefgh1230123", dont_sort_alphabet=True)
>>> shortuuid.get_alphabet()
'abcdefgh1230'
```

If the default 22 digits are too long for you, you can get shorter IDs by just
truncating the string to the desired length. The IDs won't be universally unique any
longer, but the probability of a collision will still be very low.
Expand Down Expand Up @@ -168,6 +178,7 @@ class MyModel(models.Model):
max_length=40,
prefix="id_",
alphabet="abcdefg1234",
dont_sort_alphabet=False
primary_key=True,
)

Expand Down
3 changes: 2 additions & 1 deletion shortuuid/django_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ShortUUIDField(models.CharField):
def __init__(self, *args: Any, **kwargs: Any) -> None:
self.length: int = kwargs.pop("length", 22) # type: ignore
self.prefix: str = kwargs.pop("prefix", "") # type: ignore
self.dont_sort_alphabet: bool = kwargs.pop("dont_sort_alphabet", False) # type: ignore

if "max_length" not in kwargs:
# If `max_length` was not specified, set it here.
Expand All @@ -26,7 +27,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:

def _generate_uuid(self) -> str:
"""Generate a short random string."""
return self.prefix + ShortUUID(alphabet=self.alphabet).random(
return self.prefix + ShortUUID(alphabet=self.alphabet, dont_sort_alphabet=self.dont_sort_alphabet).random(
length=self.length
)

Expand Down
8 changes: 4 additions & 4 deletions shortuuid/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ def string_to_int(string: str, alphabet: List[str]) -> int:


class ShortUUID(object):
def __init__(self, alphabet: Optional[str] = None) -> None:
def __init__(self, alphabet: Optional[str] = None, dont_sort_alphabet: Optional[bool] = False) -> None:
if alphabet is None:
alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" "abcdefghijkmnopqrstuvwxyz"

self.set_alphabet(alphabet)
self.set_alphabet(alphabet, dont_sort_alphabet=dont_sort_alphabet)

@property
def _length(self) -> int:
Expand Down Expand Up @@ -110,11 +110,11 @@ def get_alphabet(self) -> str:
"""Return the current alphabet used for new UUIDs."""
return "".join(self._alphabet)

def set_alphabet(self, alphabet: str) -> None:
def set_alphabet(self, alphabet: str, dont_sort_alphabet: bool=False) -> None:
"""Set the alphabet to be used for new UUIDs."""
# Turn the alphabet into a set and sort it to prevent duplicates
# and ensure reproducibility.
new_alphabet = list(sorted(set(alphabet)))
new_alphabet = list(dict.fromkeys(alphabet)) if dont_sort_alphabet else list(sorted(set(alphabet)))
if len(new_alphabet) > 1:
self._alphabet = new_alphabet
self._alpha_len = len(self._alphabet)
Expand Down
23 changes: 23 additions & 0 deletions shortuuid/test_shortuuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,29 @@ def test_alphabet(self):
self.assertRaises(ValueError, su1.set_alphabet, "1")
self.assertRaises(ValueError, su1.set_alphabet, "1111111")

def test_unsorted_alphabet(self):
alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

su1 = ShortUUID(alphabet, dont_sort_alphabet=True)
su2 = ShortUUID()

self.assertEqual(alphabet, su1.get_alphabet())

su2.set_alphabet(alphabet, dont_sort_alphabet=True)
self.assertEqual(alphabet, su2.get_alphabet())

su2.set_alphabet(alphabet + "123abc", dont_sort_alphabet=True)
self.assertEqual(alphabet, su2.get_alphabet())

u = uuid4()
self.assertEqual(u, su1.decode(su1.encode(u)))

u = su1.uuid()
self.assertEqual(u, su1.encode(su1.decode(u)))

self.assertRaises(ValueError, su1.set_alphabet, "1")
self.assertRaises(ValueError, su1.set_alphabet, "1111111")

def test_encoded_length(self):
su1 = ShortUUID()
self.assertEqual(su1.encoded_length(), 22)
Expand Down

0 comments on commit 6843c12

Please sign in to comment.