├── 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 |
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 |
--------------------------------------------------------------------------------