codecov[bot]: # [Codecov](https://codecov.io/gh/omaciel/fauxfactory/pull/103?src=pr&el=h1) Report
> Merging [#103](https://codecov.io/gh/omaciel/fauxfactory/pull/103?src=pr&el=desc) into [master](https://codecov.io/gh/omaciel/fauxfactory/commit/df28434be0baed3e68e9c26986ff67f78b66d5b0?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/omaciel/fauxfactory/pull/103/graphs/tree.svg?width=650&token=jlm3hYbTbH&height=150&src=pr)](https://codecov.io/gh/omaciel/fauxfactory/pull/103?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master   #103   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           9      9           
  Lines         455    476   +21     
=====================================
+ Hits          455    476   +21
```


| [Impacted Files](https://codecov.io/gh/omaciel/fauxfactory/pull/103?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [fauxfactory/factories/strings.py](https://codecov.io/gh/omaciel/fauxfactory/pull/103/diff?src=pr&el=tree#diff-ZmF1eGZhY3RvcnkvZmFjdG9yaWVzL3N0cmluZ3MucHk=) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/omaciel/fauxfactory/pull/103?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/omaciel/fauxfactory/pull/103?src=pr&el=footer). Last update [df28434...92d8a93](https://codecov.io/gh/omaciel/fauxfactory/pull/103?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

omaciel: Interesting... at first I interpreted the requirement as being `Given a start parameter, string generating methods should yield a string that starts with a string representation of said parameter, which should be counted toward the final length of the final string.`

For example:

```
In [1]: gen_alpha(start="volume") # defaults to 10 characters                                                                                                                 
Out[1]: 'volume_DGD' # 10 total chars
```

So is it standard to assume that given a method that offers both `length` and `start`, that the final string will be of length equals to the sum of `length + start` and an additional separator character? Should then `separator` be an optional parameter that defaults to `_`?

digitronik: > Interesting... at first I interpreted the requirement as being `Given a start parameter, string generating methods should yield a string that starts with a string representation of said parameter, which should be counted toward the final length of the final string.`

I agreed with this :+1: . We should to take care of final `random data` string `length`.
> 
> For example:
> 
> ```
> In [1]: gen_alpha(start="volume") # defaults to 10 characters                                                                                                                 
> Out[1]: 'volume_DGD' # 10 total chars
> ```
> So is it standard to assume that given a method that offers both `length` and `start`, that the final string will be of length equals to the sum of `length + start` and an additional separator character? Should then `separator` be an optional parameter that defaults to `_`?

@omaciel if you ack. As per me `separator` should optional `arg` and `default` value should `''`. It should user privilege that they want to add separator or not; even user can add separator as per gen `type`.
```
def gen_alpha(length=10, start=None, separator=''):
    pass
```
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

[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`.
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

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
coverage==6.2
-e git+https://github.com/omaciel/fauxfactory.git@df28434be0baed3e68e9c26986ff67f78b66d5b0#egg=fauxfactory
importlib-metadata==4.8.3
iniconfig==1.1.1
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
-e git+ssh://git@github.com/nebius/swebench_matterhorn.git@ae4d15b4472bd322342107dd10c47d793189f5b2#egg=swebench_matterhorn
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: fauxfactory
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - coverage==6.2
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/fauxfactory

tests/test_strings.py::test_start_string[gen_alpha] tests/test_strings.py::test_start_string[gen_alphanumeric] tests/test_strings.py::test_start_string[gen_cjk] tests/test_strings.py::test_start_string[gen_cyrillic] tests/test_strings.py::test_start_string[gen_latin1] tests/test_strings.py::test_start_string[gen_numeric_string] tests/test_strings.py::test_start_string[gen_utf8] tests/test_strings.py::test_start_string[gen_special]
tests/test_strings.py::test_positive_string[gen_html] tests/test_strings.py::test_positive_string[gen_alpha] tests/test_strings.py::test_positive_string[gen_alphanumeric] tests/test_strings.py::test_positive_string[gen_cjk] tests/test_strings.py::test_positive_string[gen_cyrillic] tests/test_strings.py::test_positive_string[gen_latin1] tests/test_strings.py::test_positive_string[gen_numeric_string] tests/test_strings.py::test_positive_string[gen_utf8] tests/test_strings.py::test_positive_string[gen_special] tests/test_strings.py::test_fixed_length_positional[gen_alpha] tests/test_strings.py::test_fixed_length_positional[gen_alphanumeric] tests/test_strings.py::test_fixed_length_positional[gen_cjk] tests/test_strings.py::test_fixed_length_positional[gen_cyrillic] tests/test_strings.py::test_fixed_length_positional[gen_latin1] tests/test_strings.py::test_fixed_length_positional[gen_numeric_string] tests/test_strings.py::test_fixed_length_positional[gen_utf8] tests/test_strings.py::test_fixed_length_positional[gen_special] tests/test_strings.py::test_fixed_length_keyword[gen_alpha] tests/test_strings.py::test_fixed_length_keyword[gen_alphanumeric] tests/test_strings.py::test_fixed_length_keyword[gen_cjk] tests/test_strings.py::test_fixed_length_keyword[gen_cyrillic] tests/test_strings.py::test_fixed_length_keyword[gen_latin1] tests/test_strings.py::test_fixed_length_keyword[gen_numeric_string] tests/test_strings.py::test_fixed_length_keyword[gen_utf8] tests/test_strings.py::test_fixed_length_keyword[gen_special] tests/test_strings.py::test_negative_length[gen_html] tests/test_strings.py::test_negative_length[gen_alpha] tests/test_strings.py::test_negative_length[gen_alphanumeric] tests/test_strings.py::test_negative_length[gen_cjk] tests/test_strings.py::test_negative_length[gen_cyrillic] tests/test_strings.py::test_negative_length[gen_latin1] tests/test_strings.py::test_negative_length[gen_numeric_string] tests/test_strings.py::test_negative_length[gen_utf8] tests/test_strings.py::test_negative_length[gen_special] tests/test_strings.py::test_zero_length[gen_html] tests/test_strings.py::test_zero_length[gen_alpha] tests/test_strings.py::test_zero_length[gen_alphanumeric] tests/test_strings.py::test_zero_length[gen_cjk] tests/test_strings.py::test_zero_length[gen_cyrillic] tests/test_strings.py::test_zero_length[gen_latin1] tests/test_strings.py::test_zero_length[gen_numeric_string] tests/test_strings.py::test_zero_length[gen_utf8] tests/test_strings.py::test_zero_length[gen_special] tests/test_strings.py::test_alpha_length[gen_html] tests/test_strings.py::test_alpha_length[gen_alpha] tests/test_strings.py::test_alpha_length[gen_alphanumeric] tests/test_strings.py::test_alpha_length[gen_cjk] tests/test_strings.py::test_alpha_length[gen_cyrillic] tests/test_strings.py::test_alpha_length[gen_latin1] tests/test_strings.py::test_alpha_length[gen_numeric_string] tests/test_strings.py::test_alpha_length[gen_utf8] tests/test_strings.py::test_alpha_length[gen_special] tests/test_strings.py::test_alphanumeric_length[gen_html] tests/test_strings.py::test_alphanumeric_length[gen_alpha] tests/test_strings.py::test_alphanumeric_length[gen_alphanumeric] tests/test_strings.py::test_alphanumeric_length[gen_cjk] tests/test_strings.py::test_alphanumeric_length[gen_cyrillic] tests/test_strings.py::test_alphanumeric_length[gen_latin1] tests/test_strings.py::test_alphanumeric_length[gen_numeric_string] tests/test_strings.py::test_alphanumeric_length[gen_utf8] tests/test_strings.py::test_alphanumeric_length[gen_special] tests/test_strings.py::test_empty_length[gen_html] tests/test_strings.py::test_empty_length[gen_alpha] tests/test_strings.py::test_empty_length[gen_alphanumeric] tests/test_strings.py::test_empty_length[gen_cjk] tests/test_strings.py::test_empty_length[gen_cyrillic] tests/test_strings.py::test_empty_length[gen_latin1] tests/test_strings.py::test_empty_length[gen_numeric_string] tests/test_strings.py::test_empty_length[gen_utf8] tests/test_strings.py::test_empty_length[gen_special] tests/test_strings.py::test_space_length[gen_html] tests/test_strings.py::test_space_length[gen_alpha] tests/test_strings.py::test_space_length[gen_alphanumeric] tests/test_strings.py::test_space_length[gen_cjk] tests/test_strings.py::test_space_length[gen_cyrillic] tests/test_strings.py::test_space_length[gen_latin1] tests/test_strings.py::test_space_length[gen_numeric_string] tests/test_strings.py::test_space_length[gen_utf8] tests/test_strings.py::test_space_length[gen_special] tests/test_strings.py::test_gen_string[html] tests/test_strings.py::test_gen_string[alpha] tests/test_strings.py::test_gen_string[alphanumeric] tests/test_strings.py::test_gen_string[cjk] tests/test_strings.py::test_gen_string[cyrillic] tests/test_strings.py::test_gen_string[latin1] tests/test_strings.py::test_gen_string[numeric] tests/test_strings.py::test_gen_string[utf8] tests/test_strings.py::test_gen_string[punctuation] tests/test_strings.py::test_gen_string_fixed_length_positional[alpha] tests/test_strings.py::test_gen_string_fixed_length_positional[alphanumeric] tests/test_strings.py::test_gen_string_fixed_length_positional[cjk] tests/test_strings.py::test_gen_string_fixed_length_positional[cyrillic] tests/test_strings.py::test_gen_string_fixed_length_positional[latin1] tests/test_strings.py::test_gen_string_fixed_length_positional[numeric] tests/test_strings.py::test_gen_string_fixed_length_positional[utf8] tests/test_strings.py::test_gen_string_fixed_length_positional[punctuation] tests/test_strings.py::test_gen_string_fixed_length_keyword[alpha] tests/test_strings.py::test_gen_string_fixed_length_keyword[alphanumeric] tests/test_strings.py::test_gen_string_fixed_length_keyword[cjk] tests/test_strings.py::test_gen_string_fixed_length_keyword[cyrillic] tests/test_strings.py::test_gen_string_fixed_length_keyword[latin1] tests/test_strings.py::test_gen_string_fixed_length_keyword[numeric] tests/test_strings.py::test_gen_string_fixed_length_keyword[utf8] tests/test_strings.py::test_gen_string_fixed_length_keyword[punctuation] tests/test_strings.py::test_chars_in_letters_category tests/test_strings.py::test_bmp_chars_only tests/test_strings.py::test_invalid_string_type tests/test_strings.py::test_special_string
fauxfactory/factories/strings.py
fauxfactory/factories/strings.py