├── reqs.txt ├── redi ├── packages │ ├── __init__.py │ └── jsonpickle │ │ ├── compat.py │ │ ├── tags.py │ │ ├── handlers.py │ │ ├── util.py │ │ ├── unpickler.py │ │ ├── pickler.py │ │ └── __init__.py ├── __init__.py ├── db.py ├── config.py ├── core.py ├── utils.py └── models.py ├── .gitignore ├── MANIFEST.in ├── docs ├── _themes │ ├── .gitignore │ ├── kr │ │ ├── theme.conf │ │ ├── relations.html │ │ ├── layout.html │ │ └── static │ │ │ ├── small_flask.css │ │ │ └── flasky.css_t │ ├── kr_small │ │ ├── theme.conf │ │ ├── layout.html │ │ └── static │ │ │ └── flasky.css_t │ ├── README.rst │ ├── LICENSE │ └── flask_theme_support.py ├── index.rst ├── Makefile ├── make.bat └── conf.py ├── toy.py ├── HACKING ├── LICENSE ├── setup.py ├── NOTICE ├── README.rst └── test_redi.py /reqs.txt: -------------------------------------------------------------------------------- 1 | redis-py -------------------------------------------------------------------------------- /redi/packages/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MANIFEST 2 | .idea/ 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE -------------------------------------------------------------------------------- /redi/__init__.py: -------------------------------------------------------------------------------- 1 | from core import * 2 | -------------------------------------------------------------------------------- /docs/_themes/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /redi/packages/jsonpickle/compat.py: -------------------------------------------------------------------------------- 1 | try: 2 | set = set 3 | except NameError: 4 | from sets import Set as set 5 | set = set 6 | -------------------------------------------------------------------------------- /docs/_themes/kr/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | pygments_style = flask_theme_support.FlaskyStyle 5 | 6 | [options] 7 | touch_icon = 8 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | nosidebar = true 5 | pygments_style = flask_theme_support.FlaskyStyle 6 | 7 | [options] 8 | index_logo = '' 9 | index_logo_height = 120px 10 | github_fork = '' 11 | -------------------------------------------------------------------------------- /toy.py: -------------------------------------------------------------------------------- 1 | import redi 2 | 3 | lst = redi.list(('haystack', 'metalist')) 4 | 5 | # lst.append({'x': 'y'}) 6 | 7 | # for l in lst: 8 | # print l 9 | 10 | # lst.delete() 11 | 12 | # lst.append(0) 13 | # lst.append(124) 14 | # lst.append(346) 15 | # lst.append(46) 16 | 17 | # print type(lst[1]) 18 | lst[2] = 23.3 19 | 20 | # print type(lst[2]) 21 | # print (lst[2]) 22 | 23 | # print lst[2].keys() 24 | 25 | # lst[2]['face'] = ['book'] 26 | 27 | print list(lst._raw) 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Redi documentation master file, created by 2 | sphinx-quickstart on Sat Apr 9 18:35:16 2011. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Redi's documentation! 7 | ================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | Indices and tables 15 | ================== 16 | 17 | * :ref:`genindex` 18 | * :ref:`modindex` 19 | * :ref:`search` 20 | 21 | -------------------------------------------------------------------------------- /redi/db.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | redi.db 5 | ~~~~~~~ 6 | 7 | This module contains simple mappings to 8 | 9 | """ 10 | 11 | from . import config 12 | 13 | 14 | def flush(redis=None): 15 | """Flushes active Redis database.""" 16 | 17 | if redis is None: 18 | redis = config.redis 19 | 20 | return redis.flushdb() 21 | 22 | 23 | def keys(search='*', redis=None): 24 | """Flushes Redis database.""" 25 | 26 | if redis is None: 27 | redis = config.redis 28 | 29 | return redis.keys(search) 30 | 31 | -------------------------------------------------------------------------------- /redi/packages/jsonpickle/tags.py: -------------------------------------------------------------------------------- 1 | """The jsonpickle.tags module provides the custom tags 2 | used for pickling and unpickling Python objects. 3 | 4 | These tags are keys into the flattened dictionaries 5 | created by the Pickler class. The Unpickler uses 6 | these custom key names to identify dictionaries 7 | that need to be specially handled. 8 | """ 9 | from .compat import set 10 | 11 | OBJECT = 'py/object' 12 | TYPE = 'py/type' 13 | REPR = 'py/repr' 14 | REF = 'py/ref' 15 | TUPLE = 'py/tuple' 16 | SET = 'py/set' 17 | SEQ = 'py/seq' 18 | STATE = 'py/state' 19 | 20 | # All reserved tag names 21 | RESERVED = set([OBJECT, TYPE, REPR, REF, TUPLE, SET, SEQ, STATE]) 22 | -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | Where possible, please follow PEP8 with regard to coding style. Sometimes the line 2 | length restriction is too hard to follow, so don't bend over backwards there. 3 | 4 | Triple-quotes should always be """, single quotes are ' unless using " 5 | would result in less escaping within the string. 6 | 7 | All modules, functions, and methods should be well documented reStructuredText for 8 | Sphinx AutoDoc. 9 | 10 | All functionality should be available in pure Python. Optional C (via Cython) 11 | implementations may be written for performance reasons, but should never 12 | replace the Python implementation. 13 | 14 | Lastly, don't take yourself too seriously :) -------------------------------------------------------------------------------- /docs/_themes/kr/relations.html: -------------------------------------------------------------------------------- 1 |

Related Topics

