Welcome, guest | Sign In | My Account | Store | Cart

I think this script should now be functional enough to post here at ActiveState. When you reply or vote down, please post some information on your problem with the code, and if you can, maybe something about the solution. This interface works, and is designed especially for Python. If you like it, don't be afraid to post that either, constructive criticism is all good, but compliments are even better! ;)

Get the latest version of this code here: http://wiki.sunjay.ca/Python:Contents/Python_Session

Latest Changes:

  • Added a new function for making the session cookie last longer. See code for set_expires. Note: This new function is just in case you need to include a session for longer than one brower session (for example, one day instead).
Python, 328 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#!/usr/local/bin/python26
"""
Python Session Handling

Note: A user must have cookies enabled!

TODO:
* Support Named Sessions

Copyright 2010 - Sunjay Varma

Latest Version: http://www.sunjay.ca/download/session.py
"""

import cgi
import os
import time
import errno
import hashlib
import datetime

from Cookie import SimpleCookie
from cPickle import dump, load, HIGHEST_PROTOCOL, dumps, loads

if not "HTTP_COOKIE" in os.environ:
	os.environ["HTTP_COOKIE"] = ""

SESSION = None

S_DIR = os.path.expanduser("~/.py_sessions") # expanduser for windows users
S_EXT = ".ps"
S_ID = "__sid__"

TODAY = str(datetime.date.today())
DEBUG = [] # debug messages

if not os.path.exists(S_DIR):
	os.makedirs(S_DIR)

class NoCookiesError(Exception): pass
class NotStarted(Exception): pass

class Session(object):

	def __init__(self):
		self.data = {}
		self.started = False
		self._flock = None
		self.expires = 0 # delete right away
		
		self.__sid = sid = self.__getsid()
		self.path = os.path.join(S_DIR, sid+S_EXT)

	def isset(self, name):
		"""Is the variable set in the session?"""
		if not self.started:
			raise NotStarted("Session must be started")

		return name in self

	def unset(self, name):
		"""Unset the name from the session"""
		if not self.started:
			raise NotStarted("Session must be started")
		del self[name]

	@staticmethod
	def __newsid():
		"""Create a new session ID"""
		h = hashlib.new("ripemd160")
		h.update(str(time.time()/time.clock()**-1)+str(os.getpid()))
		return h.hexdigest()

	def __getsid(self):
		"""Get the current session ID or return a new one"""
		# first, try to load the sid from the GET or POST forms
		form = cgi.FieldStorage()
		if form.has_key(S_ID):
			sid = form[S_ID].value
			return sid

		# then try to load the sid from the HTTP cookie
		self.cookie = SimpleCookie()
		if os.environ.has_key('HTTP_COOKIE'):
			self.cookie.load(os.environ['HTTP_COOKIE'])

			if S_ID in self.cookie:
				sid = self.cookie[S_ID].value
				return sid
		else:
			raise NoCookiesError("Could not find any cookies")

		# if all else fails, return a new sid
		return self.__newsid()

	def getsid(self):
		"""
		Return the name and value that the sid needs to have in a GET or POST
		request
		"""
		if not self.started:
			raise NotStarted("Session must be started")
		return (S_ID, self.__sid)

	def start(self):
		"""Start the session"""
		if self.started:
			return True # session cannot be started more than once per script

		self._flock = FileLock(self.path)
		self._flock.acquire()

		# load the session if it exists
		if os.path.exists(self.path):
			with open(self.path, "rb") as f:
				self.data = dict(load(f))
				self.data["__date_loaded__"] = TODAY

		else: # create a session
			with open(self.path, "wb") as f:
				self.data = {"__date_loaded__":TODAY}

		# the session is officially started!
		self.started = True

		# store the sid in the cookie
		self.cookie[S_ID] = self.__sid
		self.cookie[S_ID]["expires"] = str(self.expires)
		self.cookie[S_ID]["version"] = "1"

		return True

	def commit(self):
		"""Commit the changes to the session"""
		if not self.started:
			raise NotStarted("Session must be started")
		with open(self.path, "wb") as f:
			dump(self.data, f, HIGHEST_PROTOCOL)

	def destroy(self):
		"""Destroy the session"""
		if not self.started:
			raise NotStarted("Session must be started")
		os.delete(self.path)
		if self._flock:
			self._flock.release()
		self.started = False

	def output(self):
		"""Commit changes and send headers."""
		if not self.started:
			raise NotStarted("Session must be started")
		self.commit()
		return self.cookie.output()

	def setdefault(self, item, default=None):
		if not self.started:
			raise NotStarted("Session must be started")
		if not self.isset(item):
			self[item] = default

		return self[item]
		
	def set_expires(self, days):
		"""Sets the expiration of the cookie"""
		date = datetime.date.today() + datetime.timedelta(days=days)
		self.expires = date.strftime("%a, %d-%b-%Y %H:%M:%S PST")
		self.cookie[S_ID]["expires"] = str(self.expires)

	def __getitem__(self, item):
		"""Get the item from the session"""
		if not self.started:
			raise NotStarted("Session must be started")
		return self.data.__getitem__(item)

	def __setitem__(self, item, value):
		"""set the item into the session"""
		if not self.started:
			raise NotStarted("Session must be started")
		self.data.__setitem__(item, value)

	def __delitem__(self, item):
		if not self.started:
			raise NotStarted("Session must be started")
		self.data.__delitem__(item)

	def __contains__(self, item):
		"""Return if item in the session"""
		if not self.started:
			raise NotStarted("Session must be started")
		return self.data.__contains__(item)

	def __iter__(self):
		"""Go through the names of all the session variables"""
		if not self.started:
			raise NotStarted("Session must be started")
		return self.data.__iter__()

