Repository: omaciel/fauxfactory

Issue:
[Proposal] some extra param to avoid string formatting
While using some string category method user like to matriculate string; they want string `start` with something. Its most common use-case; which, I observed while using `fauxfactory` in daily life. It will reduce lots of indirect string formatting.
```
def gen_alphanumeric(length=10, start=None):
    pass
```

small example:
```
In [1]: from fauxfactory import gen_alpha, gen_alphanumeric                                                                                       

In [2]: gen_alpha(start="volume")                                                                                                                 
Out[2]: 'volume_DGDvJNxZmO'

In [3]: gen_alpha(3, start="og")                                                                                                                  
Out[3]: 'og_OpD'

In [4]: gen_alphanumeric(start="vol")                                                                                                             
Out[4]: 'vol_DJBGf7Af2L'

In [5]: gen_alphanumeric(5, start="vol")                                                                                                          
Out[5]: 'vol_3pmw0'
```

This is basic example. we can go for more enhancement, we can even take care of `string length`.

Comments:

Patch:
diff --git a/fauxfactory/factories/strings.py b/fauxfactory/factories/strings.py
index 5e38b52..67f6e50 100644
--- a/fauxfactory/factories/strings.py
+++ b/fauxfactory/factories/strings.py
@@ -68,28 +68,35 @@ def gen_string(str_type, length=None, validator=None, default=None, tries=10):
 
 @check_len
 @check_validation
-def gen_alpha(length=10):
+def gen_alpha(length=10, start=None, separator=''):
     """Return a random string made up of alpha characters.
 
     :param int length: Length for random data.
+    :param str start: Random data start with.
+    :param char separator: Separator for start and random data.
     :returns: A random string made up of alpha characters.
     :rtype: str
 
     """
     random.seed()
     output_string = ''.join(
-        random.choice(string.ascii_letters) for i in range(length)
+        random.choice(string.ascii_letters) for _ in range(length)
     )
 
+    if start:
+        output_string = '{0}{1}{2}'.format(
+            start, separator, output_string)[0:length]
     return output_string
 
 
 @check_len
 @check_validation
-def gen_alphanumeric(length=10):
+def gen_alphanumeric(length=10, start=None, separator=''):
     """Return a random string made up of alpha and numeric characters.
 
     :param int length: Length for random data.
+    :param str start: Random data start with.
+    :param char separator: Separator for start and random data.
     :returns: A random string made up of alpha and numeric characters.
     :rtype: str
 
@@ -98,19 +105,24 @@ def gen_alphanumeric(length=10):
     output_string = ''.join(
         random.choice(
             string.ascii_letters + string.digits
-        ) for i in range(length))
+        ) for _ in range(length))
 
+    if start:
+        output_string = '{0}{1}{2}'.format(
+            start, separator, output_string)[0:length]
     return output_string
 
 
 @check_len
 @check_validation
-def gen_cjk(length=10):
+def gen_cjk(length=10, start=None, separator=''):
     """Return a random string made up of CJK characters.
 
     (Source: Wikipedia - CJK Unified Ideographs)
 
     :param int length: Length for random data.
+    :param str start: Random data start with.
+    :param char separator: Separator for start and random data.
     :returns: A random string made up of CJK characters.
     :rtype: str
 
@@ -120,15 +132,22 @@ def gen_cjk(length=10):
     # Generate codepoints, then convert the codepoints to a string. The
     # valid range of CJK codepoints is 0x4E00 - 0x9FCC, inclusive.
     codepoints = [random.randint(0x4E00, 0x9FCC) for _ in range(length)]
-    return ''.join(chr(codepoint) for codepoint in codepoints)
+    output_string = ''.join(chr(codepoint) for codepoint in codepoints)
+
+    if start:
+        output_string = '{0}{1}{2}'.format(
+            start, separator, output_string)[0:length]
+    return output_string
 
 
 @check_len
 @check_validation