2 | 20 | -------------------------------------------------------------------------------- /redi/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | redi.config 5 | ~~~~~~~~~~~ 6 | 7 | This module contains the redi configuration. 8 | 9 | """ 10 | 11 | 12 | from redis import Redis as RedisClient 13 | 14 | from .packages import jsonpickle 15 | 16 | 17 | encoder = jsonpickle.encode 18 | decoder = jsonpickle.decode 19 | namespace_delimiter = ':' 20 | str_codec = 'utf8' 21 | block_timeout = 10 22 | 23 | 24 | 25 | def init(host='localhost', port=6379, db=0, password=None, r=None): 26 | """Configures module-level redis instance.""" 27 | 28 | global redis 29 | 30 | if r is not None: 31 | redis = r 32 | else: 33 | redis = RedisClient(host=host, port=port, db=db, password=password) 34 | 35 | 36 | init() 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Kenneth Reitz 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /docs/_themes/kr_small/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | {% block header %} 3 | {{ super() }} 4 | {% if pagename == 'index' %} 5 |
6 | {% endif %} 7 | {% endblock %} 8 | {% block footer %} 9 | {% if pagename == 'index' %} 10 |
11 | {% endif %} 12 | {% endblock %} 13 | {# do not display relbars #} 14 | {% block relbar1 %}{% endblock %} 15 | {% block relbar2 %} 16 | {% if theme_github_fork %} 17 | Fork me on GitHub 19 | {% endif %} 20 | {% endblock %} 21 | {% block sidebar1 %}{% endblock %} 22 | {% block sidebar2 %}{% endblock %} 23 | -------------------------------------------------------------------------------- /docs/_themes/README.rst: -------------------------------------------------------------------------------- 1 | krTheme Sphinx Style 2 | ==================== 3 | 4 | This repository contains sphinx styles Kenneth Reitz uses in most of 5 | his projects. It is a drivative of Mitsuhiko's themes for Flask and Flask related 6 | projects. To use this style in your Sphinx documentation, follow 7 | this guide: 8 | 9 | 1. put this folder as _themes into your docs folder. Alternatively 10 | you can also use git submodules to check out the contents there. 11 | 12 | 2. add this to your conf.py: :: 13 | 14 | sys.path.append(os.path.abspath('_themes')) 15 | html_theme_path = ['_themes'] 16 | html_theme = 'flask' 17 | 18 | The following themes exist: 19 | 20 | **kr** 21 | the standard flask documentation theme for large projects 22 | 23 | **kr_small** 24 | small one-page theme. Intended to be used by very small addon libraries. 25 | 26 | -------------------------------------------------------------------------------- /docs/_themes/kr/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "basic/layout.html" %} 2 | {%- block extrahead %} 3 | {{ super() }} 4 | {% if theme_touch_icon %} 5 | 6 | {% endif %} 7 | 9 | {% endblock %} 10 | {%- block relbar2 %}{% endblock %} 11 | {%- block footer %} 12 | 16 | 31 | {%- endblock %} 32 | -------------------------------------------------------------------------------- /docs/_themes/kr/static/small_flask.css: -------------------------------------------------------------------------------- 1 | /* 2 | * small_flask.css_t 3 | * ~~~~~~~~~~~~~~~~~ 4 | * 5 | * :copyright: Copyright 2010 by Armin Ronacher. 6 | * :license: Flask Design License, see LICENSE for details. 7 | */ 8 | 9 | body { 10 | margin: 0; 11 | padding: 20px 30px; 12 | } 13 | 14 | div.documentwrapper { 15 | float: none; 16 | background: white; 17 | } 18 | 19 | div.sphinxsidebar { 20 | display: block; 21 | float: none; 22 | width: 102.5%; 23 | margin: 50px -30px -20px -30px; 24 | padding: 10px 20px; 25 | background: #333; 26 | color: white; 27 | } 28 | 29 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 30 | div.sphinxsidebar h3 a { 31 | color: white; 32 | } 33 | 34 | div.sphinxsidebar a { 35 | color: #aaa; 36 | } 37 | 38 | div.sphinxsidebar p.logo { 39 | display: none; 40 | } 41 | 42 | div.document { 43 | width: 100%; 44 | margin: 0; 45 | } 46 | 47 | div.related { 48 | display: block; 49 | margin: 0; 50 | padding: 10px 0 20px 0; 51 | } 52 | 53 | div.related ul, 54 | div.related ul li { 55 | margin: 0; 56 | padding: 0; 57 | } 58 | 59 | div.footer { 60 | display: none; 61 | } 62 | 63 | div.bodywrapper { 64 | margin: 0; 65 | } 66 | 67 | div.body { 68 | min-height: 0; 69 | padding: 0; 70 | } 71 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | 7 | from distutils.core import setup 8 | 9 | 10 | def publish(): 11 | """Publish to PyPi""" 12 | os.system("python setup.py sdist upload") 13 | 14 | if sys.argv[-1] == "publish": 15 | publish() 16 | sys.exit() 17 | 18 | required = ['redis'] 19 | 20 | setup( 21 | name='redi', 22 | version='0.0.8', 23 | description='Python middle layer for interacting with Redis data easily.', 24 | long_description=open('README.rst').read(), 25 | author='Kenneth Reitz', 26 | author_email='me@kennethreitz.com', 27 | url='https://github.com/kennethreitz/redi', 28 | packages= [ 29 | 'redi', 30 | 'redi.packages', 'redi.packages.jsonpickle' 31 | ], 32 | install_requires=required, 33 | license='ISC', 34 | classifiers=( 35 | # 'Development Status :: 5 - Production/Stable', 36 | 'Intended Audience :: Developers', 37 | 'Natural Language :: English', 38 | 'License :: OSI Approved :: ISC License (ISCL)', 39 | 'Programming Language :: Python', 40 | # 'Programming Language :: Python :: 2.5', 41 | 'Programming Language :: Python :: 2.6', 42 | 'Programming Language :: Python :: 2.7', 43 | # 'Programming Language :: Python :: 3.0', 44 | # 'Programming Language :: Python :: 3.1', 45 | ), 46 | ) 47 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Redi includes some vendorized python libraries: jsonpickle 2 | 3 | JsonPickle License 4 | ================== 5 | 6 | Copyright (C) 2008 John Paulett (john -at- paulett.org) 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions 11 | are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | 2. Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in 17 | the documentation and/or other materials provided with the 18 | distribution. 19 | 3. The name of the author may not be used to endorse or promote 20 | products derived from this software without specific prior 21 | written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 24 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 27 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 29 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 31 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 33 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /redi/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | redi.core 5 | ~~~~~~~~~ 6 | 7 | This module contains the primary interface for redi. 8 | 9 | Simple, eh? 10 | """ 11 | 12 | 13 | from . import config, models, db, utils 14 | 15 | # REDI.S! 16 | s = models.RedisKey('', redis=None, o=True) 17 | 18 | 19 | def key(key, redis=config.redis, default=None): 20 | """Return Redi Datatype instance for given key. 21 | Optional `redis` keyword argument sets Redis instance. 22 | Optional `default` keyword sets default Redis datatype if no value 23 | exists. 24 | """ 25 | 26 | return models.auto_type( 27 | utils.compress_key(key), redis=config.redis, default=default 28 | ) 29 | 30 | 31 | def list_string(key, redis=config.redis): 32 | """Return RedisValue instance for given key. 33 | Optional `redis` keyword argument sets Redis instance. 34 | """ 35 | 36 | return models.RedisListString(utils.compress_key(key), redis=config.redis) 37 | 38 | 39 | def dict_string(key, redis=config.redis): 40 | """Return RedisValue instance for given key. 41 | Optional `redis` keyword argument sets Redis instance. 42 | """ 43 | 44 | return models.RedisDictString(utils.compress_key(key), redis=config.redis) 45 | 46 | 47 | def string(key, redis=config.redis): 48 | """Return RedisValue instance for given key. 49 | Optional `redis` keyword argument sets Redis instance. 50 | """ 51 | 52 | return models.RedisString(utils.compress_key(key), redis=config.redis) 53 | 54 | 55 | def list(key, redis=config.redis): 56 | """Return RedisList instance for given key. 57 | Optional `redis` keyword argument sets Redis instance. 58 | """ 59 | 60 | return models.RedisList(utils.compress_key(key), redis=config.redis) 61 | -------------------------------------------------------------------------------- /redi/packages/jsonpickle/handlers.py: -------------------------------------------------------------------------------- 1 | class BaseHandler(object): 2 | """ 3 | Abstract base class for handlers. 4 | """ 5 | def __init__(self, base): 6 | """ 7 | Initialize a new handler to handle `type`. 8 | 9 | :Parameters: 10 | - `base`: reference to pickler/unpickler 11 | 12 | """ 13 | self._base = base 14 | 15 | def flatten(self, obj, data): 16 | """ 17 | Flatten `obj` into a json-friendly form. 18 | 19 | :Parameters: 20 | - `obj`: object of `type` 21 | 22 | """ 23 | raise NotImplementedError("Abstract method.") 24 | 25 | def restore(self, obj): 26 | """ 27 | Restores the `obj` to `type` 28 | 29 | :Parameters: 30 | - `object`: json-friendly object 31 | 32 | """ 33 | raise NotImplementedError("Abstract method.") 34 | 35 | 36 | class Registry(object): 37 | REGISTRY = {} 38 | 39 | def register(self, cls, handler): 40 | """ 41 | Register handler. 42 | 43 | :Parameters: 44 | - `cls`: Object class 45 | - `handler`: `BaseHandler` subclass 46 | 47 | """ 48 | self.REGISTRY[cls] = handler 49 | return handler 50 | 51 | def unregister(self, cls): 52 | """ 53 | Unregister hander. 54 | 55 | :Parameters: 56 | - `cls`: Object class 57 | """ 58 | if cls in self.REGISTRY: 59 | del self.REGISTRY[cls] 60 | 61 | def get(self, cls): 62 | """ 63 | Get the customer handler for `obj` (if any) 64 | 65 | :Parameters: 66 | - `cls`: class to handle 67 | 68 | """ 69 | return self.REGISTRY.get(cls, None) 70 | 71 | 72 | registry = Registry() 73 | -------------------------------------------------------------------------------- /docs/_themes/LICENSE: -------------------------------------------------------------------------------- 1 | Modifications: 2 | 3 | Copyright (c) 2011 Kenneth Reitz. 4 | 5 | 6 | Original Project: 7 | 8 | Copyright (c) 2010 by Armin Ronacher. 9 | 10 | 11 | Some rights reserved. 12 | 13 | Redistribution and use in source and binary forms of the theme, with or 14 | without modification, are permitted provided that the following conditions 15 | are met: 16 | 17 | * Redistributions of source code must retain the above copyright 18 | notice, this list of conditions and the following disclaimer. 19 | 20 | * Redistributions in binary form must reproduce the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer in the documentation and/or other materials provided 23 | with the distribution. 24 | 25 | * The names of the contributors may not be used to endorse or 26 | promote products derived from this software without specific 27 | prior written permission. 28 | 29 | We kindly ask you to only use these themes in an unmodified manner just 30 | for Flask and Flask-related products, not for unrelated projects. If you 31 | like the visual style and want to use it for your own projects, please 32 | consider making some larger changes to the themes (such as changing 33 | font faces, sizes, colors or margins). 34 | 35 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 36 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 39 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 40 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 41 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 42 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 43 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 44 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE 45 | POSSIBILITY OF SUCH DAMAGE. 46 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Redi: Redis Awesomeness 2 | ======================= 3 | 4 | Redi is Redis that's ready for Python. It's powered by redis-py, and it's awesome. 5 | 6 | Essentially, **redi** allows you to interact with your Redis datatypes as if they were native Python objects. 7 | 8 | 9 | Features 10 | -------- 11 | 12 | - Pythonic interface to Redis keys/values. 13 | - Everything is automatically serialized to JSON. This is configurable. 14 | - ^^ Including custom objects. 15 | - Limited type-specific search. 16 | 17 | 18 | 19 | Usage 20 | ----- 21 | 22 | - **Easy to import** :: 23 | 24 | import redi 25 | 26 | 27 | - **Resis Value as a List** :: 28 | 29 | 30 | d = redi.list_string('haystack:metalist') 31 | 32 | d.append(dict(blah='blah')) 33 | d.append([1,2,3,4]) 34 | 35 | # redis: "[{\"blah\": \"blah\"}, [1, 2, 3, 4]]" 36 | 37 | 38 | - **Resis Value as a Dict** :: 39 | 40 | d = redi.dict_string('haystack:metadict') 41 | 42 | d.data = {} 43 | 44 | d.data['timestamp'] = datetime.now().isoformat() 45 | d.data['values'] = [12, 82, 248.2] 46 | 47 | # redis: "{\"timestamp\": \"2011-04-09T03:39:03.204597\", \"values\": [ 12, 82, 248.2]}" 48 | 49 | 50 | - **Redis List as a List** :: 51 | 52 | 53 | >>> students = redi.list('haystack:students') 54 | 55 | >>> students[3] 56 | {'first': 'kenneth', 'last': 'unknown'} 57 | 58 | >>> students[3].update(last='reitz') 59 | 60 | >>> list(students.find('reitz')) 61 | [{'first': 'kenneth', 'last': 'reitz'}] 62 | 63 | 64 | 65 | Installation 66 | ------------ 67 | 68 | To install redi, simply: :: 69 | 70 | $ pip install redi 71 | 72 | Or, if you absolutely must: :: 73 | 74 | $ easy_install redi 75 | 76 | 77 | But, you really shouldn't do that. 78 | 79 | 80 | 81 | License 82 | ------- 83 | 84 | The ISC License. :: 85 | 86 | Copyright (c) 2011, Kenneth Reitz 87 | 88 | Permission to use, copy, modify, and/or distribute this software for any 89 | purpose with or without fee is hereby granted, provided that the above 90 | copyright notice and this permission notice appear in all copies. 91 | 92 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 93 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 94 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 95 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 96 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 97 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 98 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 99 | 100 | 101 | Contribute 102 | ---------- 103 | 104 | If you'd like to contribute, simply fork the repository, commit your changes to the develop branch (or branch off of it), and send a pull request. Make sure you add yourself to AUTHORS. 105 | 106 | 107 | 108 | Advanced 109 | -------- 110 | 111 | - The de/serializing interface is completely overridable. You can fully swap JSON out with YAML w/ two lines of code. 112 | - You can use blocking pops with configurable timeouts. 113 | - Namespace aware! 114 | 115 | 116 | Roadmap 117 | ------- 118 | 119 | - Polish 120 | - Add Sets (maybe) 121 | - SHIP IT 122 | - Tests 123 | - Documentation 124 | -------------------------------------------------------------------------------- /test_redi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Tests for Redi.""" 5 | 6 | import unittest 7 | import os 8 | 9 | 10 | import redi 11 | 12 | 13 | 14 | 15 | class RediTestSuite(unittest.TestCase): 16 | """Redi test suite.""" 17 | 18 | def setUp(self): 19 | 20 | redi.config.init(db=DB) 21 | 22 | 23 | def tearDown(self): 24 | 25 | redi.db.flush() 26 | self.assertEqual(len(redi.db.keys('*')), 0) 27 | 28 | 29 | def test_value_as_dict(self): 30 | 31 | a = redi.dict_string('test') 32 | self.assertEqual(a.type, type(None)) 33 | 34 | a.data = {} 35 | self.assertEqual(a.type, redi.models.SubDict) 36 | 37 | a['face'] = 'book' 38 | 39 | self.assertEqual(a['face'], 'book') 40 | self.assertEqual(a.get('face'), 'book') 41 | self.assertEqual(a.data['face'], 'book') 42 | self.assertEqual(a.data.get('face'), 'book') 43 | 44 | del a['face'] 45 | self.assertEqual(a['face'], None) 46 | 47 | a.delete() 48 | self.assertEqual(a.data, None) 49 | 50 | 51 | def test_value_as_list(self): 52 | 53 | a = redi.list_string('test') 54 | 55 | 56 | def test_key_rename_unsafe(self): 57 | 58 | a = redi.string('rename_me') 59 | a.data = 'i am going to be renamed' 60 | success = a.rename('renamed', safe=False) 61 | 62 | b = redi.string('renamed') 63 | c = redi.string('rename_me') 64 | 65 | self.assertTrue(success) 66 | self.assertEqual(a.data, b.data) 67 | self.assertEqual(c.data, None) 68 | 69 | a.delete() 70 | b.delete() 71 | c.delete() 72 | 73 | 74 | def test_key_rename_unsafe_overwrite(self): 75 | 76 | a = redi.string('rename_me') 77 | a.data = 'i am going to be renamed' 78 | 79 | orig_value = a.data 80 | orig_key = a.key 81 | 82 | b = redi.string('exists') 83 | b.data = 'yes i do' 84 | 85 | success = a.rename('exists', safe=False) 86 | 87 | self.assertTrue(success) 88 | self.assertEqual(orig_value, a.data) 89 | self.assertNotEqual(a.key, orig_key) 90 | 91 | a.delete() 92 | b.delete() 93 | 94 | 95 | def test_key_rename_safe(self): 96 | 97 | a = redi.string('rename_me') 98 | a.data = 'i am going to be renamed' 99 | 100 | orig_value = a.data 101 | orig_key = a.key 102 | 103 | success = a.rename('exists', safe=True) 104 | 105 | self.assertTrue(success) 106 | self.assertEqual(orig_value, a.data) 107 | self.assertNotEqual(a.key, orig_key) 108 | 109 | a.delete() 110 | 111 | 112 | def test_key_rename_safe_overwrite(self): 113 | 114 | a = redi.string('rename_me') 115 | a.data = 'i am going to be renamed' 116 | 117 | orig_value = a.data 118 | orig_key = a.key 119 | 120 | b = redi.string('exists') 121 | b.data = 'yes i do' 122 | 123 | success = a.rename('exists', safe=True) 124 | 125 | self.assertFalse(success) 126 | self.assertEqual(orig_value, a.data) 127 | self.assertEqual(a.key, orig_key) 128 | 129 | a.delete() 130 | b.delete() 131 | 132 | 133 | 134 | if __name__ == '__main__': 135 | 136 | DB = os.environ.get('REDI_TEST_DB_NUM', None) 137 | 138 | if DB is None: 139 | raise Exception('REDI_TEST_DB_NUM env must be set.') 140 | 141 | unittest.main() 142 | -------------------------------------------------------------------------------- /docs/_themes/flask_theme_support.py: -------------------------------------------------------------------------------- 1 | # flasky extensions. flasky pygments style based on tango style 2 | from pygments.style import Style 3 | from pygments.token import Keyword, Name, Comment, String, Error, \ 4 | Number, Operator, Generic, Whitespace, Punctuation, Other, Literal 5 | 6 | 7 | class FlaskyStyle(Style): 8 | background_color = "#f8f8f8" 9 | default_style = "" 10 | 11 | styles = { 12 | # No corresponding class for the following: 13 | #Text: "", # class: '' 14 | Whitespace: "underline #f8f8f8", # class: 'w' 15 | Error: "#a40000 border:#ef2929", # class: 'err' 16 | Other: "#000000", # class 'x' 17 | 18 | Comment: "italic #8f5902", # class: 'c' 19 | Comment.Preproc: "noitalic", # class: 'cp' 20 | 21 | Keyword: "bold #004461", # class: 'k' 22 | Keyword.Constant: "bold #004461", # class: 'kc' 23 | Keyword.Declaration: "bold #004461", # class: 'kd' 24 | Keyword.Namespace: "bold #004461", # class: 'kn' 25 | Keyword.Pseudo: "bold #004461", # class: 'kp' 26 | Keyword.Reserved: "bold #004461", # class: 'kr' 27 | Keyword.Type: "bold #004461", # class: 'kt' 28 | 29 | Operator: "#582800", # class: 'o' 30 | Operator.Word: "bold #004461", # class: 'ow' - like keywords 31 | 32 | Punctuation: "bold #000000", # class: 'p' 33 | 34 | # because special names such as Name.Class, Name.Function, etc. 35 | # are not recognized as such later in the parsing, we choose them 36 | # to look the same as ordinary variables. 37 | Name: "#000000", # class: 'n' 38 | Name.Attribute: "#c4a000", # class: 'na' - to be revised 39 | Name.Builtin: "#004461", # class: 'nb' 40 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp' 41 | Name.Class: "#000000", # class: 'nc' - to be revised 42 | Name.Constant: "#000000", # class: 'no' - to be revised 43 | Name.Decorator: "#888", # class: 'nd' - to be revised 44 | Name.Entity: "#ce5c00", # class: 'ni' 45 | Name.Exception: "bold #cc0000", # class: 'ne' 46 | Name.Function: "#000000", # class: 'nf' 47 | Name.Property: "#000000", # class: 'py' 48 | Name.Label: "#f57900", # class: 'nl' 49 | Name.Namespace: "#000000", # class: 'nn' - to be revised 50 | Name.Other: "#000000", # class: 'nx' 51 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword 52 | Name.Variable: "#000000", # class: 'nv' - to be revised 53 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised 54 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised 55 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised 56 | 57 | Number: "#990000", # class: 'm' 58 | 59 | Literal: "#000000", # class: 'l' 60 | Literal.Date: "#000000", # class: 'ld' 61 | 62 | String: "#4e9a06", # class: 's' 63 | String.Backtick: "#4e9a06", # class: 'sb' 64 | String.Char: "#4e9a06", # class: 'sc' 65 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment 66 | String.Double: "#4e9a06", # class: 's2' 67 | String.Escape: "#4e9a06", # class: 'se' 68 | String.Heredoc: "#4e9a06", # class: 'sh' 69 | String.Interpol: "#4e9a06", # class: 'si' 70 | String.Other: "#4e9a06", # class: 'sx' 71 | String.Regex: "#4e9a06", # class: 'sr' 72 | String.Single: "#4e9a06", # class: 's1' 73 | String.Symbol: "#4e9a06", # class: 'ss' 74 | 75 | Generic: "#000000", # class: 'g' 76 | Generic.Deleted: "#a40000", # class: 'gd' 77 | Generic.Emph: "italic #000000", # class: 'ge' 78 | Generic.Error: "#ef2929", # class: 'gr' 79 | Generic.Heading: "bold #000080", # class: 'gh' 80 | Generic.Inserted: "#00A000", # class: 'gi' 81 | Generic.Output: "#888", # class: 'go' 82 | Generic.Prompt: "#745334", # class: 'gp' 83 | Generic.Strong: "bold #000000", # class: 'gs' 84 | Generic.Subheading: "bold #800080", # class: 'gu' 85 | Generic.Traceback: "bold #a40000", # class: 'gt' 86 | } 87 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Redi.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Redi.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Redi" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Redi" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | goto end 41 | ) 42 | 43 | if "%1" == "html" ( 44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 45 | if errorlevel 1 exit /b 1 46 | echo. 47 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 48 | goto end 49 | ) 50 | 51 | if "%1" == "dirhtml" ( 52 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 56 | goto end 57 | ) 58 | 59 | if "%1" == "singlehtml" ( 60 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "pickle" ( 68 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished; now you can process the pickle files. 72 | goto end 73 | ) 74 | 75 | if "%1" == "json" ( 76 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the JSON files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "htmlhelp" ( 84 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can run HTML Help Workshop with the ^ 88 | .hhp project file in %BUILDDIR%/htmlhelp. 89 | goto end 90 | ) 91 | 92 | if "%1" == "qthelp" ( 93 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 97 | .qhcp project file in %BUILDDIR%/qthelp, like this: 98 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Redi.qhcp 99 | echo.To view the help file: 100 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Redi.ghc 101 | goto end 102 | ) 103 | 104 | if "%1" == "devhelp" ( 105 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 106 | if errorlevel 1 exit /b 1 107 | echo. 108 | echo.Build finished. 109 | goto end 110 | ) 111 | 112 | if "%1" == "epub" ( 113 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 117 | goto end 118 | ) 119 | 120 | if "%1" == "latex" ( 121 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 125 | goto end 126 | ) 127 | 128 | if "%1" == "text" ( 129 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished. The text files are in %BUILDDIR%/text. 133 | goto end 134 | ) 135 | 136 | if "%1" == "man" ( 137 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 141 | goto end 142 | ) 143 | 144 | if "%1" == "changes" ( 145 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.The overview file is in %BUILDDIR%/changes. 149 | goto end 150 | ) 151 | 152 | if "%1" == "linkcheck" ( 153 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Link check complete; look for any errors in the above output ^ 157 | or in %BUILDDIR%/linkcheck/output.txt. 158 | goto end 159 | ) 160 | 161 | if "%1" == "doctest" ( 162 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Testing of doctests in the sources finished, look at the ^ 166 | results in %BUILDDIR%/doctest/output.txt. 167 | goto end 168 | ) 169 | 170 | :end 171 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- flasky theme based on nature theme. 6 | * 7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'Georgia', serif; 18 | font-size: 17px; 19 | color: #000; 20 | background: white; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 40px auto 0 auto; 32 | width: 700px; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #ffffff; 41 | color: #3E4349; 42 | padding: 0 30px 30px 30px; 43 | } 44 | 45 | img.floatingflask { 46 | padding: 0 0 10px 10px; 47 | float: right; 48 | } 49 | 50 | div.footer { 51 | text-align: right; 52 | color: #888; 53 | padding: 10px; 54 | font-size: 14px; 55 | width: 650px; 56 | margin: 0 auto 40px auto; 57 | } 58 | 59 | div.footer a { 60 | color: #888; 61 | text-decoration: underline; 62 | } 63 | 64 | div.related { 65 | line-height: 32px; 66 | color: #888; 67 | } 68 | 69 | div.related ul { 70 | padding: 0 0 0 10px; 71 | } 72 | 73 | div.related a { 74 | color: #444; 75 | } 76 | 77 | /* -- body styles ----------------------------------------------------------- */ 78 | 79 | a { 80 | color: #004B6B; 81 | text-decoration: underline; 82 | } 83 | 84 | a:hover { 85 | color: #6D4100; 86 | text-decoration: underline; 87 | } 88 | 89 | div.body { 90 | padding-bottom: 40px; /* saved for footer */ 91 | } 92 | 93 | div.body h1, 94 | div.body h2, 95 | div.body h3, 96 | div.body h4, 97 | div.body h5, 98 | div.body h6 { 99 | font-family: 'Garamond', 'Georgia', serif; 100 | font-weight: normal; 101 | margin: 30px 0px 10px 0px; 102 | padding: 0; 103 | } 104 | 105 | {% if theme_index_logo %} 106 | div.indexwrapper h1 { 107 | text-indent: -999999px; 108 | background: url({{ theme_index_logo }}) no-repeat center center; 109 | height: {{ theme_index_logo_height }}; 110 | } 111 | {% endif %} 112 | 113 | div.body h2 { font-size: 180%; } 114 | div.body h3 { font-size: 150%; } 115 | div.body h4 { font-size: 130%; } 116 | div.body h5 { font-size: 100%; } 117 | div.body h6 { font-size: 100%; } 118 | 119 | a.headerlink { 120 | color: white; 121 | padding: 0 4px; 122 | text-decoration: none; 123 | } 124 | 125 | a.headerlink:hover { 126 | color: #444; 127 | background: #eaeaea; 128 | } 129 | 130 | div.body p, div.body dd, div.body li { 131 | line-height: 1.4em; 132 | } 133 | 134 | div.admonition { 135 | background: #fafafa; 136 | margin: 20px -30px; 137 | padding: 10px 30px; 138 | border-top: 1px solid #ccc; 139 | border-bottom: 1px solid #ccc; 140 | } 141 | 142 | div.admonition p.admonition-title { 143 | font-family: 'Garamond', 'Georgia', serif; 144 | font-weight: normal; 145 | font-size: 24px; 146 | margin: 0 0 10px 0; 147 | padding: 0; 148 | line-height: 1; 149 | } 150 | 151 | div.admonition p.last { 152 | margin-bottom: 0; 153 | } 154 | 155 | div.highlight{ 156 | background-color: white; 157 | } 158 | 159 | dt:target, .highlight { 160 | background: #FAF3E8; 161 | } 162 | 163 | div.note { 164 | background-color: #eee; 165 | border: 1px solid #ccc; 166 | } 167 | 168 | div.seealso { 169 | background-color: #ffc; 170 | border: 1px solid #ff6; 171 | } 172 | 173 | div.topic { 174 | background-color: #eee; 175 | } 176 | 177 | div.warning { 178 | background-color: #ffe4e4; 179 | border: 1px solid #f66; 180 | } 181 | 182 | p.admonition-title { 183 | display: inline; 184 | } 185 | 186 | p.admonition-title:after { 187 | content: ":"; 188 | } 189 | 190 | pre, tt { 191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 192 | font-size: 0.85em; 193 | } 194 | 195 | img.screenshot { 196 | } 197 | 198 | tt.descname, tt.descclassname { 199 | font-size: 0.95em; 200 | } 201 | 202 | tt.descname { 203 | padding-right: 0.08em; 204 | } 205 | 206 | img.screenshot { 207 | -moz-box-shadow: 2px 2px 4px #eee; 208 | -webkit-box-shadow: 2px 2px 4px #eee; 209 | box-shadow: 2px 2px 4px #eee; 210 | } 211 | 212 | table.docutils { 213 | border: 1px solid #888; 214 | -moz-box-shadow: 2px 2px 4px #eee; 215 | -webkit-box-shadow: 2px 2px 4px #eee; 216 | box-shadow: 2px 2px 4px #eee; 217 | } 218 | 219 | table.docutils td, table.docutils th { 220 | border: 1px solid #888; 221 | padding: 0.25em 0.7em; 222 | } 223 | 224 | table.field-list, table.footnote { 225 | border: none; 226 | -moz-box-shadow: none; 227 | -webkit-box-shadow: none; 228 | box-shadow: none; 229 | } 230 | 231 | table.footnote { 232 | margin: 15px 0; 233 | width: 100%; 234 | border: 1px solid #eee; 235 | } 236 | 237 | table.field-list th { 238 | padding: 0 0.8em 0 0; 239 | } 240 | 241 | table.field-list td { 242 | padding: 0; 243 | } 244 | 245 | table.footnote td { 246 | padding: 0.5em; 247 | } 248 | 249 | dl { 250 | margin: 0; 251 | padding: 0; 252 | } 253 | 254 | dl dd { 255 | margin-left: 30px; 256 | } 257 | 258 | pre { 259 | padding: 0; 260 | margin: 15px -30px; 261 | padding: 8px; 262 | line-height: 1.3em; 263 | padding: 7px 30px; 264 | background: #eee; 265 | border-radius: 2px; 266 | -moz-border-radius: 2px; 267 | -webkit-border-radius: 2px; 268 | } 269 | 270 | dl pre { 271 | margin-left: -60px; 272 | padding-left: 60px; 273 | } 274 | 275 | tt { 276 | background-color: #ecf0f3; 277 | color: #222; 278 | /* padding: 1px 2px; */ 279 | } 280 | 281 | tt.xref, a tt { 282 | background-color: #FBFBFB; 283 | } 284 | 285 | a:hover tt { 286 | background: #EEE; 287 | } 288 | -------------------------------------------------------------------------------- /redi/packages/jsonpickle/util.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2008 John Paulett (john -at- paulett.org) 4 | # All rights reserved. 5 | # 6 | # This software is licensed as described in the file COPYING, which 7 | # you should have received as part of this distribution. 8 | 9 | """Helper functions for pickling and unpickling. Most functions assist in 10 | determining the type of an object. 11 | """ 12 | import time 13 | import types 14 | import datetime 15 | 16 | from . import tags 17 | from .compat import set 18 | 19 | COLLECTIONS = set, list, tuple 20 | PRIMITIVES = str, unicode, int, float, bool, long 21 | NEEDS_REPR = (datetime.datetime, datetime.time, datetime.date, 22 | datetime.timedelta) 23 | 24 | def is_type(obj): 25 | """Returns True is obj is a reference to a type. 26 | 27 | >>> is_type(1) 28 | False 29 | 30 | >>> is_type(object) 31 | True 32 | 33 | >>> class Klass: pass 34 | >>> is_type(Klass) 35 | True 36 | """ 37 | #FIXME ">> is_object(1) 46 | True 47 | 48 | >>> is_object(object()) 49 | True 50 | 51 | >>> is_object(lambda x: 1) 52 | False 53 | """ 54 | return (isinstance(obj, object) and 55 | type(obj) is not types.TypeType and 56 | type(obj) is not types.FunctionType) 57 | 58 | def is_primitive(obj): 59 | """Helper method to see if the object is a basic data type. Strings, 60 | integers, longs, floats, booleans, and None are considered primitive 61 | and will return True when passed into *is_primitive()* 62 | 63 | >>> is_primitive(3) 64 | True 65 | >>> is_primitive([4,4]) 66 | False 67 | """ 68 | if obj is None: 69 | return True 70 | elif type(obj) in PRIMITIVES: 71 | return True 72 | return False 73 | 74 | def is_dictionary(obj): 75 | """Helper method for testing if the object is a dictionary. 76 | 77 | >>> is_dictionary({'key':'value'}) 78 | True 79 | """ 80 | return type(obj) is dict 81 | 82 | def is_collection(obj): 83 | """Helper method to see if the object is a Python collection (list, 84 | set, or tuple). 85 | >>> is_collection([4]) 86 | True 87 | """ 88 | return type(obj) in COLLECTIONS 89 | 90 | def is_list(obj): 91 | """Helper method to see if the object is a Python list. 92 | 93 | >>> is_list([4]) 94 | True 95 | """ 96 | return type(obj) is list 97 | 98 | def is_set(obj): 99 | """Helper method to see if the object is a Python set. 100 | 101 | >>> is_set(set()) 102 | True 103 | """ 104 | return type(obj) is set 105 | 106 | def is_tuple(obj): 107 | """Helper method to see if the object is a Python tuple. 108 | 109 | >>> is_tuple((1,)) 110 | True 111 | """ 112 | return type(obj) is tuple 113 | 114 | def is_dictionary_subclass(obj): 115 | """Returns True if *obj* is a subclass of the dict type. *obj* must be 116 | a subclass and not the actual builtin dict. 117 | 118 | >>> class Temp(dict): pass 119 | >>> is_dictionary_subclass(Temp()) 120 | True 121 | """ 122 | return (hasattr(obj, '__class__') and 123 | issubclass(obj.__class__, dict) and not is_dictionary(obj)) 124 | 125 | def is_collection_subclass(obj): 126 | """Returns True if *obj* is a subclass of a collection type, such as list 127 | set, tuple, etc.. *obj* must be a subclass and not the actual builtin, such 128 | as list, set, tuple, etc.. 129 | 130 | >>> class Temp(list): pass 131 | >>> is_collection_subclass(Temp()) 132 | True 133 | """ 134 | #TODO add UserDict 135 | return issubclass(obj.__class__, COLLECTIONS) and not is_collection(obj) 136 | 137 | def is_noncomplex(obj): 138 | """Returns True if *obj* is a special (weird) class, that is complex than 139 | primitive data types, but is not a full object. Including: 140 | 141 | * :class:`~time.struct_time` 142 | """ 143 | if type(obj) is time.struct_time: 144 | return True 145 | return False 146 | 147 | def is_repr(obj): 148 | """Returns True if the *obj* must be encoded and decoded using the 149 | :func:`repr` function. Including: 150 | 151 | * :class:`~datetime.datetime` 152 | * :class:`~datetime.date` 153 | * :class:`~datetime.time` 154 | * :class:`~datetime.timedelta` 155 | """ 156 | return isinstance(obj, NEEDS_REPR) 157 | 158 | def is_function(obj): 159 | """Returns true if passed a function 160 | 161 | >>> is_function(lambda x: 1) 162 | True 163 | 164 | >>> is_function(locals) 165 | True 166 | 167 | >>> def method(): pass 168 | >>> is_function(method) 169 | True 170 | 171 | >>> is_function(1) 172 | False 173 | """ 174 | if type(obj) is types.FunctionType: 175 | return True 176 | if not is_object(obj): 177 | return False 178 | if not hasattr(obj, '__class__'): 179 | return False 180 | module = obj.__class__.__module__ 181 | name = obj.__class__.__name__ 182 | return (module == '__builtin__' and 183 | name in ('function', 184 | 'builtin_function_or_method', 185 | 'instancemethod', 186 | 'method-wrapper')) 187 | 188 | def is_module(obj): 189 | """Returns True if passed a module 190 | 191 | >>> import os 192 | >>> is_module(os) 193 | True 194 | 195 | """ 196 | return type(obj) is types.ModuleType 197 | 198 | def is_picklable(name, value): 199 | """Return True if an object cannot be pickled 200 | 201 | >>> import os 202 | >>> is_picklable('os', os) 203 | True 204 | 205 | >>> def foo(): pass 206 | >>> is_picklable('foo', foo) 207 | False 208 | 209 | """ 210 | if name in tags.RESERVED: 211 | return False 212 | return not is_function(value) 213 | -------------------------------------------------------------------------------- /redi/packages/jsonpickle/unpickler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2008 John Paulett (john -at- paulett.org) 4 | # All rights reserved. 5 | # 6 | # This software is licensed as described in the file COPYING, which 7 | # you should have received as part of this distribution. 8 | 9 | import sys 10 | from . import util as util 11 | from . import tags as tags 12 | from . import handlers as handlers 13 | from .compat import set 14 | 15 | 16 | class Unpickler(object): 17 | def __init__(self): 18 | ## The current recursion depth 19 | self._depth = 0 20 | ## Maps reference names to object instances 21 | self._namedict = {} 22 | ## The namestack grows whenever we recurse into a child object 23 | self._namestack = [] 24 | 25 | def _reset(self): 26 | """Resets the object's internal state. 27 | """ 28 | self._namedict = {} 29 | self._namestack = [] 30 | 31 | def _push(self): 32 | """Steps down one level in the namespace. 33 | """ 34 | self._depth += 1 35 | 36 | def _pop(self, value): 37 | """Step up one level in the namespace and return the value. 38 | If we're at the root, reset the unpickler's state. 39 | """ 40 | self._depth -= 1 41 | if self._depth == 0: 42 | self._reset() 43 | return value 44 | 45 | def restore(self, obj): 46 | """Restores a flattened object to its original python state. 47 | 48 | Simply returns any of the basic builtin types 49 | 50 | >>> u = Unpickler() 51 | >>> u.restore('hello world') 52 | 'hello world' 53 | >>> u.restore({'key': 'value'}) 54 | {'key': 'value'} 55 | """ 56 | self._push() 57 | 58 | if has_tag(obj, tags.REF): 59 | return self._pop(self._namedict.get(obj[tags.REF])) 60 | 61 | if has_tag(obj, tags.TYPE): 62 | typeref = loadclass(obj[tags.TYPE]) 63 | if not typeref: 64 | return self._pop(obj) 65 | return self._pop(typeref) 66 | 67 | if has_tag(obj, tags.REPR): 68 | return self._pop(loadrepr(obj[tags.REPR])) 69 | 70 | if has_tag(obj, tags.OBJECT): 71 | 72 | cls = loadclass(obj[tags.OBJECT]) 73 | if not cls: 74 | return self._pop(obj) 75 | 76 | # check custom handlers 77 | HandlerClass = handlers.registry.get(cls) 78 | if HandlerClass: 79 | handler = HandlerClass(self) 80 | return self._pop(handler.restore(obj)) 81 | 82 | try: 83 | instance = object.__new__(cls) 84 | except TypeError: 85 | # old-style classes 86 | try: 87 | instance = cls() 88 | except TypeError: 89 | # fail gracefully if the constructor requires arguments 90 | self._mkref(obj) 91 | return self._pop(obj) 92 | 93 | # keep a obj->name mapping for use in the _isobjref() case 94 | self._mkref(instance) 95 | 96 | if hasattr(instance, '__setstate__') and has_tag(obj, tags.STATE): 97 | state = self.restore(obj[tags.STATE]) 98 | instance.__setstate__(state) 99 | return self._pop(instance) 100 | 101 | for k, v in obj.iteritems(): 102 | # ignore the reserved attribute 103 | if k in tags.RESERVED: 104 | continue 105 | self._namestack.append(k) 106 | # step into the namespace 107 | value = self.restore(v) 108 | if (util.is_noncomplex(instance) or 109 | util.is_dictionary_subclass(instance)): 110 | instance[k] = value 111 | else: 112 | setattr(instance, k, value) 113 | # step out 114 | self._namestack.pop() 115 | 116 | # Handle list and set subclasses 117 | if has_tag(obj, tags.SEQ): 118 | if hasattr(instance, 'append'): 119 | for v in obj[tags.SEQ]: 120 | instance.append(self.restore(v)) 121 | if hasattr(instance, 'add'): 122 | for v in obj[tags.SEQ]: 123 | instance.add(self.restore(v)) 124 | 125 | return self._pop(instance) 126 | 127 | if util.is_list(obj): 128 | return self._pop([self.restore(v) for v in obj]) 129 | 130 | if has_tag(obj, tags.TUPLE): 131 | return self._pop(tuple([self.restore(v) for v in obj[tags.TUPLE]])) 132 | 133 | if has_tag(obj, tags.SET): 134 | return self._pop(set([self.restore(v) for v in obj[tags.SET]])) 135 | 136 | if util.is_dictionary(obj): 137 | data = {} 138 | for k, v in obj.iteritems(): 139 | self._namestack.append(k) 140 | data[k] = self.restore(v) 141 | self._namestack.pop() 142 | return self._pop(data) 143 | 144 | return self._pop(obj) 145 | 146 | def _refname(self): 147 | """Calculates the name of the current location in the JSON stack. 148 | 149 | This is called as jsonpickle traverses the object structure to 150 | create references to previously-traversed objects. This allows 151 | cyclical data structures such as doubly-linked lists. 152 | jsonpickle ensures that duplicate python references to the same 153 | object results in only a single JSON object definition and 154 | special reference tags to represent each reference. 155 | 156 | >>> u = Unpickler() 157 | >>> u._namestack = [] 158 | >>> u._refname() 159 | '/' 160 | 161 | >>> u._namestack = ['a'] 162 | >>> u._refname() 163 | '/a' 164 | 165 | >>> u._namestack = ['a', 'b'] 166 | >>> u._refname() 167 | '/a/b' 168 | 169 | """ 170 | return '/' + '/'.join(self._namestack) 171 | 172 | def _mkref(self, obj): 173 | """ 174 | >>> from samples import Thing 175 | >>> thing = Thing('referenced-thing') 176 | >>> u = Unpickler() 177 | >>> u._mkref(thing) 178 | '/' 179 | >>> u._namedict['/'] 180 | samples.Thing("referenced-thing") 181 | 182 | """ 183 | name = self._refname() 184 | if name not in self._namedict: 185 | self._namedict[name] = obj 186 | return name 187 | 188 | def loadclass(module_and_name): 189 | """Loads the module and returns the class. 190 | 191 | >>> loadclass('samples.Thing') 192 | 193 | 194 | >>> loadclass('example.module.does.not.exist.Missing') 195 | 196 | 197 | >>> loadclass('samples.MissingThing') 198 | 199 | 200 | """ 201 | try: 202 | module, name = module_and_name.rsplit('.', 1) 203 | __import__(module) 204 | return getattr(sys.modules[module], name) 205 | except: 206 | return None 207 | 208 | def loadrepr(reprstr): 209 | """Returns an instance of the object from the object's repr() string. 210 | It involves the dynamic specification of code. 211 | 212 | >>> from jsonpickle import tags 213 | >>> loadrepr('samples/samples.Thing("json")') 214 | samples.Thing("json") 215 | 216 | """ 217 | module, evalstr = reprstr.split('/') 218 | mylocals = locals() 219 | localname = module 220 | if '.' in localname: 221 | localname = module.split('.', 1)[0] 222 | mylocals[localname] = __import__(module) 223 | return eval(evalstr) 224 | 225 | def has_tag(obj, tag): 226 | """Helper class that tests to see if the obj is a dictionary 227 | and contains a particular key/tag. 228 | 229 | >>> obj = {'test': 1} 230 | >>> has_tag(obj, 'test') 231 | True 232 | >>> has_tag(obj, 'fail') 233 | False 234 | 235 | >>> has_tag(42, 'fail') 236 | False 237 | 238 | """ 239 | return type(obj) is dict and tag in obj 240 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Redi documentation build configuration file, created by 4 | # sphinx-quickstart on Sat Apr 9 18:35:16 2011. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | sys.path.insert(0, os.path.abspath('..')) 20 | import redi 21 | 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # If your documentation needs a minimal Sphinx version, state it here. 25 | #needs_sphinx = '1.0' 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8-sig' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'Redi' 45 | copyright = u'2011, Kenneth Reitz. Styles (modified) © Armin Ronacher' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | # The short X.Y version. 52 | version = '0.0.3' 53 | # The full version, including alpha/beta/rc tags. 54 | release = version 55 | 56 | # The language for content autogenerated by Sphinx. Refer to documentation 57 | # for a list of supported languages. 58 | #language = None 59 | 60 | # There are two options for replacing |today|: either, you set today to some 61 | # non-false value, then it is used: 62 | #today = '' 63 | # Else, today_fmt is used as the format for a strftime call. 64 | #today_fmt = '%B %d, %Y' 65 | 66 | # List of patterns, relative to source directory, that match files and 67 | # directories to ignore when looking for source files. 68 | exclude_patterns = ['_build'] 69 | 70 | # The reST default role (used for this markup: `text`) to use for all documents. 71 | #default_role = None 72 | 73 | # If true, '()' will be appended to :func: etc. cross-reference text. 74 | #add_function_parentheses = True 75 | 76 | # If true, the current module name will be prepended to all description 77 | # unit titles (such as .. function::). 78 | #add_module_names = True 79 | 80 | # If true, sectionauthor and moduleauthor directives will be shown in the 81 | # output. They are ignored by default. 82 | #show_authors = False 83 | 84 | # The name of the Pygments (syntax highlighting) style to use. 85 | pygments_style = 'flask_theme_support.FlaskyStyle' 86 | 87 | # A list of ignored prefixes for module index sorting. 88 | #modindex_common_prefix = [] 89 | 90 | 91 | # -- Options for HTML output --------------------------------------------------- 92 | 93 | # The theme to use for HTML and HTML Help pages. See the documentation for 94 | # a list of builtin themes. 95 | # html_theme = 'default' 96 | 97 | # Theme options are theme-specific and customize the look and feel of a theme 98 | # further. For a list of options available for each theme, see the 99 | # documentation. 100 | #html_theme_options = {} 101 | 102 | # Add any paths that contain custom themes here, relative to this directory. 103 | #html_theme_path = [] 104 | 105 | # The name for this set of Sphinx documents. If None, it defaults to 106 | # " v documentation". 107 | #html_title = None 108 | 109 | # A shorter title for the navigation bar. Default is the same as html_title. 110 | #html_short_title = None 111 | 112 | # The name of an image file (relative to this directory) to place at the top 113 | # of the sidebar. 114 | #html_logo = None 115 | 116 | # The name of an image file (within the static path) to use as favicon of the 117 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 118 | # pixels large. 119 | #html_favicon = None 120 | 121 | # Add any paths that contain custom static files (such as style sheets) here, 122 | # relative to this directory. They are copied after the builtin static files, 123 | # so a file named "default.css" will overwrite the builtin "default.css". 124 | html_static_path = ['static'] 125 | 126 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 127 | # using the given strftime format. 128 | #html_last_updated_fmt = '%b %d, %Y' 129 | 130 | # If true, SmartyPants will be used to convert quotes and dashes to 131 | # typographically correct entities. 132 | #html_use_smartypants = True 133 | 134 | # Custom sidebar templates, maps document names to template names. 135 | #html_sidebars = {} 136 | 137 | # Additional templates that should be rendered to pages, maps page names to 138 | # template names. 139 | #html_additional_pages = {} 140 | 141 | # If false, no module index is generated. 142 | #html_domain_indices = True 143 | 144 | # If false, no index is generated. 145 | #html_use_index = True 146 | 147 | # If true, the index is split into individual pages for each letter. 148 | #html_split_index = False 149 | 150 | # If true, links to the reST sources are added to the pages. 151 | #html_show_sourcelink = True 152 | 153 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 154 | #html_show_sphinx = True 155 | 156 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 157 | #html_show_copyright = True 158 | 159 | # If true, an OpenSearch description file will be output, and all pages will 160 | # contain a tag referring to it. The value of this option must be the 161 | # base URL from which the finished HTML is served. 162 | #html_use_opensearch = '' 163 | 164 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 165 | #html_file_suffix = None 166 | 167 | # Output file base name for HTML help builder. 168 | htmlhelp_basename = 'Redidoc' 169 | 170 | 171 | # -- Options for LaTeX output -------------------------------------------------- 172 | 173 | # The paper size ('letter' or 'a4'). 174 | #latex_paper_size = 'letter' 175 | 176 | # The font size ('10pt', '11pt' or '12pt'). 177 | #latex_font_size = '10pt' 178 | 179 | # Grouping the document tree into LaTeX files. List of tuples 180 | # (source start file, target name, title, author, documentclass [howto/manual]). 181 | latex_documents = [ 182 | ('index', 'Redi.tex', u'Redi Documentation', 183 | u'Kenneth Reitz', 'manual'), 184 | ] 185 | 186 | # The name of an image file (relative to this directory) to place at the top of 187 | # the title page. 188 | #latex_logo = None 189 | 190 | # For "manual" documents, if this is true, then toplevel headings are parts, 191 | # not chapters. 192 | #latex_use_parts = False 193 | 194 | # If true, show page references after internal links. 195 | #latex_show_pagerefs = False 196 | 197 | # If true, show URL addresses after external links. 198 | #latex_show_urls = False 199 | 200 | # Additional stuff for the LaTeX preamble. 201 | #latex_preamble = '' 202 | 203 | # Documents to append as an appendix to all manuals. 204 | #latex_appendices = [] 205 | 206 | # If false, no module index is generated. 207 | #latex_domain_indices = True 208 | 209 | 210 | # -- Options for manual page output -------------------------------------------- 211 | 212 | # One entry per manual page. List of tuples 213 | # (source start file, name, description, authors, manual section). 214 | man_pages = [ 215 | ('index', 'redi', u'Redi Documentation', 216 | [u'Kenneth Reitz'], 1) 217 | ] 218 | 219 | sys.path.append(os.path.abspath('_themes')) 220 | html_theme_path = ['_themes'] 221 | html_theme = 'kr' -------------------------------------------------------------------------------- /docs/_themes/kr/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz. 6 | * :license: Flask Design License, see LICENSE for details. 7 | */ 8 | 9 | {% set page_width = '940px' %} 10 | {% set sidebar_width = '220px' %} 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro'; 18 | font-size: 17px; 19 | background-color: white; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | width: {{ page_width }}; 27 | margin: 30px auto 0 auto; 28 | } 29 | 30 | div.documentwrapper { 31 | float: left; 32 | width: 100%; 33 | } 34 | 35 | div.bodywrapper { 36 | margin: 0 0 0 {{ sidebar_width }}; 37 | } 38 | 39 | div.sphinxsidebar { 40 | width: {{ sidebar_width }}; 41 | } 42 | 43 | hr { 44 | border: 1px solid #B1B4B6; 45 | } 46 | 47 | div.body { 48 | background-color: #ffffff; 49 | color: #3E4349; 50 | padding: 0 30px 0 30px; 51 | } 52 | 53 | img.floatingflask { 54 | padding: 0 0 10px 10px; 55 | float: right; 56 | } 57 | 58 | div.footer { 59 | width: {{ page_width }}; 60 | margin: 20px auto 30px auto; 61 | font-size: 14px; 62 | color: #888; 63 | text-align: right; 64 | } 65 | 66 | div.footer a { 67 | color: #888; 68 | } 69 | 70 | div.related { 71 | display: none; 72 | } 73 | 74 | div.sphinxsidebar a { 75 | color: #444; 76 | text-decoration: none; 77 | border-bottom: 1px dotted #999; 78 | } 79 | 80 | div.sphinxsidebar a:hover { 81 | border-bottom: 1px solid #999; 82 | } 83 | 84 | div.sphinxsidebar { 85 | font-size: 14px; 86 | line-height: 1.5; 87 | } 88 | 89 | div.sphinxsidebarwrapper { 90 | padding: 18px 10px; 91 | } 92 | 93 | div.sphinxsidebarwrapper p.logo { 94 | padding: 0 0 20px 0; 95 | margin: 0; 96 | text-align: center; 97 | } 98 | 99 | div.sphinxsidebar h3, 100 | div.sphinxsidebar h4 { 101 | font-family: 'Garamond', 'Georgia', serif; 102 | color: #444; 103 | font-size: 24px; 104 | font-weight: normal; 105 | margin: 0 0 5px 0; 106 | padding: 0; 107 | } 108 | 109 | div.sphinxsidebar h4 { 110 | font-size: 20px; 111 | } 112 | 113 | div.sphinxsidebar h3 a { 114 | color: #444; 115 | } 116 | 117 | div.sphinxsidebar p.logo a, 118 | div.sphinxsidebar h3 a, 119 | div.sphinxsidebar p.logo a:hover, 120 | div.sphinxsidebar h3 a:hover { 121 | border: none; 122 | } 123 | 124 | div.sphinxsidebar p { 125 | color: #555; 126 | margin: 10px 0; 127 | } 128 | 129 | div.sphinxsidebar ul { 130 | margin: 10px 0; 131 | padding: 0; 132 | color: #000; 133 | } 134 | 135 | div.sphinxsidebar input { 136 | border: 1px solid #ccc; 137 | font-family: 'Georgia', serif; 138 | font-size: 1em; 139 | } 140 | 141 | /* -- body styles ----------------------------------------------------------- */ 142 | 143 | a { 144 | color: #004B6B; 145 | text-decoration: underline; 146 | } 147 | 148 | a:hover { 149 | color: #6D4100; 150 | text-decoration: underline; 151 | } 152 | 153 | div.body h1, 154 | div.body h2, 155 | div.body h3, 156 | div.body h4, 157 | div.body h5, 158 | div.body h6 { 159 | font-family: 'Garamond', 'Georgia', serif; 160 | font-weight: normal; 161 | margin: 30px 0px 10px 0px; 162 | padding: 0; 163 | } 164 | 165 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 166 | div.body h2 { font-size: 180%; } 167 | div.body h3 { font-size: 150%; } 168 | div.body h4 { font-size: 130%; } 169 | div.body h5 { font-size: 100%; } 170 | div.body h6 { font-size: 100%; } 171 | 172 | a.headerlink { 173 | color: #ddd; 174 | padding: 0 4px; 175 | text-decoration: none; 176 | } 177 | 178 | a.headerlink:hover { 179 | color: #444; 180 | background: #eaeaea; 181 | } 182 | 183 | div.body p, div.body dd, div.body li { 184 | line-height: 1.4em; 185 | } 186 | 187 | div.admonition { 188 | background: #fafafa; 189 | margin: 20px -30px; 190 | padding: 10px 30px; 191 | border-top: 1px solid #ccc; 192 | border-bottom: 1px solid #ccc; 193 | } 194 | 195 | div.admonition tt.xref, div.admonition a tt { 196 | border-bottom: 1px solid #fafafa; 197 | } 198 | 199 | dd div.admonition { 200 | margin-left: -60px; 201 | padding-left: 60px; 202 | } 203 | 204 | div.admonition p.admonition-title { 205 | font-family: 'Garamond', 'Georgia', serif; 206 | font-weight: normal; 207 | font-size: 24px; 208 | margin: 0 0 10px 0; 209 | padding: 0; 210 | line-height: 1; 211 | } 212 | 213 | div.admonition p.last { 214 | margin-bottom: 0; 215 | } 216 | 217 | div.highlight { 218 | background-color: white; 219 | } 220 | 221 | dt:target, .highlight { 222 | background: #FAF3E8; 223 | } 224 | 225 | div.note { 226 | background-color: #eee; 227 | border: 1px solid #ccc; 228 | } 229 | 230 | div.seealso { 231 | background-color: #ffc; 232 | border: 1px solid #ff6; 233 | } 234 | 235 | div.topic { 236 | background-color: #eee; 237 | } 238 | 239 | p.admonition-title { 240 | display: inline; 241 | } 242 | 243 | p.admonition-title:after { 244 | content: ":"; 245 | } 246 | 247 | pre, tt { 248 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 249 | font-size: 0.9em; 250 | } 251 | 252 | img.screenshot { 253 | } 254 | 255 | tt.descname, tt.descclassname { 256 | font-size: 0.95em; 257 | } 258 | 259 | tt.descname { 260 | padding-right: 0.08em; 261 | } 262 | 263 | img.screenshot { 264 | -moz-box-shadow: 2px 2px 4px #eee; 265 | -webkit-box-shadow: 2px 2px 4px #eee; 266 | box-shadow: 2px 2px 4px #eee; 267 | } 268 | 269 | table.docutils { 270 | border: 1px solid #888; 271 | -moz-box-shadow: 2px 2px 4px #eee; 272 | -webkit-box-shadow: 2px 2px 4px #eee; 273 | box-shadow: 2px 2px 4px #eee; 274 | } 275 | 276 | table.docutils td, table.docutils th { 277 | border: 1px solid #888; 278 | padding: 0.25em 0.7em; 279 | } 280 | 281 | table.field-list, table.footnote { 282 | border: none; 283 | -moz-box-shadow: none; 284 | -webkit-box-shadow: none; 285 | box-shadow: none; 286 | } 287 | 288 | table.footnote { 289 | margin: 15px 0; 290 | width: 100%; 291 | border: 1px solid #eee; 292 | background: #fdfdfd; 293 | font-size: 0.9em; 294 | } 295 | 296 | table.footnote + table.footnote { 297 | margin-top: -15px; 298 | border-top: none; 299 | } 300 | 301 | table.field-list th { 302 | padding: 0 0.8em 0 0; 303 | } 304 | 305 | table.field-list td { 306 | padding: 0; 307 | } 308 | 309 | table.footnote td.label { 310 | width: 0px; 311 | padding: 0.3em 0 0.3em 0.5em; 312 | } 313 | 314 | table.footnote td { 315 | padding: 0.3em 0.5em; 316 | } 317 | 318 | dl { 319 | margin: 0; 320 | padding: 0; 321 | } 322 | 323 | dl dd { 324 | margin-left: 30px; 325 | } 326 | 327 | blockquote { 328 | margin: 0 0 0 30px; 329 | padding: 0; 330 | } 331 | 332 | ul, ol { 333 | margin: 10px 0 10px 30px; 334 | padding: 0; 335 | } 336 | 337 | pre { 338 | background: #eee; 339 | padding: 7px 30px; 340 | margin: 15px -30px; 341 | line-height: 1.3em; 342 | } 343 | 344 | dl pre, blockquote pre, li pre { 345 | margin-left: -60px; 346 | padding-left: 60px; 347 | } 348 | 349 | dl dl pre { 350 | margin-left: -90px; 351 | padding-left: 90px; 352 | } 353 | 354 | tt { 355 | background-color: #ecf0f3; 356 | color: #222; 357 | /* padding: 1px 2px; */ 358 | } 359 | 360 | tt.xref, a tt { 361 | background-color: #FBFBFB; 362 | border-bottom: 1px solid white; 363 | } 364 | 365 | a.reference { 366 | text-decoration: none; 367 | border-bottom: 1px dotted #004B6B; 368 | } 369 | 370 | a.reference:hover { 371 | border-bottom: 1px solid #6D4100; 372 | } 373 | 374 | a.footnote-reference { 375 | text-decoration: none; 376 | font-size: 0.7em; 377 | vertical-align: top; 378 | border-bottom: 1px dotted #004B6B; 379 | } 380 | 381 | a.footnote-reference:hover { 382 | border-bottom: 1px solid #6D4100; 383 | } 384 | 385 | a:hover tt { 386 | background: #EEE; 387 | } 388 | 389 | 390 | @media screen and (max-width: 600px) { 391 | 392 | div.sphinxsidebar { 393 | display: none; 394 | } 395 | 396 | div.documentwrapper { 397 | margin-left: 0; 398 | margin-top: 0; 399 | margin-right: 0; 400 | margin-bottom: 0; 401 | } 402 | 403 | div.bodywrapper { 404 | margin-top: 0; 405 | margin-right: 0; 406 | margin-bottom: 0; 407 | margin-left: 0; 408 | } 409 | 410 | ul { 411 | margin-left: 0; 412 | } 413 | 414 | .document { 415 | width: auto; 416 | } 417 | 418 | .bodywrapper { 419 | margin: 0; 420 | } 421 | 422 | .footer { 423 | width: auto; 424 | } 425 | 426 | 427 | 428 | } 429 | 430 | 431 | -------------------------------------------------------------------------------- /redi/packages/jsonpickle/pickler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2008 John Paulett (john -at- paulett.org) 4 | # All rights reserved. 5 | # 6 | # This software is licensed as described in the file COPYING, which 7 | # you should have received as part of this distribution. 8 | import types 9 | from . import util as util 10 | from . import tags as tags 11 | from . import handlers as handlers 12 | 13 | 14 | class Pickler(object): 15 | """Converts a Python object to a JSON representation. 16 | 17 | Setting unpicklable to False removes the ability to regenerate 18 | the objects into object types beyond what the standard 19 | simplejson library supports. 20 | 21 | Setting max_depth to a negative number means there is no 22 | limit to the depth jsonpickle should recurse into an 23 | object. Setting it to zero or higher places a hard limit 24 | on how deep jsonpickle recurses into objects, dictionaries, etc. 25 | 26 | >>> p = Pickler() 27 | >>> p.flatten('hello world') 28 | 'hello world' 29 | """ 30 | 31 | def __init__(self, unpicklable=True, max_depth=None): 32 | self.unpicklable = unpicklable 33 | ## The current recursion depth 34 | self._depth = -1 35 | ## The maximal recursion depth 36 | self._max_depth = max_depth 37 | ## Maps id(obj) to reference names 38 | self._objs = {} 39 | ## The namestack grows whenever we recurse into a child object 40 | self._namestack = [] 41 | 42 | def _reset(self): 43 | self._objs = {} 44 | self._namestack = [] 45 | 46 | def _push(self): 47 | """Steps down one level in the namespace. 48 | """ 49 | self._depth += 1 50 | 51 | def _pop(self, value): 52 | """Step up one level in the namespace and return the value. 53 | If we're at the root, reset the pickler's state. 54 | """ 55 | self._depth -= 1 56 | if self._depth == -1: 57 | self._reset() 58 | return value 59 | 60 | def _mkref(self, obj): 61 | objid = id(obj) 62 | if objid not in self._objs: 63 | self._objs[objid] = '/' + '/'.join(self._namestack) 64 | return True 65 | return False 66 | 67 | def _getref(self, obj): 68 | return {tags.REF: self._objs.get(id(obj))} 69 | 70 | def flatten(self, obj): 71 | """Takes an object and returns a JSON-safe representation of it. 72 | 73 | Simply returns any of the basic builtin datatypes 74 | 75 | >>> p = Pickler() 76 | >>> p.flatten('hello world') 77 | 'hello world' 78 | >>> p.flatten(u'hello world') 79 | u'hello world' 80 | >>> p.flatten(49) 81 | 49 82 | >>> p.flatten(350.0) 83 | 350.0 84 | >>> p.flatten(True) 85 | True 86 | >>> p.flatten(False) 87 | False 88 | >>> r = p.flatten(None) 89 | >>> r is None 90 | True 91 | >>> p.flatten(False) 92 | False 93 | >>> p.flatten([1, 2, 3, 4]) 94 | [1, 2, 3, 4] 95 | >>> p.flatten((1,2,))[tags.TUPLE] 96 | [1, 2] 97 | >>> p.flatten({'key': 'value'}) 98 | {'key': 'value'} 99 | """ 100 | 101 | self._push() 102 | 103 | if self._depth == self._max_depth: 104 | return self._pop(repr(obj)) 105 | 106 | if util.is_primitive(obj): 107 | return self._pop(obj) 108 | 109 | if util.is_list(obj): 110 | return self._pop([ self.flatten(v) for v in obj ]) 111 | 112 | # We handle tuples and sets by encoding them in a "(tuple|set)dict" 113 | if util.is_tuple(obj): 114 | return self._pop({tags.TUPLE: [ self.flatten(v) for v in obj ]}) 115 | 116 | if util.is_set(obj): 117 | return self._pop({tags.SET: [ self.flatten(v) for v in obj ]}) 118 | 119 | if util.is_dictionary(obj): 120 | return self._pop(self._flatten_dict_obj(obj, obj.__class__())) 121 | 122 | if util.is_type(obj): 123 | return self._pop(_mktyperef(obj)) 124 | 125 | if util.is_object(obj): 126 | if self._mkref(obj): 127 | # We've never seen this object so return its 128 | # json representation. 129 | return self._pop(self._flatten_obj_instance(obj)) 130 | else: 131 | # We've seen this object before so place an object 132 | # reference tag in the data. This avoids infinite recursion 133 | # when processing cyclical objects. 134 | return self._pop(self._getref(obj)) 135 | 136 | return self._pop(data) 137 | # else, what else? (methods, functions, old style classes...) 138 | 139 | def _flatten_obj_instance(self, obj): 140 | """Recursively flatten an instance and return a json-friendly dict 141 | """ 142 | data = {} 143 | has_class = hasattr(obj, '__class__') 144 | has_dict = hasattr(obj, '__dict__') 145 | has_slots = not has_dict and hasattr(obj, '__slots__') 146 | has_getstate = has_dict and hasattr(obj, '__getstate__') 147 | has_getstate_support = has_getstate and hasattr(obj, '__setstate__') 148 | HandlerClass = handlers.registry.get(type(obj)) 149 | 150 | if (has_class and not util.is_repr(obj) and 151 | not util.is_module(obj)): 152 | module, name = _getclassdetail(obj) 153 | if self.unpicklable is True: 154 | data[tags.OBJECT] = '%s.%s' % (module, name) 155 | # Check for a custom handler 156 | if HandlerClass: 157 | handler = HandlerClass(self) 158 | return handler.flatten(obj, data) 159 | 160 | if util.is_module(obj): 161 | if self.unpicklable is True: 162 | data[tags.REPR] = '%s/%s' % (obj.__name__, 163 | obj.__name__) 164 | else: 165 | data = unicode(obj) 166 | return data 167 | 168 | if util.is_repr(obj): 169 | if self.unpicklable is True: 170 | data[tags.REPR] = '%s/%s' % (obj.__class__.__module__, 171 | repr(obj)) 172 | else: 173 | data = unicode(obj) 174 | return data 175 | 176 | if util.is_dictionary_subclass(obj): 177 | return self._flatten_dict_obj(obj, data) 178 | 179 | if util.is_noncomplex(obj): 180 | return [self.flatten(v) for v in obj] 181 | 182 | if has_dict: 183 | # Support objects that subclasses list and set 184 | if util.is_collection_subclass(obj): 185 | return self._flatten_collection_obj(obj, data) 186 | 187 | # Support objects with __getstate__(); this ensures that 188 | # both __setstate__() and __getstate__() are implemented 189 | if has_getstate_support: 190 | data[tags.STATE] = self.flatten(obj.__getstate__()) 191 | return data 192 | 193 | # hack for zope persistent objects; this unghostifies the object 194 | getattr(obj, '_', None) 195 | return self._flatten_dict_obj(obj.__dict__, data) 196 | 197 | if has_slots: 198 | return self._flatten_newstyle_with_slots(obj, data) 199 | 200 | def _flatten_dict_obj(self, obj, data): 201 | """Recursively call flatten() and return json-friendly dict 202 | """ 203 | for k, v in obj.iteritems(): 204 | self._flatten_key_value_pair(k, v, data) 205 | return data 206 | 207 | def _flatten_newstyle_with_slots(self, obj, data): 208 | """Return a json-friendly dict for new-style objects with __slots__. 209 | """ 210 | for k in obj.__slots__: 211 | self._flatten_key_value_pair(k, getattr(obj, k), data) 212 | return data 213 | 214 | def _flatten_key_value_pair(self, k, v, data): 215 | """Flatten a key/value pair into the passed-in dictionary.""" 216 | if not util.is_picklable(k, v): 217 | return data 218 | if type(k) not in types.StringTypes: 219 | try: 220 | k = repr(k) 221 | except: 222 | k = unicode(k) 223 | self._namestack.append(k) 224 | data[k] = self.flatten(v) 225 | self._namestack.pop() 226 | return data 227 | 228 | def _flatten_collection_obj(self, obj, data): 229 | """Return a json-friendly dict for a collection subclass.""" 230 | self._flatten_dict_obj(obj.__dict__, data) 231 | data[tags.SEQ] = [ self.flatten(v) for v in obj ] 232 | return data 233 | 234 | def _mktyperef(obj): 235 | """Return a typeref dictionary. Used for references. 236 | 237 | >>> from jsonpickle import tags 238 | >>> _mktyperef(AssertionError)[tags.TYPE].rsplit('.', 1)[0] 239 | 'exceptions' 240 | 241 | >>> _mktyperef(AssertionError)[tags.TYPE].rsplit('.', 1)[-1] 242 | 'AssertionError' 243 | """ 244 | return {tags.TYPE: '%s.%s' % (obj.__module__, obj.__name__)} 245 | 246 | def _getclassdetail(obj): 247 | """Helper class to return the class of an object. 248 | 249 | >>> class Example(object): pass 250 | >>> _getclassdetail(Example()) 251 | ('jsonpickle.pickler', 'Example') 252 | >>> _getclassdetail(25) 253 | ('__builtin__', 'int') 254 | >>> _getclassdetail(None) 255 | ('__builtin__', 'NoneType') 256 | >>> _getclassdetail(False) 257 | ('__builtin__', 'bool') 258 | """ 259 | cls = obj.__class__ 260 | module = getattr(cls, '__module__') 261 | name = getattr(cls, '__name__') 262 | return module, name 263 | -------------------------------------------------------------------------------- /redi/utils.py: -------------------------------------------------------------------------------- 1 | ## {{{ http://code.activestate.com/recipes/440656/ (r7) 2 | import copy 3 | import sys 4 | 5 | from . import config 6 | 7 | 8 | 9 | def compress_key(key): 10 | """Compresses tupled key to string.""" 11 | 12 | if is_collection(key): 13 | key = config.namespace_delimiter.join(key) 14 | 15 | return key 16 | 17 | 18 | def expand_key(key): 19 | """Expands stringed keys to tuples.""" 20 | 21 | if is_collection(key): 22 | return key 23 | 24 | return key.split(config.namespace_delimiter) 25 | 26 | 27 | 28 | def is_collection(obj): 29 | """Tests if an object is a collection. Strings don't count.""" 30 | 31 | if isinstance(obj, basestring): 32 | return False 33 | 34 | return hasattr(obj, '__getitem__') 35 | 36 | 37 | 38 | # Public domain 39 | class ListMixin(object): 40 | """ 41 | Defines all list operations from a small subset of methods. 42 | 43 | Subclasses should define _get_element(i), _set_element(i, value), 44 | __len__(), _resize_region(start, end, new_size) and 45 | _constructor(iterable). Define __iter__() for extra speed. 46 | 47 | The _get_element() and _set_element() methods are given indices with 48 | 0 <= i < len(self). 49 | 50 | The _resize_region() method should resize the slice self[start:end] 51 | so that it has size new_size. It is given indices such that 52 | start <= end, 0 <= start <= len(self) and 0 <= end <= len(self). 53 | The resulting elements in self[start:start+new_size] can be set to 54 | None or arbitrary Python values. 55 | 56 | The _constructor() method accepts an iterable and should return a 57 | new instance of the same class as self, populated with the elements 58 | of the given iterable. 59 | """ 60 | 61 | def __cmp__(self, other): 62 | return cmp(list(self), list(other)) 63 | 64 | 65 | def __hash__(self): 66 | raise TypeError('list objects are unhashable') 67 | 68 | 69 | def __iter__(self): 70 | for i in xrange(len(self)): 71 | yield self._get_element(i) 72 | 73 | 74 | def _tuple_from_slice(self, i): 75 | """ 76 | Get (start, end, step) tuple from slice object. 77 | """ 78 | (start, end, step) = i.indices(len(self)) 79 | # Replace (0, -1, 1) with (0, 0, 1) (misfeature in .indices()). 80 | if step == 1: 81 | if end < start: 82 | end = start 83 | step = None 84 | if i.step == None: 85 | step = None 86 | return (start, end, step) 87 | 88 | 89 | 90 | def _fix_index(self, i): 91 | if i < 0: 92 | i += len(self) 93 | if i < 0 or i >= len(self): 94 | raise IndexError('list index out of range') 95 | return i 96 | 97 | 98 | def __getitem__(self, i): 99 | if isinstance(i, slice): 100 | (start, end, step) = self._tuple_from_slice(i) 101 | if step == None: 102 | indices = xrange(start, end) 103 | else: 104 | indices = xrange(start, end, step) 105 | return self._constructor([self._get_element(i) for i in indices]) 106 | else: 107 | return self._get_element(self._fix_index(i)) 108 | 109 | 110 | 111 | def __setitem__(self, i, value): 112 | if isinstance(i, slice): 113 | (start, end, step) = self._tuple_from_slice(i) 114 | if step != None: 115 | # Extended slice 116 | indices = range(start, end, step) 117 | if len(value) != len(indices): 118 | raise ValueError(('attempt to assign sequence of size %d' + 119 | ' to extended slice of size %d') % 120 | (len(value), len(indices))) 121 | for (j, assign_val) in enumerate(value): 122 | self._set_element(indices[j], assign_val) 123 | else: 124 | # Normal slice 125 | if len(value) != (end - start): 126 | self._resize_region(start, end, len(value)) 127 | for (j, assign_val) in enumerate(value): 128 | self._set_element(start + j, assign_val) 129 | else: 130 | # Single element 131 | self._set_element(self._fix_index(i), value) 132 | 133 | 134 | 135 | def __delitem__(self, i): 136 | if isinstance(i, slice): 137 | (start, end, step) = self._tuple_from_slice(i) 138 | if step != None: 139 | # Extended slice 140 | indices = range(start, end, step) 141 | # Sort indices descending 142 | if len(indices) > 0 and indices[0] < indices[-1]: 143 | indices.reverse() 144 | for j in indices: 145 | del self[j] 146 | else: 147 | # Normal slice 148 | self._resize_region(start, end, 0) 149 | else: 150 | # Single element 151 | i = self._fix_index(i) 152 | self._resize_region(i, i + 1, 0) 153 | 154 | 155 | def __add__(self, other): 156 | if isinstance(other, self.__class__): 157 | ans = self._constructor(self) 158 | ans += other 159 | return ans 160 | return list(self) + other 161 | 162 | 163 | def __mul__(self, other): 164 | ans = self._constructor(self) 165 | ans *= other 166 | return ans 167 | 168 | 169 | def __radd__(self, other): 170 | if isinstance(other, self.__class__): 171 | ans = other._constructor(self) 172 | ans += self 173 | return ans 174 | return other + list(self) 175 | 176 | 177 | def __rmul__(self, other): 178 | return self * other 179 | 180 | 181 | def __iadd__(self, other): 182 | self[len(self):len(self)] = other 183 | return self 184 | 185 | 186 | def __imul__(self, other): 187 | if other <= 0: 188 | self[:] = [] 189 | elif other > 1: 190 | aux = list(self) 191 | for i in xrange(other-1): 192 | self.extend(aux) 193 | return self 194 | 195 | 196 | def append(self, other): 197 | self[len(self):len(self)] = [other] 198 | 199 | 200 | def extend(self, other): 201 | self[len(self):len(self)] = other 202 | 203 | 204 | def count(self, other): 205 | ans = 0 206 | for item in self: 207 | if item == other: 208 | ans += 1 209 | return ans 210 | 211 | 212 | def reverse(self): 213 | for i in xrange(len(self)//2): 214 | j = len(self) - 1 - i 215 | (self[i], self[j]) = (self[j], self[i]) 216 | 217 | 218 | def index(self, x, i=0, j=None): 219 | if i != 0 or j is not None: 220 | (i, j, ignore) = self._tuple_from_slice(slice(i, j)) 221 | if j is None: 222 | j = len(self) 223 | for k in xrange(i, j): 224 | if self._get_element(k) == x: 225 | return k 226 | raise ValueError('index(x): x not in list') 227 | 228 | 229 | def insert(self, i, x): 230 | self[i:i] = [x] 231 | 232 | 233 | def pop(self, i=None): 234 | if i == None: 235 | i = len(self)-1 236 | ans = self[i] 237 | del self[i] 238 | return ans 239 | 240 | 241 | def remove(self, x): 242 | for i in xrange(len(self)): 243 | if self._get_element(i) == x: 244 | del self[i] 245 | return 246 | raise ValueError('remove(x): x not in list') 247 | 248 | 249 | # Define sort() as appropriate for the Python version. 250 | if sys.version_info[:3] < (2, 4, 0): 251 | def sort(self, cmpfunc=None): 252 | ans = list(self) 253 | ans.sort(cmpfunc) 254 | self[:] = ans 255 | else: 256 | def sort(self, cmpfunc=None, key=None, reverse=False): 257 | ans = list(self) 258 | if reverse == True: 259 | ans.sort(cmpfunc, key, reverse) 260 | elif key != None: 261 | ans.sort(cmpfunc, key) 262 | else: 263 | ans.sort(cmpfunc) 264 | self[:] = ans 265 | 266 | 267 | def __copy__(self): 268 | return self._constructor(self) 269 | 270 | 271 | def __deepcopy__(self, memo={}): 272 | ans = self._constructor([]) 273 | memo[id(self)] = ans 274 | ans[:] = copy.deepcopy(tuple(self), memo) 275 | return ans 276 | 277 | # Tracking idea from R. Hettinger's deque class. It's not 278 | # multithread safe, but does work with the builtin Python classes. 279 | def __str__(self, track=[]): 280 | if id(self) in track: 281 | return '...' 282 | track.append(id(self)) 283 | ans = '%r' % (list(self),) 284 | track.remove(id(self)) 285 | return ans 286 | 287 | def __repr__(self): 288 | return self.__class__.__name__ + '(' + str(self) + ')' 289 | 290 | 291 | # Example usage: 292 | 293 | class TestList(ListMixin): 294 | def __init__(self, L=[]): 295 | self.L = list(L) 296 | 297 | def _constructor(self, iterable): 298 | return TestList(iterable) 299 | 300 | def __len__(self): 301 | return len(self.L) 302 | 303 | def _get_element(self, i): 304 | assert 0 <= i < len(self) 305 | return self.L[i] 306 | 307 | def _set_element(self, i, x): 308 | assert 0 <= i < len(self) 309 | self.L[i] = x 310 | 311 | def _resize_region(self, start, end, new_size): 312 | assert 0 <= start <= len(self) 313 | assert 0 <= end <= len(self) 314 | assert start <= end 315 | self.L[start:end] = [None] * new_size 316 | 317 | # Now TestList() has behavior identical to that of list(). 318 | ## end of http://code.activestate.com/recipes/440656/ }}} 319 | -------------------------------------------------------------------------------- /redi/packages/jsonpickle/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2008 John Paulett (john -at- paulett.org) 4 | # All rights reserved. 5 | # 6 | # This software is licensed as described in the file COPYING, which 7 | # you should have received as part of this distribution. 8 | 9 | """Python library for serializing any arbitrary object graph into JSON. 10 | It can take almost any Python object and turn the object into JSON. 11 | Additionally, it can reconstitute the object back into Python. 12 | 13 | >>> import jsonpickle 14 | >>> from samples import Thing 15 | 16 | Create an object. 17 | 18 | >>> obj = Thing('A String') 19 | >>> print obj.name 20 | A String 21 | 22 | Use jsonpickle to transform the object into a JSON string. 23 | 24 | >>> pickled = jsonpickle.encode(obj) 25 | >>> print pickled 26 | {"py/object": "samples.Thing", "name": "A String", "child": null} 27 | 28 | Use jsonpickle to recreate a Python object from a JSON string 29 | 30 | >>> unpickled = jsonpickle.decode(pickled) 31 | >>> str(unpickled.name) 32 | 'A String' 33 | 34 | .. warning:: 35 | 36 | Loading a JSON string from an untrusted source represents a potential 37 | security vulnerability. jsonpickle makes no attempt to sanitize the input. 38 | 39 | The new object has the same type and data, but essentially is now a copy of 40 | the original. 41 | 42 | >>> obj == unpickled 43 | False 44 | >>> obj.name == unpickled.name 45 | True 46 | >>> type(obj) == type(unpickled) 47 | True 48 | 49 | If you will never need to load (regenerate the Python class from JSON), you can 50 | pass in the keyword unpicklable=False to prevent extra information from being 51 | added to JSON. 52 | 53 | >>> oneway = jsonpickle.encode(obj, unpicklable=False) 54 | >>> print oneway 55 | {"name": "A String", "child": null} 56 | 57 | """ 58 | 59 | from .pickler import Pickler 60 | from .unpickler import Unpickler 61 | 62 | __version__ = '0.3.1' 63 | __all__ = ('encode', 'decode') 64 | 65 | 66 | class JSONPluginMgr(object): 67 | """The JSONPluginMgr handles encoding and decoding. 68 | 69 | It tries these modules in this order: 70 | simplejson, json, demjson 71 | 72 | simplejson is a fast and popular backend and is tried first. 73 | json comes with python2.6 and is tried second. 74 | demjson is the most permissive backend and is tried last. 75 | 76 | """ 77 | def __init__(self): 78 | ## The names of backends that have been successfully imported 79 | self._backend_names = [] 80 | 81 | ## A dictionary mapping backend names to encode/decode functions 82 | self._encoders = {} 83 | self._decoders = {} 84 | 85 | ## Options to pass to specific encoders 86 | self._encoder_options = {} 87 | 88 | ## The exception class that is thrown when a decoding error occurs 89 | self._decoder_exceptions = {} 90 | 91 | ## Whether we've loaded any backends successfully 92 | self._verified = False 93 | 94 | ## Try loading simplejson and demjson 95 | self.load_backend('simplejson', 'dumps', 'loads', ValueError) 96 | self.load_backend('json', 'dumps', 'loads', ValueError) 97 | self.load_backend('demjson', 'encode', 'decode', 'JSONDecodeError') 98 | 99 | def _verify(self): 100 | """Ensures that we've loaded at least one JSON backend.""" 101 | if self._verified: 102 | return 103 | raise AssertionError('jsonpickle requires at least one of the ' 104 | 'following:\n' 105 | ' python2.6, simplejson, or demjson') 106 | 107 | def load_backend(self, name, encode_name, decode_name, decode_exc): 108 | """ 109 | Load a JSON backend by name. 110 | 111 | This method loads a backend and sets up references to that 112 | backend's encode/decode functions and exception classes. 113 | 114 | :param encode_name: is the name of the backend's encode method. 115 | The method should take an object and return a string. 116 | :param decode_name: names the backend's method for the reverse 117 | operation -- returning a Python object from a string. 118 | :param decode_exc: can be either the name of the exception class 119 | used to denote decoding errors, or it can be a direct reference 120 | to the appropriate exception class itself. If it is a name, 121 | then the assumption is that an exception class of that name 122 | can be found in the backend module's namespace. 123 | 124 | """ 125 | try: 126 | ## Load the JSON backend 127 | mod = __import__(name) 128 | except ImportError: 129 | return 130 | 131 | try: 132 | ## Handle submodules, e.g. django.utils.simplejson 133 | components = name.split('.') 134 | for comp in components[1:]: 135 | mod = getattr(mod, comp) 136 | except AttributeError: 137 | return 138 | 139 | try: 140 | ## Setup the backend's encode/decode methods 141 | self._encoders[name] = getattr(mod, encode_name) 142 | self._decoders[name] = getattr(mod, decode_name) 143 | except AttributeError: 144 | self.remove_backend(name) 145 | return 146 | 147 | try: 148 | if type(decode_exc) is str: 149 | ## This backend's decoder exception is part of the backend 150 | self._decoder_exceptions[name] = getattr(mod, decode_exc) 151 | else: 152 | ## simplejson uses the ValueError exception 153 | self._decoder_exceptions[name] = decode_exc 154 | except AttributeError: 155 | self.remove_backend(name) 156 | return 157 | 158 | ## Setup the default args and kwargs for this encoder 159 | self._encoder_options[name] = ([], {}) 160 | 161 | ## Add this backend to the list of candidate backends 162 | self._backend_names.append(name) 163 | 164 | ## Indicate that we successfully loaded a JSON backend 165 | self._verified = True 166 | 167 | def remove_backend(self, name): 168 | """Remove all entries for a particular backend.""" 169 | self._encoders.pop(name, None) 170 | self._decoders.pop(name, None) 171 | self._decoder_exceptions.pop(name, None) 172 | self._encoder_options.pop(name, None) 173 | if name in self._backend_names: 174 | self._backend_names.remove(name) 175 | self._verified = bool(self._backend_names) 176 | 177 | def encode(self, obj): 178 | """ 179 | Attempt to encode an object into JSON. 180 | 181 | This tries the loaded backends in order and passes along the last 182 | exception if no backend is able to encode the object. 183 | 184 | """ 185 | self._verify() 186 | for idx, name in enumerate(self._backend_names): 187 | try: 188 | optargs, optkwargs = self._encoder_options[name] 189 | encoder_kwargs = optkwargs.copy() 190 | encoder_args = (obj,) + tuple(optargs) 191 | return self._encoders[name](*encoder_args, **encoder_kwargs) 192 | except Exception: 193 | if idx == len(self._backend_names) - 1: 194 | raise 195 | 196 | def decode(self, string): 197 | """ 198 | Attempt to decode an object from a JSON string. 199 | 200 | This tries the loaded backends in order and passes along the last 201 | exception if no backends are able to decode the string. 202 | 203 | """ 204 | self._verify() 205 | for idx, name in enumerate(self._backend_names): 206 | try: 207 | return self._decoders[name](string) 208 | except self._decoder_exceptions[name], e: 209 | if idx == len(self._backend_names) - 1: 210 | raise e 211 | else: 212 | pass # and try a more forgiving encoder, e.g. demjson 213 | 214 | def set_preferred_backend(self, name): 215 | """ 216 | Set the preferred json backend. 217 | 218 | If a preferred backend is set then jsonpickle tries to use it 219 | before any other backend. 220 | 221 | For example:: 222 | 223 | set_preferred_backend('simplejson') 224 | 225 | If the backend is not one of the built-in jsonpickle backends 226 | (json/simplejson, or demjson) then you must load the backend 227 | prior to calling set_preferred_backend. 228 | 229 | AssertionError is raised if the backend has not been loaded. 230 | 231 | """ 232 | if name in self._backend_names: 233 | self._backend_names.remove(name) 234 | self._backend_names.insert(0, name) 235 | else: 236 | errmsg = 'The "%s" backend has not been loaded.' % name 237 | raise AssertionError(errmsg) 238 | 239 | def set_encoder_options(self, name, *args, **kwargs): 240 | """ 241 | Associate encoder-specific options with an encoder. 242 | 243 | After calling set_encoder_options, any calls to jsonpickle's 244 | encode method will pass the supplied args and kwargs along to 245 | the appropriate backend's encode method. 246 | 247 | For example:: 248 | 249 | set_encoder_options('simplejson', sort_keys=True, indent=4) 250 | set_encoder_options('demjson', compactly=False) 251 | 252 | See the appropriate encoder's documentation for details about 253 | the supported arguments and keyword arguments. 254 | 255 | """ 256 | self._encoder_options[name] = (args, kwargs) 257 | 258 | # Initialize a JSONPluginMgr 259 | json = JSONPluginMgr() 260 | 261 | # Export specific JSONPluginMgr methods into the jsonpickle namespace 262 | set_preferred_backend = json.set_preferred_backend 263 | set_encoder_options = json.set_encoder_options 264 | load_backend = json.load_backend 265 | remove_backend = json.remove_backend 266 | 267 | 268 | def encode(value, unpicklable=True, max_depth=None): 269 | """ 270 | Return a JSON formatted representation of value, a Python object. 271 | 272 | The keyword argument 'unpicklable' defaults to True. 273 | If set to False, the output will not contain the information 274 | necessary to turn the JSON data back into Python objects. 275 | 276 | The keyword argument 'max_depth' defaults to None. 277 | If set to a non-negative integer then jsonpickle will not recurse 278 | deeper than 'max_depth' steps into the object. Anything deeper 279 | than 'max_depth' is represented using a Python repr() of the object. 280 | 281 | >>> encode('my string') 282 | '"my string"' 283 | >>> encode(36) 284 | '36' 285 | 286 | >>> encode({'foo': True}) 287 | '{"foo": true}' 288 | 289 | >>> encode({'foo': True}, max_depth=0) 290 | '"{\\'foo\\': True}"' 291 | 292 | >>> encode({'foo': True}, max_depth=1) 293 | '{"foo": "True"}' 294 | 295 | 296 | """ 297 | j = Pickler(unpicklable=unpicklable, 298 | max_depth=max_depth) 299 | return json.encode(j.flatten(value)) 300 | 301 | def decode(string): 302 | """ 303 | Convert a JSON string into a Python object. 304 | 305 | >>> str(decode('"my string"')) 306 | 'my string' 307 | >>> decode('36') 308 | 36 309 | """ 310 | j = Unpickler() 311 | return j.restore(json.decode(string)) 312 | -------------------------------------------------------------------------------- /redi/models.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | redi.models 6 | ~~~~~~~~~~~ 7 | 8 | This module contains most of the functionality of redi. 9 | 10 | """ 11 | 12 | import uuid 13 | 14 | from operator import itemgetter 15 | from UserDict import DictMixin 16 | from . import config 17 | 18 | from .utils import ListMixin, is_collection, compress_key, expand_key 19 | 20 | 21 | 22 | 23 | class RedisKey(object): 24 | """Contains methods that can be applied to any Redis key.""" 25 | 26 | def __init__(self, key, redis=None, o=False): 27 | super(RedisKey, self).__init__() 28 | 29 | self.key = key 30 | self._o = o 31 | 32 | if redis is None: 33 | self.redis = config.redis 34 | 35 | self.redis = redis 36 | self.uuid = uuid.uuid4().hex 37 | 38 | 39 | def __repr__(self): 40 | return ''.format(self.key) 41 | 42 | 43 | def _(self, key, default='string'): 44 | 45 | if len(self.key): 46 | key = compress_key(expand_key(self.key) + [key]) 47 | else: 48 | key = [key] 49 | 50 | return auto_type(key, redis=self.redis, default=default, o=True) 51 | 52 | 53 | def delete(self): 54 | """Removes this key from Redis.""" 55 | return self.redis.delete(self.key) 56 | 57 | 58 | def __getattribute__(self, key): 59 | 60 | if key not in ('_o', 'children', 'key', 'data', 'redis'): 61 | 62 | try: 63 | return object.__getattribute__(self, key) 64 | except AttributeError: 65 | pass 66 | 67 | if self._o: 68 | # if key in self.children: 69 | if True: 70 | 71 | if len(self.key): 72 | key = compress_key(expand_key(self.key) + [key]) 73 | else: 74 | key = [key] 75 | 76 | return auto_type(key, redis=self.redis, default='key', o=True) 77 | 78 | return object.__getattribute__(self, key) 79 | 80 | def expire(self, s): 81 | """Expires this key from Redis in given seconds.""" 82 | # TODO: Accept datetime for expire_at 83 | 84 | return self.redis.expire(self.key, s) 85 | 86 | 87 | def rename(self, new_name, safe=True): 88 | """Renames this key.""" 89 | 90 | new_name = compress_key(new_name) 91 | 92 | if safe: 93 | if self.redis.renamenx(self.key, new_name): 94 | self.key = new_name 95 | return True 96 | return False 97 | else: 98 | self.redis.rename(self.key, new_name) 99 | self.key = new_name 100 | return True 101 | 102 | 103 | @property 104 | def children(self): 105 | """Lists all children of current key.""" 106 | 107 | current_key = expand_key(self.key) 108 | 109 | namespace = compress_key(current_key + ['*']) 110 | 111 | keys = [] 112 | 113 | for key in self.redis.keys(namespace): 114 | _key = expand_key(key)[len(current_key):] 115 | keys.append(compress_key(_key)) 116 | 117 | return keys 118 | 119 | 120 | @property 121 | def siblings(self): 122 | """Lists all siblings of current key.""" 123 | 124 | namespace = compress_key(expand_key(self.key)[:-1]+ ['*']) 125 | 126 | keys = self.redis.keys(namespace) 127 | 128 | # clean up results 129 | keys.remove(self.key) 130 | 131 | for key in self.children: 132 | try: 133 | keys.remove(key) 134 | except ValueError: 135 | pass 136 | 137 | 138 | return keys 139 | 140 | 141 | @staticmethod 142 | def to_redis(o): 143 | """Converts Python datatypes to Redis values.""" 144 | 145 | # don't serialize internal datatype classesf 146 | if isinstance(o, SubList) or isinstance(o, SubDict): 147 | o = o.data 148 | 149 | return config.encoder(o) 150 | 151 | 152 | def to_python(self, o): 153 | """Converts Redis values to Python datatypes.""" 154 | try: 155 | v = config.decoder(o) 156 | 157 | if isinstance(v, dict): 158 | return SubDict(v, self.save) 159 | 160 | elif is_collection(v): 161 | return SubList(v, self.save) 162 | 163 | try: 164 | if not isinstance(v, float): 165 | return int(v) 166 | except ValueError: 167 | pass 168 | 169 | try: 170 | if not isinstance(v, int): 171 | return float(v) 172 | except ValueError: 173 | pass 174 | 175 | return v 176 | 177 | except (ValueError, TypeError): 178 | try: 179 | return unicode(o, config.str_codec) 180 | except (UnicodeDecodeError, TypeError): 181 | return o 182 | 183 | 184 | def save(self, *args): 185 | pass 186 | 187 | 188 | 189 | class RedisString(RedisKey): 190 | """Redis String interface.""" 191 | 192 | def __init__(self, key, redis=None, o=False): 193 | super(RedisString, self).__init__(key, redis=redis, o=o) 194 | 195 | self.key = key 196 | 197 | 198 | def __repr__(self): 199 | return ''.format(self.key) 200 | 201 | 202 | @property 203 | def _raw(self): 204 | """Returns raw Redis data.""" 205 | return self.redis.get(self.key) 206 | 207 | 208 | def save(self, value): 209 | """Saves current value to Database.""" 210 | v = self.to_redis(value) 211 | return self.redis.set(self.key, v) 212 | 213 | 214 | @property 215 | def data(self): 216 | """Writes value to database.""" 217 | v = self.redis.get(self.key) 218 | return self.to_python(v) 219 | 220 | 221 | @data.setter 222 | def data(self, value): 223 | """Writes value to database.""" 224 | self.save(value) 225 | 226 | 227 | @property 228 | def type(self): 229 | return self.data.__class__ 230 | 231 | 232 | def write(self): 233 | return self.data.write() 234 | 235 | 236 | def __getitem__(self, item): 237 | return self.data.__getitem__(item) 238 | 239 | 240 | def __setitem__(self, k, v): 241 | return self.data.__setitem__(k, v) 242 | 243 | 244 | def __delitem__(self, k): 245 | return self.data.__delitem__(k) 246 | 247 | 248 | 249 | class RedisListString(RedisString, ListMixin): 250 | """Redis value of awesomeness.""" 251 | 252 | 253 | def __init__(self, key, redis, o=False): 254 | super(RedisListString, self).__init__(key, redis=redis, o=o) 255 | self.key = key 256 | 257 | 258 | def __repr__(self): 259 | return ''.format(self.key) 260 | 261 | 262 | def __getitem__(self, item): 263 | return self.data.__getitem__(item) 264 | 265 | def __setitem__(self, k, v): 266 | return self.data.__setitem__(k, v) 267 | 268 | def __delitem__(self, k): 269 | return self.data.__delitem__(k) 270 | 271 | def _get_element(self, i): 272 | return self.data._get_element(i) 273 | 274 | def _set_element(self, i, value): 275 | return self.data._set_element(i, value) 276 | 277 | def __len__(self): 278 | return self.data.__len__() 279 | 280 | def _resize_region(self, start, end, new_size): 281 | return self.data._resize_region(start, end, new_size) 282 | 283 | def _constructor(self, iter): 284 | return self.data._constructor(iter) 285 | 286 | 287 | 288 | class RedisDictString(RedisString, DictMixin): 289 | """Redis value of awesomeness.""" 290 | 291 | 292 | def __init__(self, key, redis, o=False): 293 | super(RedisDictString, self).__init__(key, redis=redis, o=o) 294 | self.key = key 295 | 296 | 297 | def __repr__(self): 298 | return ''.format(self.key) 299 | 300 | 301 | def get(self, item): 302 | return self.data.get(item) 303 | 304 | 305 | def keys(self): 306 | return self.data.keys() 307 | 308 | 309 | 310 | class RedisList(RedisKey): 311 | """Redis list of awesomeness.""" 312 | 313 | def __init__(self, key, redis, o=False): 314 | super(RedisList, self).__init__(key, redis=redis, o=o) 315 | self.key = key 316 | 317 | 318 | def __repr__(self): 319 | return ''.format(self.key) 320 | 321 | 322 | @property 323 | def _raw(self): 324 | for v in self.redis.lrange(self.key, 0, -1): 325 | yield v 326 | 327 | 328 | def save(self, value, i=None): 329 | """Save list.""" 330 | 331 | self[i] = value 332 | 333 | 334 | def __getitem__(self, i): 335 | is_single = not isinstance(i, slice) 336 | 337 | if is_single: 338 | value = self.redis.lindex(self.key, i) 339 | values = self.to_python(value) 340 | try: 341 | values.i = i 342 | except AttributeError: 343 | pass 344 | 345 | 346 | else: 347 | start = 0 if i.start is None else i.start 348 | stop = -1 if i.stop is None else i.stop 349 | 350 | values = self.redis.lrange(self.key, start, stop) 351 | values = map(self.to_python, values) 352 | 353 | for i in range(start, start+len(values)): 354 | try: 355 | values[i].i = i 356 | except AttributeError: 357 | pass 358 | 359 | 360 | return values 361 | 362 | 363 | def __setitem__(self, i, value): 364 | v = self.to_redis(value) 365 | return self.redis.lset(self.key, i, v) 366 | 367 | 368 | def __delitem__(self, i): 369 | 370 | for value in self[i]: 371 | self.redis.lrem(self.key, value) 372 | 373 | 374 | def __iter__(self): 375 | for v in self[:]: 376 | yield v 377 | 378 | 379 | def __len__(self): 380 | return self.redis.llen(self.key) 381 | 382 | 383 | def __contains__(self, value): 384 | i = self.index(value) 385 | return (i is not None) 386 | 387 | 388 | def insert(self, index, value, before=True): 389 | 390 | refvalue = self[index] 391 | where = 'BEFORE' if before else 'AFTER' 392 | 393 | return self.redis.linsert(self.key, where, refvalue, value) 394 | 395 | 396 | def index(self, value): 397 | """Returns first found index of given value.""" 398 | 399 | for i, v in enumerate(self): 400 | try: 401 | if value.__dict__ == v.__dict__: 402 | return i 403 | except AttributeError: 404 | if value == v: 405 | return i 406 | 407 | 408 | def append(self, value, right=True): 409 | v = self.to_redis(value) 410 | 411 | if right: 412 | return self.redis.rpush(self.key, v) 413 | else: 414 | return self.redis.lpush(self.key, v) 415 | 416 | 417 | def extend(self, values): 418 | for value in values: 419 | v = self.to_redis(value) 420 | self.append(v) 421 | 422 | def rpush(self, value): 423 | """Redis RPUSH.""" 424 | return self.append(value, right=True) 425 | 426 | 427 | def lpush(self, value): 428 | """Redis LPUSH.""" 429 | return self.append(value, right=False) 430 | 431 | 432 | def lpop(self): 433 | """Redis LPOP.""" 434 | return self.pop(right=False) 435 | 436 | 437 | def rpop(self): 438 | """Redis RPOP.""" 439 | return self.pop(right=True) 440 | 441 | 442 | def pop(self, right=True): 443 | """Redis (R|L)POP.""" 444 | 445 | if right: 446 | v = self.redis.lpop(self.key) 447 | else: 448 | v = self.redis.rpop(self.key) 449 | 450 | 451 | return self.to_python(v) 452 | 453 | 454 | def brpop(self, timeout=config.block_timeout): 455 | """Redis BRPOP.""" 456 | return self.bpop(timeout, right=True) 457 | 458 | 459 | def blpop(self, timeout=config.block_timeout): 460 | """Redis BLPOP.""" 461 | return self.bpop(timeout, right=False) 462 | 463 | 464 | def bpop(self, timeout=config.block_timeout, right=True): 465 | """Redis B(R|L)POP.""" 466 | 467 | if right: 468 | v = self.redis.brpop(self.key, timeout) 469 | else: 470 | v = self.redis.blpop(self.key, timeout) 471 | 472 | try: 473 | return self.to_python(v[1]) 474 | except TypeError: 475 | return None 476 | 477 | 478 | def find(self, *search): 479 | """FINDS ALL TEH THINGS.""" 480 | 481 | for item in self._raw: 482 | for s in search: 483 | if callable(s): 484 | if s(item): 485 | yield item 486 | else: 487 | if s in item: 488 | yield self.to_python(item) 489 | 490 | def sorted_by(self, key, reverse=False ): 491 | 492 | return sorted(self, key=itemgetter(key), reverse=reverse) 493 | 494 | 495 | class SubList(ListMixin): 496 | """Lists within Redis values.""" 497 | 498 | def __init__(self, l, writer): 499 | self.data = l 500 | self.writer = writer 501 | self.i = None 502 | 503 | 504 | def write(self): 505 | """Writes List to Redis.""" 506 | if self.i is not None: 507 | self.writer(self.data, self.i) 508 | else: 509 | self.writer(self.data) 510 | 511 | 512 | def _get_element(self, i): 513 | return self.data[i] 514 | 515 | 516 | def _set_element(self, i, value): 517 | self.data[i] = value 518 | self.write() 519 | 520 | 521 | def __len__(self): 522 | return len(self.data) 523 | 524 | 525 | def _resize_region(self, start, end, new_size): 526 | 527 | self.data[start:end] = [None] * new_size 528 | self.write() 529 | 530 | 531 | def _constructor(self, iter): 532 | return SubList(iter, self.writer) 533 | 534 | 535 | def __iter__(self): 536 | for item in self.data: 537 | yield item 538 | 539 | 540 | 541 | class SubDict(DictMixin): 542 | """Dicts within Redis values.""" 543 | 544 | def __init__(self, d, writer): 545 | self.data = d 546 | self.writer = writer 547 | self.i = None 548 | 549 | def write(self): 550 | """Writes Dict to Redis.""" 551 | if self.i is not None: 552 | self.writer(self.data, self.i) 553 | else: 554 | self.writer(self.data) 555 | 556 | 557 | def __getitem__(self, item): 558 | return self.data.get(item) 559 | 560 | 561 | def __setitem__(self, k, v): 562 | self.data[k] = v 563 | self.write() 564 | 565 | def __delitem__(self, k): 566 | del self.data[k] 567 | self.write() 568 | 569 | def __repr__(self): 570 | return repr(self.data) 571 | 572 | def keys(self): 573 | return self.data.keys() 574 | 575 | 576 | 577 | def auto_type(key, redis=None, default=None, o=True): 578 | """Returns datatype instance""" 579 | 580 | if redis is None: 581 | redis = config.redis 582 | 583 | key = compress_key(key) 584 | 585 | if redis.exists(key): 586 | 587 | datatype = redis.type(key) 588 | 589 | if datatype == 'string': 590 | test_string = RedisString(key, redis=redis).data 591 | 592 | if isinstance(test_string, dict): 593 | datatype = 'dict-string' 594 | elif isinstance(test_string, list): 595 | datatype = 'list-string' 596 | elif isinstance(test_string, basestring): 597 | datatype = 'string' 598 | elif isinstance(test_string, int): 599 | datatype = 'string' 600 | elif isinstance(test_string, float): 601 | datatype = 'string' 602 | 603 | return TYPE_MAP.get(datatype)(key, redis=redis, o=o) 604 | 605 | else: 606 | if default: 607 | try: 608 | return TYPE_MAP.get(default)(key, redis=redis, o=o) 609 | except KeyError: 610 | raise ValueError('Provide a valid default redis type.') 611 | 612 | return None 613 | 614 | 615 | 616 | TYPE_MAP = { 617 | 'string': RedisString, 618 | 'value': RedisString, 619 | 620 | 'key': RedisKey, 621 | 622 | 'liststring': RedisListString, 623 | 'list-string': RedisListString, 624 | 'stringlist': RedisListString, 625 | 'string-list': RedisListString, 626 | 627 | 'dictstring': RedisDictString, 628 | 'dict-string': RedisDictString, 629 | 'stringdict': RedisDictString, 630 | 'string-dict': RedisDictString, 631 | 632 | 'list': RedisList 633 | } 634 | 635 | --------------------------------------------------------------------------------