-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(utils): generate random identifiers (#85)
- Loading branch information
Showing
3 changed files
with
112 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = '0.5.0' | ||
__version__ = '0.6.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
"""Miscellaneous utility functions.""" | ||
|
||
from random import choice | ||
import string | ||
|
||
|
||
def generate_id( | ||
charset: str = ''.join([string.ascii_letters, string.digits]), | ||
length: int = 6 | ||
) -> str: | ||
"""Generate random string based on allowed set of characters. | ||
Args: | ||
charset: A string of allowed characters or an expression evaluating to | ||
a string of allowed characters. | ||
length: Length of returned string. | ||
Returns: | ||
Random string of specified length and composed of defined set of | ||
allowed characters. | ||
Raises: | ||
TypeError: Raised if 'charset' cannot be evaluated to a string or if | ||
'length' is not a positive integer. | ||
""" | ||
try: | ||
charset = eval(charset) | ||
except (NameError, SyntaxError): | ||
pass | ||
except Exception as e: | ||
raise TypeError(f"Could not evaluate 'charset': {charset}") from e | ||
if not isinstance(charset, str) or charset == "": | ||
raise TypeError( | ||
f"Could not evaluate 'charset' to non-empty string: {charset}" | ||
) | ||
if not isinstance(length, int) or not length > 0: | ||
raise TypeError( | ||
f"Argument to 'length' is not a positive integer: {length}" | ||
) | ||
charset = ''.join(sorted(set(charset))) | ||
return ''.join(choice(charset) for __ in range(length)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
"""Tests for miscellaneous utility functions.""" | ||
|
||
import string | ||
|
||
import pytest | ||
|
||
from foca.utils.misc import generate_id | ||
|
||
|
||
class TestGenerateId: | ||
|
||
def test_default(self): | ||
"""Use only default arguments.""" | ||
res = generate_id() | ||
assert isinstance(res, str) | ||
|
||
def test_charset_literal_string(self): | ||
"""Argument to `charset` is non-default literal string.""" | ||
charset = string.digits | ||
res = generate_id(charset=charset) | ||
assert set(res) <= set(string.digits) | ||
|
||
def test_charset_literal_string_duplicates(self): | ||
"""Argument to `charset` is non-default literal string with duplicates. | ||
""" | ||
charset = string.digits + string.digits | ||
res = generate_id(charset=charset) | ||
assert set(res) <= set(string.digits) | ||
|
||
def test_charset_evaluates_to_string(self): | ||
"""Argument to `charset` evaluates to string.""" | ||
charset = "''.join([c for c in string.digits])" | ||
res = generate_id(charset=charset) | ||
assert set(res) <= set(string.digits) | ||
|
||
def test_charset_evaluates_to_empty_string(self): | ||
"""Argument to `charset` evaluates to non-string.""" | ||
charset = "''.join([])" | ||
with pytest.raises(TypeError): | ||
generate_id(charset=charset) | ||
|
||
def test_charset_evaluates_to_non_string(self): | ||
"""Argument to `charset` evaluates to non-string.""" | ||
charset = "1" | ||
with pytest.raises(TypeError): | ||
generate_id(charset=charset) | ||
|
||
def test_evaluation_error(self): | ||
"""Evaulation of `length` raises an exception.""" | ||
charset = int | ||
with pytest.raises(TypeError): | ||
generate_id(charset=charset) # type: ignore | ||
|
||
def test_length(self): | ||
"""Non-default argument to `length`.""" | ||
length = 1 | ||
res = generate_id(length=length) | ||
assert len(res) == length | ||
|
||
def test_length_not_int(self): | ||
"""Argument to `length` is not an integer.""" | ||
length = "" | ||
with pytest.raises(TypeError): | ||
generate_id(length=length) # type: ignore | ||
|
||
def test_length_not_positive(self): | ||
"""Argument to `length` is not a positive integer.""" | ||
length = -1 | ||
with pytest.raises(TypeError): | ||
generate_id(length=length) |