3
\$\begingroup\$

I've created a system to generate tokens that could potentially be used in some sort of program:

# _gen.py is simply to generate the tokens, not to check them.

import string, secrets, random

# String is for getting all ascii letters and digits.
# Secrets is for a cryptographically secure function to choose chars.
# Random is providing a random number generator.

# Base functions used to generate a token

def _tknFrag(length: int=32):
    """Generates a tknFrag, or Token Fragment."""
    return ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(length))

def _tknFragLen():
    """Returns the tknFrag's tknFragLen, or Token Fragment Length, an int in between 30 and 34"""
    return int(random.randint(30, 34))

def _insDot(tknFrag: str):
    """Inserts a dot in the tknFrag at the end"""
    tknFrag += "."
    return tknFrag

def _insDash(tknFrag: str):
    """Inserts a dash in the tknFrag at the end"""
    tknFrag += "-"
    return tknFrag

def _ins(tknFrag: str):
    """Inserts a dash or a dot inside of the tknFrag"""
    return {
        "0" : lambda frag: _insDot(tknFrag=frag),
        "1" : lambda frag: _insDash(tknFrag=frag)
    }[str(random.randint(0, 1))](tknFrag)

# Now on to actually generating the tokens

# This is one last helper function, completely generating a fragment
def tokenFragment(repetition):
    """Generates a fragment with the random length, and the dot/dash. The repetition is to determine wether or not to actually put the dot/dash at the end of the token."""
    return _ins(_tknFrag(length=_tknFragLen())) if repetition != 2 else _tknFrag(length=_tknFragLen())

def token():
    """Generates a three-fragment-long token"""
    return "".join([tokenFragment(repetition) for repetition in range(3)])

# Gotta test it somehow

if __name__ == '__main__':
    print(token())

Here are five results from running the code:

2dD0ZYjV4AOGMzZ2lIg6wVPzwpB82Z.BsX34AaWpvJ7i6jtWzYI1zNRjz2pI0.GRW9Uhfl9P8Xu7pYzoSYutEwuhqjhQnN

AghhjknBMqgKLpD6rR90iXKj2yuT44B.CAyXZJtAE62L97SZUbadGBmXTTSflC9th.RbuKtUDWxQ9ROgwo2OkKYEozHc1ToRz8Q

5sFE1npNyEA7JCz9hHEJFwmP2aX4CY3p.RMiKkilzUp7kmJCigrB6HfOVPkWmsczJ-4DF3qLuQSgdwmRxiOMBLUw1ZLj1Al7n

6FCXlDGWjAKylt5rZFYxOfLecxyyL4Pj-0MGu0B2knedbR8HnFI16gHChWZ8uldQ.chCVuFycHPJXF2tj9wR3mi4W4yuRp3o

rEAi9Rn1lXNkMett8wHXLF0iJLQHbc6lI.ksViH35sOlIwFfvOjVHlyXfMS0Ye58diZB-jEX93wyHTRznkYUJvbADmOrdZZF2D0R0

Note that the dots and dashes are for looks only and serve no purpose.

\$\endgroup\$
3
\$\begingroup\$

Well, in the first place I have a question as to why you would need this specific pattern of tokens; it seems for a very specific use case.

Secondly, you use a pretty consistent naming convention in your code (which is very good), but the shortened names and abbreviations make it difficult for a person to understand your code quickly (in my opinion).


Lastly, if you are looking for less code and more speed with the same functionality, I can propose this:

import random
import secrets
import string

FRAGMENT_ALPHABET = string.ascii_letters + string.digits
FRAGMENT_SEPARATOR_OPTIONS = ('.', '-')
FRAGMENT_MIN_LENGTH = 30
FRAGMENT_MAX_LENGTH = 34

def _generate_fragment(length: int, prepend_separator: bool):
    prefix = ''
    if prepend_separator:
        prefix = secrets.choice(FRAGMENT_SEPARATOR_OPTIONS)

    s = ''.join(
        secrets.choice(FRAGMENT_ALPHABET)
        for _ in range(length))

    return prefix + s

def token(num_parts: int = 3):
    return ''.join(
        _generate_fragment(
            length=random.randint(FRAGMENT_MIN_LENGTH, FRAGMENT_MAX_LENGTH),
            prepend_separator=i > 0)          # only False for the first fragment
        for i in range(num_parts))

In this code:

  • In my opinion (opinions can be very subjective) the code is still readable, but has less functions and those have explicit long names, which helps users to understand it faster.
  • I also put some general parameters as uppercase module variables at the top of the file; this makes it easier to modify and (in my opinion) also easier to read/understand in the code.
  • The execution time is less. Comparing the two versions using timeit like this for both versions (with Ubuntu and Python3.6):

    >>> timeit.timeit('token()', 'from __main__ import token', number=10000)
    

    I get these results, showing my version as faster using about 8 % less time:

    Version    Time      Time comparisson
    yours      2.222258   100.0 %
    mine       2.060732    92.7 %
    
\$\endgroup\$
  • \$\begingroup\$ Thank you! Although, the shortened names were not designed to be used by the user. They were designed to be used by the _ins and token functions. \$\endgroup\$ – Shiatryx Jan 12 at 19:51

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.