Guest User

Untitled

a guest
Nov 22nd, 2017
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.15 KB | None | 0 0
  1. #!/usr/bin/python3
  2.  
  3. import logging
  4. from logging import StreamHandler
  5.  
  6. from functools import wraps
  7.  
  8. import time
  9.  
  10.  
  11. class Typoer:
  12. """
  13. A singleton class, serving a do method for inserting typos in strings. It is random -but not so random- acting in
  14. random places in the string, but cycling regularly over three modes ['switch', 'error', 'case'], and having a
  15. certain error rate (defaults to 10%) over the calls (not the printed characters).
  16.  
  17. Every typo-ed string contains one error only.
  18. """
  19. __instance = None
  20.  
  21. def __new__(cls):
  22. # In case we don't have an instance let's create it
  23. if Typoer.__instance is None:
  24. Typoer.__instance = object.__new__(cls)
  25. # Initialize values
  26. Typoer.__instance.typos = ['switch', 'error', 'case']
  27. Typoer.__instance.counter = 0
  28. Typoer.__instance.rate = 0.1
  29. # In any case let's return the instance
  30. return Typoer.__instance
  31.  
  32. @staticmethod
  33. def set_rate(rate):
  34. """
  35. Sets the error rate in terms of strings containing typos over the number of given strings.
  36. :param rate: a float in [0,1]
  37. """
  38. if rate == 1 or rate == 0:
  39. rate = float(rate)
  40. if type(rate) is not float:
  41. raise TypeError('Input rate is supposed to be float')
  42. if rate < 0 or rate > 1:
  43. raise ValueError('Input rate is supposed to be in [0,1]')
  44.  
  45. Typoer().__instance.rate = rate
  46.  
  47. @staticmethod
  48. def do(input_text):
  49. """
  50. Returns a modified version of the input text, introducing a typo pseudo-randomly
  51. :param input_text: a string of 2 or more chars
  52. :return: the original string modified with the error
  53. """
  54. if type(input_text) is not str:
  55. raise TypeError('Only string expected')
  56.  
  57. if len(input_text) <= 2:
  58. raise ValueError('The text to be injected with typo should be 2 or more chars long')
  59.  
  60. from random import randrange, random
  61. inst = Typoer()
  62.  
  63. # We don't need this anywhere else - let's define and use it right away
  64. def replace(str_in, ind, char):
  65. return str_in[:ind] + char + str_in[ind+1:]
  66.  
  67. # if we got a rand number higher than the rate, than we don't have to inject an error
  68. if inst.rate < random():
  69. return input_text
  70.  
  71. typo_type = inst.typos[inst.counter % len(inst.typos)]
  72. if typo_type == 'switch':
  73. a = randrange(len(input_text))
  74. b = 1
  75. if a:
  76. b = a-1
  77. buff = input_text[a]
  78. input_text = replace(input_text, a, input_text[b])
  79. input_text = replace(input_text, b, buff)
  80. elif typo_type == 'error':
  81. input_text = replace(input_text, randrange(len(input_text)), chr(randrange(32, 126)))
  82. # elif m == 'case':
  83. else:
  84. a = randrange(len(input_text))
  85. input_text = replace(input_text, a, input_text[a].swapcase())
  86. inst.counter += 1
  87. return input_text
  88.  
  89.  
  90. def inject_typo(rate):
  91. """
  92. A decorator that handles the string inputs of the decorated function and inserts typos with the given rate
  93. :param rate: float in [0,1] - the error rate
  94. :return: the decorated version of the method
  95. """
  96. # We do instantiate (indirectly) the Typoer and set the typo rate
  97. Typoer.set_rate(rate)
  98.  
  99. # This inner method wraps the real decorated method - this will be returned by the outer decorator receiving inputs
  100. def inner_method(method):
  101.  
  102. @wraps(method)
  103. def deeper_method(*args, **kwargs):
  104. a = ()
  105. for arg in args:
  106. if type(arg) is str:
  107. a += (Typoer.do(arg),)
  108. for k in iter(kwargs.keys()):
  109. kwargs[k] = Typoer.do(kwargs[k])
  110. return method(*a, **kwargs)
  111.  
  112. return deeper_method
  113.  
  114. return inner_method
  115.  
  116.  
  117. @inject_typo(0.8)
  118. def typewrite(string):
  119. """
  120. Typewrites the string, introducing errors with an 80% probability.
  121. """
  122. import sys
  123. from random import random
  124. # The trick is printing chars one by one flushing the stream
  125. for c in string:
  126. sys.stdout.write(c)
  127. sys.stdout.flush()
  128. # This ensures a delay of 0.125s + or - 0.025s, imitating the jitter in human typing at 8 chars per second
  129. time.sleep(0.1+random()*0.05)
  130. # We just throw a newline at the end
  131. print()
  132.  
  133.  
  134. def run():
  135. """
  136. This is the main method for an audition code in python. Here the aim is to cleanly demonstrate few skills in a not
  137. boring way. Ctrl+C to quit execution.
  138. ;]
  139. """
  140. # Looks fine - we just log to stdout
  141. log = logging.getLogger('main')
  142. h = StreamHandler()
  143. h.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(funcName)s - %(levelname)s - %(message)s'))
  144. log.addHandler(h)
  145. log.setLevel('DEBUG')
  146. log.info('Main function set up - Starting typewriting (Ctrl+C to stop)')
  147.  
  148. while True:
  149. try:
  150. typewrite('The quick brown fox jumps over the lazy dog.')
  151. time.sleep(0.5)
  152. except:
  153. print()
  154. log.info('Exiting')
  155. break
  156. return 0
  157.  
  158.  
  159. if __name__ == '__main__':
  160. run()
Add Comment
Please, Sign In to add comment