comparison data_generation/transformations/pycaptcha/Captcha/Base.py @ 167:1f5937e9e530

More moves - transformations into data_generation, added "deep" folder
author Dumitru Erhan <dumitru.erhan@gmail.com>
date Fri, 26 Feb 2010 14:15:38 -0500
parents pycaptcha/Captcha/Base.py@4775b4195b4b
children
comparison
equal deleted inserted replaced
166:17ae5a1a4dd1 167:1f5937e9e530
1 """ Captcha.Base
2
3 Base class for all types of CAPTCHA tests. All tests have one or
4 more solution, determined when the test is generated. Solutions
5 can be any python object,
6
7 All tests can be solved by presenting at least some preset number
8 of correct solutions. Some tests may only have one solution and require
9 one solution, but other tests may require N correct solutions of M
10 possible solutions.
11 """
12 #
13 # PyCAPTCHA Package
14 # Copyright (C) 2004 Micah Dowty <micah@navi.cx>
15 #
16
17 import random, string, time, shelve
18
19 __all__ = ["BaseCaptcha", "Factory", "PersistentFactory"]
20
21
22 def randomIdentifier(alphabet = string.ascii_letters + string.digits,
23 length = 24):
24 return "".join([random.choice(alphabet) for i in xrange(length)])
25
26
27 class BaseCaptcha(object):
28 """Base class for all CAPTCHA tests"""
29 # Subclasses can override these to set the solution criteria
30 minCorrectSolutions = 1
31 maxIncorrectSolutions = 0
32
33 def __init__(self):
34 self.solutions = []
35 self.valid = True
36
37 # Each test has a unique identifier, used to refer to that test
38 # later, and a creation time so it can expire later.
39 self.id = randomIdentifier()
40 self.creationTime = time.time()
41
42 def addSolution(self, solution):
43 self.solutions.append(solution)
44
45 def testSolutions(self, solutions):
46 """Test whether the given solutions are sufficient for this CAPTCHA.
47 A given CAPTCHA can only be tested once, after that it is invalid
48 and always returns False. This makes random guessing much less effective.
49 """
50 if not self.valid:
51 return False
52 self.valid = False
53
54 numCorrect = 0
55 numIncorrect = 0
56
57 for solution in solutions:
58 if solution in self.solutions:
59 numCorrect += 1
60 else:
61 numIncorrect += 1
62
63 return numCorrect >= self.minCorrectSolutions and \
64 numIncorrect <= self.maxIncorrectSolutions
65
66
67 class Factory(object):
68 """Creates BaseCaptcha instances on demand, and tests solutions.
69 CAPTCHAs expire after a given amount of time, given in seconds.
70 The default is 15 minutes.
71 """
72 def __init__(self, lifetime=60*15):
73 self.lifetime = lifetime
74 self.storedInstances = {}
75
76 def new(self, cls, *args, **kwargs):
77 """Create a new instance of our assigned BaseCaptcha subclass, passing
78 it any extra arguments we're given. This stores the result for
79 later testing.
80 """
81 self.clean()
82 inst = cls(*args, **kwargs)
83 self.storedInstances[inst.id] = inst
84 return inst
85
86 def get(self, id):
87 """Retrieve the CAPTCHA with the given ID. If it's expired already,
88 this will return None. A typical web application will need to
89 new() a CAPTCHA when generating an html page, then get() it later
90 when its images or sounds must be rendered.
91 """
92 return self.storedInstances.get(id)
93
94 def clean(self):
95 """Removed expired tests"""
96 expiredIds = []
97 now = time.time()
98 for inst in self.storedInstances.itervalues():
99 if inst.creationTime + self.lifetime < now:
100 expiredIds.append(inst.id)
101 for id in expiredIds:
102 del self.storedInstances[id]
103
104 def test(self, id, solutions):
105 """Test the given list of solutions against the BaseCaptcha instance
106 created earlier with the given id. Returns True if the test passed,
107 False on failure. In either case, the test is invalidated. Returns
108 False in the case of an invalid id.
109 """
110 self.clean()
111 inst = self.storedInstances.get(id)
112 if not inst:
113 return False
114 result = inst.testSolutions(solutions)
115 return result
116
117
118 class PersistentFactory(Factory):
119 """A simple persistent factory, for use in CGI or multi-process environments
120 where the state must remain across python interpreter sessions.
121 This implementation uses the 'shelve' module.
122 """
123 def __init__(self, filename, lifetime=60*15):
124 Factory.__init__(self, lifetime)
125 self.storedInstances = shelve.open(filename)
126
127 ### The End ###