def start():
	global SESSION
	SESSION = Session()
	return SESSION.start()

def destroy():
	global SESSION
	if SESSION:
		SESSION.destroy()

def get_session():
	global SESSION
	if not SESSION:
		SESSION = Session()
	return SESSION

### The following is a (little) modified version of this:
# http://www.evanfosmark.com/2009/01/cross-platform-file-locking-support-in-
# python/

class FileLockException(Exception):
	pass

class FileLock(object):
	""" A file locking mechanism that has context-manager support so
		you can use it in a with statement. This should be relatively cross
		compatible as it doesn't rely on msvcrt or fcntl for the locking.
	"""

	def __init__(self, file_name, timeout=10, delay=.05):
		""" Prepare the file locker. Specify the file to lock and optionally
			the maximum timeout and the delay between each attempt to lock.
		"""
		self.is_locked = False
		self.lockfile = os.path.join(os.getcwd(), "%s.lock" % file_name)
		self.file_name = file_name
		self.timeout = timeout
		self.delay = delay

	def acquire(self):
		""" Acquire the lock, if possible. If the lock is in use, it check again
			every `wait` seconds. It does this until it either gets the lock or
			exceeds `timeout` number of seconds, in which case it throws
			an exception.
		"""
		if self.is_locked:
			return

		start_time = time.time()
		while True:
			try:
				self.fd = os.open(self.lockfile, os.O_CREAT|os.O_EXCL|os.O_RDWR)
				break;
			except OSError as e:
				if e.errno != errno.EEXIST:
					raise

				if (time.time() - start_time) >= self.timeout:
					raise FileLockException("Timeout occured.")
				time.sleep(self.delay)
		self.is_locked = True

	def release(self):
		""" Get rid of the lock by deleting the lockfile.
			When working in a `with` statement, this gets automatically
			called at the end.
		"""
		if self.is_locked:
			os.close(self.fd)
			os.unlink(self.lockfile)
			self.is_locked = False

	def __enter__(self):
		""" Activated when used in the with statement.
			Should automatically acquire a lock to be used in the with block.
		"""
		if not self.is_locked:
			self.acquire()
		return self

	def __exit__(self, type, value, traceback):
		""" Activated at the end of the with statement.
			It automatically releases the lock if it isn't locked.
		"""
		if self.is_locked:
			self.release()

	def __del__(self):
		""" Make sure that the FileLock instance doesn't leave a lockfile
			lying around.
		"""
		self.release()

def print_session(session):
	"""
	Prints info from the current session.

	WARNING: ONLY FOR DEBUGGING. MAJOR SECURITY RISK!
	"""
	print "<h3>Session Data</h3>"
	print "<dl>"
	for name in session:
		print "<dt>%s <i>%s</i></dt>"%(name, type(session[name]))
		print "<dd>%s</dd>"%repr(session[name])
	print "</dl>"

def main():
	"""Tester Program"""

	start() # session.start()
	if SESSION.isset("views"):
		SESSION["views"] += 1
	else:
		SESSION["views"] = 1

	print "Content-type: text/html"
	print SESSION.output()
	print

	print "<html><head><title>Session Testing</title></head><body>"
	print "<h2>You have viewed this page %i times.</h2>"%SESSION["views"]
	print_session(SESSION)
	print "<p>", SESSION.path, "</p>"
	for line in DEBUG:
		print "<p>%s</p>"%line
	print "<p>%s</p>"%SESSION.data
	print "</body></html>"

if __name__ == "__main__":
	main()

There can be some security leaks, but depending on your server, and other things, it should work quite well.

If the client does not have cookies enabled, you may need to pass some query parameters in every single time, or keep track of the session id some other way.

You can get the session id through the getsid method of the session object.

I tried to imitate the PHP session interface as much as possible, although I did add some Python specialties as well.

The setdefault method, was an idea that I stole from Python's dict object.

Of course, with my lack of PHP knowledge, I cannot say that I completely copied the Session interface. There may be things that I missed. Still consider this though, I was only going for basing this on PHP (even though I may sometimes say otherwise) this is not meant to copy the interface, only give some way into managing it through Python.

For an example on how to use this, go to http://wiki.sunjay.ca/Python:Contents/Python_Session#Examples

3 comments

Phan Tinh Phong 9 years, 9 months ago  # | flag

Good but IE not run :)

zahid 7 years, 11 months ago  # | flag

how can i make it work in IE

Paramdeep Singh 7 years, 10 months ago  # | flag

Hi I have been trying to download an image from website (no username and password required) but every time I am getting an empty file. I have used conventional urllib .retrieve and requests methodologies but getting the same result. One thing more is that if I try to open the same image manually by copy pasting the URL after 15-20 min then that image itself does not open. I am assuming that some sort of session handling is required in this case . Below is my code which returns me empty image.

def savePic(url): uri="C:\Python27\Scripts\Photosurl2.jpg" if url!="": urllib.urlretrieve(url, uri)

savePic("http://www-nass.nhtsa.dot.gov/nass/cds/GetBinary.aspx?ImageView&ImageID=491410290&Desc=Lookback+from+final+rest&Title=Scene+Photos+-+image1&Version=1&Extend=jpg")

Any help is appreciated.