{"id":17044772,"url":"https://github.com/keredson/drsession","last_synced_at":"2026-03-06T14:03:50.469Z","repository":{"id":57424219,"uuid":"62817040","full_name":"keredson/drsession","owner":"keredson","description":"Derek's Redis Session Middleware","archived":false,"fork":false,"pushed_at":"2017-04-20T21:36:47.000Z","size":17,"stargazers_count":8,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-26T09:51:10.697Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/keredson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-07T15:18:23.000Z","updated_at":"2022-08-06T18:28:01.000Z","dependencies_parsed_at":"2022-09-14T08:40:27.589Z","dependency_job_id":null,"html_url":"https://github.com/keredson/drsession","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fdrsession","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fdrsession/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fdrsession/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fdrsession/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/keredson","download_url":"https://codeload.github.com/keredson/drsession/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248151635,"owners_count":21056142,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T09:35:28.204Z","updated_at":"2026-03-06T14:03:50.406Z","avatar_url":"https://github.com/keredson.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cbr\u003e\n\u003cp align='center'\u003e\n  \u003cimg src='https://cloud.githubusercontent.com/assets/2049665/16746260/20813782-477f-11e6-8161-5d0f8ab37b40.png'\u003e\n\u003c/p\u003e\n\n# DRSession\nDerek's Redis Session Middleware\n\n## Why?\nI started a bottle project with the beaker session example everyone seems to give:\n\n```python\n\n# EXAMPLE OF BEAKER'S MIDDLEWARE - DO NOT USE!!!\n\nimport bottle\nfrom beaker.middleware import SessionMiddleware\n\nsession_opts = {\n    'session.type': 'ext:memcached',\n    'session.url': '127.0.0.1:11211',\n    'session.cookie_expires': False,\n    'session.auto': True,\n    'session.key': 'hvst-session',\n    'session.lock_dir': '/tmp/.lock_dir',\n}\napp = SessionMiddleware(bottle.app(), session_opts)\n\n@bottle.route('/test')\ndef test():\n  s = bottle.request.environ.get('beaker.session')\n  s['test'] = s.get('test',0) + 1\n  s.save()\n  return 'Test counter: %d' % s['test']\n\nbottle.run(app=app)\n```\n\nBut it didn't quite fit our needs for the following reasons:\n\n1. It's using `memcached` which drops old sessions after a while (and all sessions during restarts).\n2. It's serializing/setting the entire session every request (even when no values in the session were set).  This was causing changes to be lost in production due to the obvious race condition.\n3. If I'm unable to set a session value, I want an exception, not a silent failure.\n4. WTF is up w/ the required lock file?!?\n\nBeaker's session middleware supposedly supports Redis (which in theory would solve the first problem, but we couldn't get it to work), but the rest remain.  So I wrote this bit of code.\n\n## Install\n```bash\n$ sudo pip install drsession\n```\n\n## Hello World\n\n```python\nimport bottle, drsession\n\napp = drsession.SessionMiddleware(bottle.app())\n\n@bottle.hook('before_request')\ndef setup_session():\n  bottle.request.session = bottle.request.environ.get('drsession')\n\n@bottle.route('/set')\ndef set():\n  bottle.request.session['foo'] = 'bar'\n  return 'OK'\n\n@bottle.route('/get')\ndef get():\n  return bottle.request.session['foo']\n\nbottle.run(app=app)\n```\n\nThis backs your session object with the Redis server instance running on `localhost` (using a connection pool) and sets `request.session` as the dictionary-like interface.\n\n## Design\n\n### All Changes are Live\nThere is no `request.session.save()`.\n\nIf I call `request.session['foo'] = 'bar'`, it sets it in Redis right then and there.  If Redis is unavailable or throws an exception, you get that exception.  No more \"I JUST set that - where the heck did it go?\" debugging.\n\n### Don't Write All The Things\n\nIf I call `request.session['foo'] = 'bar'`, just write `foo`.\n\nDon't write every value in my entire session every time I set anything.\n\nDefinitely don't write every value in my entire session every time I read anything.\n\n### Take Advantage of Redis' Hash Type\n\nYour session id (plus a prefix) is your Redis key.  The keys in your session are keys to the Redis hash type set at your Redis key.  The hash values are `json` encoded strings of the objects in your session.\n\n### Use `HINCRBY` for `+=` Operations\n\nFor fast, race-free counters.\n\n### Support All the Types\n\nBy default DRSession serializes with the python `json` module.  If you wish for a different encoding, pass in new functions to the `loads` and `dumps` parameters of `SessionMiddleware`.\n\n### Keep it Simple\n\nThis isn't rocket science.  One file (`drsession.py`) is required to use.  Very few lines of code.\n\n## Documentation\n\n```\n\u003e\u003e\u003e help(drsession.SessionMiddleware)\nclass SessionMiddleware(__builtin__.object)\n |  Methods defined here:\n |  __init__(self, app, \n |      prefix='drsession:', cookie='drsession', env='drsession', \n |      redis_server=None, redis_kwargs={},\n |      connection_pool=None, connection_pool_kwargs={},\n |      loads=None, dumps=None)\n```\n\n| Option        | Description  |\n| ------------- | ------------ |\n| `app`         | Any WSGI app object (ie any callable object that takes `environ, start_response, exec_info=None`). |\n| `prefix`      | The Redis key prefix.  Default is `'drsession:'`.  If your session id is `abc123` your Redis key will be `'drsession:abc123'`. |\n| `cookie` | The HTML cookie storing your session id. |\n| `env` | The key  used to store the session object in `bottle.request.environ`. |\n| `redis_server` and `redis_kwargs` | If `redis_server` is provided it is used directly.  Else created with `redis.Redis(**redis_kwargs)` |\n| `connection_pool` and `connection_pool_kwargs` | If `connection_pool` is provided it is used directly.  Else created with `redis.ConnectionPool(**connection_pool_kwargs)` if `connection_pool_kwargs` is defined.  To not use any connection pooling set `connection_pool_kwargs=None`.  To be \"used\" means to be added to `redis_kwargs`. |\n| `dumps` and `loads` | Serialization and deserialization functions.  Default to `json.dumps` and `json.loads`. |\n\n## Testing\n```bash\n$ python test.py \n..................\n----------------------------------------------------------------------\nRan 18 tests in 0.010s\n\nOK\n```\n\n## Performance\n```bash\n$ cat /proc/cpuinfo | grep \"model name\" | uniq\nmodel name\t: Intel(R) Core(TM) i5-6260U CPU @ 1.80GHz\n\n$ python performance.py \n\n--==[ DRSession ]==--\n\nHitting a local Redis server.\nAll data changes are live.\n\nTESTING: session['foo'] = 'bar' # set a value\n20000 loops took 0.862565040588 seconds (0.043ms per loop)\n\nTESTING: session['foo'] # read a value\n20000 loops took 0.903891086578 seconds (0.045ms per loop)\n\nTESTING: 'foo' in session # does a value exist?\n20000 loops took 0.729822874069 seconds (0.036ms per loop)\n\nTESTING: del session['foo'] # remove a value\n20000 loops took 0.743810892105 seconds (0.037ms per loop)\n\nTESTING: session.save() # doesn't do anything\n20000 loops took 0.00244402885437 seconds (0.000ms per loop)\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeredson%2Fdrsession","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkeredson%2Fdrsession","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeredson%2Fdrsession/lists"}