-def gen_cyrillic(length=10):
+def gen_cyrillic(length=10, start=None, separator=''):
     """Return a random string made up of Cyrillic characters.
 
     :param int length: Length for random data.
+    :param str start: Random data start with.
+    :param char separator: Separator for start and random data.
     :returns: A random string made up of Cyrillic characters.
     :rtype: str
 
@@ -136,9 +155,14 @@ def gen_cyrillic(length=10):
     random.seed()
 
     # Generate codepoints, then convert the codepoints to a string. The
-    # valid range of Cyrillic codepoints is 0x410 - 0x4ff, inclusive.
+    # valid range of Cyrillic codepoints is 0x0400 - 0x04FF, inclusive.
     codepoints = [random.randint(0x0400, 0x04FF) for _ in range(length)]
-    return ''.join(chr(codepoint) for codepoint in codepoints)
+    output_string = ''.join(chr(codepoint) for codepoint in codepoints)
+
+    if start:
+        output_string = '{0}{1}{2}'.format(
+            start, separator, output_string)[0:length]
+    return output_string
 
 
 @check_len
@@ -236,12 +260,14 @@ def gen_iplum(words=None, paragraphs=None):
 
 @check_len
 @check_validation
-def gen_latin1(length=10):
+def gen_latin1(length=10, start=None, separator=''):
     """Return a random string made up of UTF-8 characters.
 
     (Font: Wikipedia - Latin-1 Supplement Unicode Block)
 
     :param int length: Length for random data.
+    :param str start: Random data start with.
+    :param char separator: Separator for start and random data.
     :returns: A random string made up of ``Latin1`` characters.
     :rtype: str
 
@@ -264,31 +290,45 @@ def gen_latin1(length=10):
         chr(random.choice(output_array)) for _ in range(length)
     )
 
+    if start:
+        output_string = '{0}{1}{2}'.format(
+            start, separator, output_string)[0:length]
     return output_string
 
 
 @check_len
 @check_validation
-def gen_numeric_string(length=10):
+def gen_numeric_string(length=10, start=None, separator=''):
     """Return a random string made up of numbers.
 
     :param int length: Length for random data.
+    :param str start: Random data start with.
+    :param char separator: Separator for start and random data.
     :returns: A random string made up of numbers.
     :rtype: str
 
     """
     random.seed()
-    return ''.join(random.choice(string.digits) for _ in range(length))
+    output_string = ''.join(
+        random.choice(string.digits) for _ in range(length)
+    )
+
+    if start:
+        output_string = '{0}{1}{2}'.format(
+            start, separator, output_string)[0:length]
+    return output_string
 
 
 @check_len
 @check_validation
-def gen_utf8(length=10, smp=True):
+def gen_utf8(length=10, smp=True, start=None, separator=''):
     """Return a random string made up of UTF-8 letters characters.
 
     Follows `RFC 3629`_.
 
     :param int length: Length for random data.
+    :param str start: Random data start with.
+    :param char separator: Separator for start and random data.
     :param bool smp: Include Supplementary Multilingual Plane (SMP)
         characters
     :returns: A random string made up of ``UTF-8`` letters characters.
@@ -299,17 +339,33 @@ def gen_utf8(length=10, smp=True):
     """
     UNICODE_LETTERS = [c for c in unicode_letters_generator(smp)]
     random.seed()
-    return ''.join([random.choice(UNICODE_LETTERS) for _ in range(length)])
+    output_string = ''.join(
+        [random.choice(UNICODE_LETTERS) for _ in range(length)]
+    )
+
+    if start:
+        output_string = '{0}{1}{2}'.format(
+            start, separator, output_string)[0:length]
+    return output_string
 
 
 @check_len
 @check_validation
-def gen_special(length=10):
+def gen_special(length=10, start=None, separator=''):
     """Return a random special characters string.
 
     :param int length: Length for random data.
+    :param str start: Random data start with.
+    :param char separator: Separator for start and random data.
     :returns: A random string made up of special characters.
     :rtype: str
     """
     random.seed()
-    return ''.join(random.choice(string.punctuation) for _ in range(length))
+    output_string = ''.join(
+        random.choice(string.punctuation) for _ in range(length)
+    )
+
+    if start:
+        output_string = '{0}{1}{2}'.format(
+            start, separator, output_string)[0:length]
+    return output_string


Test patch:
diff --git a/tests/test_strings.py b/tests/test_strings.py
index 7b977a3..2095fe4 100644
--- a/tests/test_strings.py
+++ b/tests/test_strings.py
@@ -1,6 +1,7 @@
 """Tests for all string generators."""
 import string
 import unicodedata
+from random import randint
 
 import pytest
 
@@ -150,3 +151,14 @@ def test_special_string():
     special_str = gen_special()
     for char in special_str:
         assert char in VALID_CHARS
+
+
+@pytest.mark.parametrize('fnc', GENERATORS[1:])
+def test_start_string(fnc):
+    """"String generated has start with specific keyword."""
+    start = fnc(randint(1, 5))
+    separator = fnc(1)
+    random_str = fnc(start=start, separator=separator)
+    assert start == random_str[0:len(start)]
+    assert separator == random_str[len(start)]
+    assert len(random_str) == 10