Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python3
- import logging
- from logging import StreamHandler
- from functools import wraps
- import time
- class Typoer:
- """
- A singleton class, serving a do method for inserting typos in strings. It is random -but not so random- acting in
- random places in the string, but cycling regularly over three modes ['switch', 'error', 'case'], and having a
- certain error rate (defaults to 10%) over the calls (not the printed characters).
- Every typo-ed string contains one error only.
- """
- __instance = None
- def __new__(cls):
- # In case we don't have an instance let's create it
- if Typoer.__instance is None:
- Typoer.__instance = object.__new__(cls)
- # Initialize values
- Typoer.__instance.typos = ['switch', 'error', 'case']
- Typoer.__instance.counter = 0
- Typoer.__instance.rate = 0.1
- # In any case let's return the instance
- return Typoer.__instance
- @staticmethod
- def set_rate(rate):
- """
- Sets the error rate in terms of strings containing typos over the number of given strings.
- :param rate: a float in [0,1]
- """
- if rate == 1 or rate == 0:
- rate = float(rate)
- if type(rate) is not float:
- raise TypeError('Input rate is supposed to be float')
- if rate < 0 or rate > 1:
- raise ValueError('Input rate is supposed to be in [0,1]')
- Typoer().__instance.rate = rate
- @staticmethod
- def do(input_text):
- """
- Returns a modified version of the input text, introducing a typo pseudo-randomly
- :param input_text: a string of 2 or more chars
- :return: the original string modified with the error
- """
- if type(input_text) is not str:
- raise TypeError('Only string expected')
- if len(input_text) <= 2:
- raise ValueError('The text to be injected with typo should be 2 or more chars long')
- from random import randrange, random
- inst = Typoer()
- # We don't need this anywhere else - let's define and use it right away
- def replace(str_in, ind, char):
- return str_in[:ind] + char + str_in[ind+1:]
- # if we got a rand number higher than the rate, than we don't have to inject an error
- if inst.rate < random():
- return input_text
- typo_type = inst.typos[inst.counter % len(inst.typos)]
- if typo_type == 'switch':
- a = randrange(len(input_text))
- b = 1
- if a:
- b = a-1
- buff = input_text[a]
- input_text = replace(input_text, a, input_text[b])
- input_text = replace(input_text, b, buff)
- elif typo_type == 'error':
- input_text = replace(input_text, randrange(len(input_text)), chr(randrange(32, 126)))
- # elif m == 'case':
- else:
- a = randrange(len(input_text))
- input_text = replace(input_text, a, input_text[a].swapcase())
- inst.counter += 1
- return input_text
- def inject_typo(rate):
- """
- A decorator that handles the string inputs of the decorated function and inserts typos with the given rate
- :param rate: float in [0,1] - the error rate
- :return: the decorated version of the method
- """
- # We do instantiate (indirectly) the Typoer and set the typo rate
- Typoer.set_rate(rate)
- # This inner method wraps the real decorated method - this will be returned by the outer decorator receiving inputs
- def inner_method(method):
- @wraps(method)
- def deeper_method(*args, **kwargs):
- a = ()
- for arg in args:
- if type(arg) is str:
- a += (Typoer.do(arg),)
- for k in iter(kwargs.keys()):
- kwargs[k] = Typoer.do(kwargs[k])
- return method(*a, **kwargs)
- return deeper_method
- return inner_method
- @inject_typo(0.8)
- def typewrite(string):
- """
- Typewrites the string, introducing errors with an 80% probability.
- """
- import sys
- from random import random
- # The trick is printing chars one by one flushing the stream
- for c in string:
- sys.stdout.write(c)
- sys.stdout.flush()
- # This ensures a delay of 0.125s + or - 0.025s, imitating the jitter in human typing at 8 chars per second
- time.sleep(0.1+random()*0.05)
- # We just throw a newline at the end
- print()
- def run():
- """
- This is the main method for an audition code in python. Here the aim is to cleanly demonstrate few skills in a not
- boring way. Ctrl+C to quit execution.
- ;]
- """
- # Looks fine - we just log to stdout
- log = logging.getLogger('main')
- h = StreamHandler()
- h.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(funcName)s - %(levelname)s - %(message)s'))
- log.addHandler(h)
- log.setLevel('DEBUG')
- log.info('Main function set up - Starting typewriting (Ctrl+C to stop)')
- while True:
- try:
- typewrite('The quick brown fox jumps over the lazy dog.')
- time.sleep(0.5)
- except:
- print()
- log.info('Exiting')
- break
- return 0
- if __name__ == '__main__':
- run()
Add Comment
Please, Sign In to add comment