[
  {
    "path": ".gitignore",
    "content": "build\ndist\nredisco.egg-info\n*.pyc\n.DS_Store\ndump.rdb\n*.swp\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2010 Tim Medina\n\n Permission is hereby granted, free of charge, to any person\n obtaining a copy of this software and associated documentation\n files (the \"Software\"), to deal in the Software without\n restriction, including without limitation the rights to use,\n copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the\n Software is furnished to do so, subject to the following\n conditions:\n\n The above copyright notice and this permission notice shall be\n included in all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include README.rst\n"
  },
  {
    "path": "README.rst",
    "content": "\n=============================================================================================\nThis fork is no longer actively maintained. Go to https://github.com/kiddouk/redisco instead.\n=============================================================================================\n\n=======\nRedisco\n=======\nPython Containers and Simple Models for Redis\n\nDescription\n-----------\nRedisco allows you to store objects in Redis_. It is inspired by the Ruby library\nOhm_ and its design and code are loosely based on Ohm and the Django ORM.\nIt is built on top of redis-py_. It includes container classes that allow\neasier access to Redis sets, lists, and sorted sets.\n\nInstallation\n------------\nRedisco requires redis-py 2.0.0 so get it first.\n\n    pip install redis\n\nThen install redisco.\n\n    pip install redisco\n\n\nModels\n------\n\n::\n\n    from redisco import models\n    class Person(models.Model):\n        name = models.Attribute(required=True)\n        created_at = models.DateTimeField(auto_now_add=True)\n        fave_colors = models.ListField(str)\n\n    >>> person = Person(name=\"Conchita\")\n    >>> person.is_valid()\n    True\n    >>> person.save()\n    True\n    >>> conchita = Person.objects.filter(name='Conchita')[0]\n    >>> conchita.name\n    'Conchita'\n    >>> conchita.created_at\n    datetime.datetime(2010, 5, 24, 16, 0, 31, 954704)\n\n\nModel Attributes\n----------------\n\nAttribute\n    Stores unicode strings. If used for large bodies of text,\n    turn indexing of this field off by setting indexed=True.\n\nIntegerField\n    Stores an int. Ints are stringified using unicode() before saving to\n    Redis.\n\nCounter\n    An IntegerField that can only be accessed via Model.incr and Model.decr.\n\nDateTimeField\n    Can store a DateTime object. Saved in the Redis store as a float.\n\nDateField\n    Can store a Date object. Saved in Redis as a float.\n\nFloatField\n    Can store floats.\n\nBooleanField\n    Can store bools. Saved in Redis as 1's and 0's.\n\nReferenceField\n    Can reference other redisco model.\n\nListField\n    Can store a list of unicode, int, float, as well as other redisco models.\n\n\nAttribute Options\n-----------------\n\nrequired\n    If True, the attirbute cannot be None or empty. Strings are stripped to\n    check if they are empty. Default is False.\n\ndefault\n    Sets the default value of the attribute. Default is None.\n\nindexed\n    If True, redisco will create index entries for the attribute. Indexes\n    are used in filtering and ordering results of queries. For large bodies\n    of strings, this should be set to False. Default is True.\n\nvalidator\n    Set this to a callable that accepts two arguments -- the field name and\n    the value of the attribute. The callable should return a list of tuples\n    with the first item is the field name, and the second item is the error.\n\nunique\n    The field must be unique. Default is False.\n\nDateField and DateTimeField Options\n\nauto_now_add\n    Automatically set the datetime/date field to now/today when the object\n    is first created. Default is False.\n\nauto_now\n    Automatically set the datetime/date field to now/today everytime the object\n    is saved. Default is False.\n\n\nSaving and Validating\n---------------------\n\nTo save an object, call its save method. This returns True on success (i.e. when\nthe object is valid) and False otherwise.\n\nCalling Model.is_valid will validate the attributes and lists. Model.is_valid\nis called when the instance is being saved. When there are invalid fields,\nModel.errors will hold the list of tuples containing the invalid fields and\nthe reason for its invalidity. E.g.\n[('name', 'required'),('name', 'too short')]\n\nFields can be validated using the validator argument of the attribute. Just\npass a callable that accepts two arguments -- the field name and the value\nof the attribute. The callable should return a list of errors.\n\nModel.validate will also be called before saving the instance. Override it\nto validate instances not related to attributes.\n\n::\n\n    def not_me(field_name, value):\n        if value == 'Me':\n            return ((field_name, 'it is me'),)\n\n    class Person(models.Model):\n        name = models.Attribute(required=True, validator=not_me)\n        age = models.IntegerField()\n\n        def validate(self):\n            if self.age and self.age < 21:\n                self._errors.append(('age', 'below 21'))\n\n    >>> person = Person(name='Me')\n    >>> person.is_valid()\n    False\n    >>> person.errors\n    [('name', 'it is me')]\n\n\nQueries\n-------\n\nQueries are executed using a manager, accessed via the objects class\nattribute.\n\n::\n\n    Person.objects.all()\n    Person.objects.filter(name='Conchita')\n    Person.objects.filter(name='Conchita').first()\n    Person.objects.all().order('name')\n    Person.objects.filter(fave_colors='Red')\n\nRanged Queries\n--------------\n\nRedisco has a limited support for queries involving ranges -- it can only\nfilter fields that are numeric, i.e. DateField, DateTimeField, IntegerField,\nand FloatField. The zfilter method of the manager is used for these queries.\n\n::\n\n    Person.objects.zfilter(created_at__lt=datetime(2010, 4, 20, 5, 2, 0))\n    Person.objects.zfilter(created_at__gte=datetime(2010, 4, 20, 5, 2, 0))\n    Person.objects.zfilter(created_at__in=(datetime(2010, 4, 20, 5, 2, 0), datetime(2010, 5, 1)))\n\n\nContainers\n----------\nRedisco has three containers that roughly match Redis's supported data\nstructures: lists, sets, sorted set. Anything done to the container is\npersisted to Redis.\n\nSets\n    >>> from redisco.containers import Set\n    >>> s = Set('myset')\n    >>> s.add('apple')\n    >>> s.add('orange')\n    >>> s.members\n    set(['orange', 'apple'])\n    >>> t = Set('nset')\n    >>> t.add('kiwi')\n    >>> t.add('guava')\n    >>> t.members\n    set(['kiwi', 'guava'])\n    >>> s.update(t)\n    >>> s.members\n    set(['kiwi', 'orange', 'guava', 'apple'])\n\nLists\n    >>> import redis\n    >>> from redisco.containers import List\n    >>> l = List('alpha')\n    >>> l.append('a')\n    >>> l.append('b')\n    >>> l.append('c')\n    >>> 'a' in l\n    True\n    >>> 'd' in l\n    False\n    >>> len(l)\n    3\n    >>> l.index('b')\n    1\n    >>> l.members\n    ['a', 'b', 'c']\n\n\nSorted Sets\n    >>> zset = SortedSet('zset')\n    >>> zset.members\n    ['d', 'a', 'b', 'c']\n    >>> 'e' in zset\n    False\n    >>> 'a' in zset\n    True\n    >>> zset.rank('d')\n    0\n    >>> zset.rank('b')\n    2\n    >>> zset[1]\n    'a'\n    >>> zset.add('f', 200)\n    >>> zset.members\n    ['d', 'a', 'b', 'c', 'f']\n    >>> zset.add('d', 99)\n    >>> zset.members\n    ['a', 'b', 'c', 'd', 'f']\n\n\nDicts/Hashes\n    >>> h = cont.Hash('hkey')\n    >>> len(h)\n    0\n    >>> h['name'] = \"Richard Cypher\"\n    >>> h['real_name'] = \"Richard Rahl\"\n    >>> h\n    <Hash 'hkey' {'name': 'Richard Cypher', 'real_name': 'Richard Rahl'}>\n    >>> h.dict\n    {'name': 'Richard Cypher', 'real_name': 'Richard Rahl'}\n\n\nAdditional Info on Containers\n-----------------------------\n\nSome methods of the Redis client that require the key as the first argument\ncan be accessed from the container itself.\n\n    >>> l = List('mylist')\n    >>> l.lrange(0, -1)\n    0\n    >>> l.rpush('b')\n    >>> l.rpush('c')\n    >>> l.lpush('a')\n    >>> l.lrange(0, -1)\n    ['a', 'b', 'c']\n    >>> h = Hash('hkey')\n    >>> h.hset('name', 'Richard Rahl')\n    >>> h\n    <Hash 'hkey' {'name': 'Richard Rahl'}>\n\n\nConnecting to Redis\n-------------------\n\nAll models and containers use a global Redis client object to\ninteract with the key-value storage. By default, it connects\nto localhost:6379, selecting db 0. If you wish to specify settings:\n\n::\n\n    import redisco\n    redisco.connection_setup(host='localhost', port=6380, db=10)\n\nThe arguments to connect are simply passed to the redis.Redis init method.\n\nFor the containers, you can specify a second argument as the Redis client.\nThat client object will be used instead of the default.\n\n    >>> import redis\n    >>> r = redis.Redis(host='localhost', port=6381)\n    >>> Set('someset', r)\n\n\nCredits\n-------\n\nMost of the concepts are taken from `Soveran`_'s Redis related Ruby libraries.\ncyx_ for sharing his expertise in indexing in Redis.\nDjango, of course, for the popular model API.\n\n.. _Redis: http://code.google.com/p/redis/\n.. _Ohm: http://github.com/soveran/ohm/\n.. _redis-py: http://github.com/andymccurdy/redis-py/\n.. _`Soveran`: http://github.com/soveran\n.. _cyx: http://github.com/cyx\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "_build\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = _build\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n\n.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest\n\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html      to make standalone HTML files\"\n\t@echo \"  dirhtml   to make HTML files named index.html in directories\"\n\t@echo \"  pickle    to make pickle files\"\n\t@echo \"  json      to make JSON files\"\n\t@echo \"  htmlhelp  to make HTML files and a HTML help project\"\n\t@echo \"  qthelp    to make HTML files and a qthelp project\"\n\t@echo \"  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  changes   to make an overview of all changed/added/deprecated items\"\n\t@echo \"  linkcheck to check all external links for integrity\"\n\t@echo \"  doctest   to run all doctests embedded in the documentation (if enabled)\"\n\nclean:\n\t-rm -rf $(BUILDDIR)/*\n\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/Redisco.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/Redisco.qhc\"\n\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make all-pdf' or \\`make all-ps' in that directory to\" \\\n\t      \"run these through (pdf)latex.\"\n\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# Redisco documentation build configuration file, created by\n# sphinx-quickstart on Mon May 24 21:29:02 2010.\n#\n# This file is execfile()d with the current directory set to its containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys, os\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#sys.path.append(os.path.abspath('.'))\n\n# -- General configuration -----------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be extensions\n# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.\nextensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage']\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'Redisco'\ncopyright = u'2010, Tim Medina'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '0.1'\n# The full version, including alpha/beta/rc tags.\nrelease = '0.1'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of documents that shouldn't be included in the build.\n#unused_docs = []\n\n# List of directories, relative to source directory, that shouldn't be searched\n# for source files.\nexclude_trees = ['_build']\n\n# The reST default role (used for this markup: `text`) to use for all documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n\n# -- Options for HTML output ---------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  Major themes that come with\n# Sphinx are currently 'default' and 'sphinxdoc'.\nhtml_theme = 'default'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\n#html_theme_path = []\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_use_modindex = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# If nonempty, this is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = ''\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'Rediscodoc'\n\n\n# -- Options for LaTeX output --------------------------------------------------\n\n# The paper size ('letter' or 'a4').\n#latex_paper_size = 'letter'\n\n# The font size ('10pt', '11pt' or '12pt').\n#latex_font_size = '10pt'\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title, author, documentclass [howto/manual]).\nlatex_documents = [\n  ('index', 'Redisco.tex', u'Redisco Documentation',\n   u'Tim Medina', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# Additional stuff for the LaTeX preamble.\n#latex_preamble = ''\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_use_modindex = True\n"
  },
  {
    "path": "docs/index.rst",
    "content": ".. Redisco documentation master file, created by\n   sphinx-quickstart on Mon May 24 21:29:02 2010.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nWelcome to Redisco's documentation!\n===================================\n\nContents:\n\n.. toctree::\n   :maxdepth: 2\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n\n"
  },
  {
    "path": "redisco/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport redis\n\nclass Client(object):\n    def __init__(self, **kwargs):\n        self.connection_settings = kwargs or {'host': 'localhost',\n                'port': 6379, 'db': 0}\n\n    def redis(self):\n        return redis.Redis(**self.connection_settings)\n\n    def update(self, d):\n        self.connection_settings.update(d)\n\ndef connection_setup(**kwargs):\n    global connection, client\n    if client:\n        client.update(kwargs)\n    else:\n        client = Client(**kwargs)\n    connection = client.redis()\n\ndef get_client():\n    global connection\n    return connection\n\nclient = Client()\nconnection = client.redis()\n\n__all__ = ['connection_setup', 'get_client']\n"
  },
  {
    "path": "redisco/containers.py",
    "content": "\"\"\"\nThis module contains the container classes to create objects\nthat persist directly in a Redis server.\n\"\"\"\n\nimport collections\nfrom functools import partial\n\n\nclass Container(object):\n    \"\"\"Create a container object saved in Redis.\n\n    Arguments:\n        key -- the Redis key this container is stored at\n        db  -- the Redis client object. Default: None\n\n    When ``db`` is not set, the gets the default connection from\n    ``redisco.connection`` module.\n    \"\"\"\n\n    def __init__(self, key, db=None, pipeline=None):\n        self._db = db\n        self.key = key\n        self.pipeline = pipeline\n\n    def clear(self):\n        \"\"\"Remove container from Redis database.\"\"\"\n        del self.db[self.key]\n\n    def __getattribute__(self, att):\n        if att in object.__getattribute__(self, 'DELEGATEABLE_METHODS'):\n            return partial(getattr(object.__getattribute__(self, 'db'), att), self.key)\n        else:\n            return object.__getattribute__(self, att)\n\n\n    @property\n    def db(self):\n        if self.pipeline:\n            return self.pipeline\n        if self._db:\n            return self._db\n        if hasattr(self, 'db_cache') and self.db_cache:\n            return self.db_cache\n        else:\n            from redisco import connection\n            self.db_cache = connection\n            return self.db_cache\n\n    DELEGATEABLE_METHODS = ()\n\n\nclass Set(Container):\n    \"\"\"A set stored in Redis.\"\"\"\n\n    def add(self, value):\n        \"\"\"Add the specified member to the Set.\"\"\"\n        self.sadd(value)\n\n    def remove(self, value):\n        \"\"\"Remove the value from the redis set.\"\"\"\n        if not self.srem(value):\n            raise KeyError, value\n        \n    def pop(self):\n        \"\"\"Remove and return (pop) a random element from the Set.\"\"\"\n        return self.spop()\n\n    def discard(self, value):\n        \"\"\"Remove element elem from the set if it is present.\"\"\"\n        self.srem(value)\n\n    def __len__(self):\n        \"\"\"Return the cardinality of set.\"\"\"\n        return self.scard()\n\n    def __repr__(self):\n        return \"<%s '%s' %s>\" % (self.__class__.__name__, self.key,\n                self.members)\n\n    # TODO: Note, the elem argument to the __contains__(), remove(),\n    #       and discard() methods may be a set\n    def __contains__(self, value):\n        return self.sismember(value)\n\n    def isdisjoint(self, other):\n        \"\"\"Return True if the set has no elements in common with other.\"\"\"\n        return not bool(self.db.sinter([self.key, other.key]))\n\n    def issubset(self, other):\n        \"\"\"Test whether every element in the set is in other.\"\"\"\n        return self <= other\n\n    def __le__(self, other):\n        return self.db.sinter([self.key, other.key]) == self.all()\n\n    def __lt__(self, other):\n        \"\"\"Test whether the set is a true subset of other.\"\"\"\n        return self <= other and self != other\n\n    def __eq__(self, other):\n        if other.key == self.key:\n            return True\n        slen, olen = len(self), len(other)\n        if olen == slen:\n            return self.members == other.members\n        else:\n            return False\n\n    def issuperset(self, other):\n        \"\"\"Test whether every element in other is in the set.\"\"\"\n        return self >= other\n\n    def __ge__(self, other):\n        \"\"\"Test whether every element in other is in the set.\"\"\"\n        return self.db.sinter([self.key, other.key]) == other.all()\n    \n    def __gt__(self, other):\n        \"\"\"Test whether the set is a true superset of other.\"\"\"\n        return self >= other and self != other\n\n\n    # SET Operations\n    def union(self, key, *others):\n        \"\"\"Return a new set with elements from the set and all others.\"\"\"\n        if not isinstance(key, str):\n            raise ValueError(\"String expected.\")\n        self.db.sunionstore(key, [self.key] + [o.key for o in others])\n        return Set(key)\n\n    def intersection(self, key, *others):\n        \"\"\"Return a new set with elements common to the set and all others.\"\"\"\n        if not isinstance(key, str):\n            raise ValueError(\"String expected.\")\n        self.db.sinterstore(key, [self.key] + [o.key for o in others])\n        return Set(key)\n\n    def difference(self, key, *others):\n        \"\"\"Return a new set with elements in the set that are not in the others.\"\"\"\n        if not isinstance(key, str):\n            raise ValueError(\"String expected.\")\n        self.db.sdiffstore(key, [self.key] + [o.key for o in others])\n        return Set(key)\n\n    def update(self, *others):\n        \"\"\"Update the set, adding elements from all others.\"\"\"\n        self.db.sunionstore(self.key, [self.key] + [o.key for o in others])\n\n    def __ior__(self, other):\n        self.db.sunionstore(self.key, [self.key, other.key])\n        return self\n\n    def intersection_update(self, *others):\n        \"\"\"Update the set, keeping only elements found in it and all others.\"\"\"\n        self.db.sinterstore(self.key, [o.key for o in [self.key] + others])\n\n    def __iand__(self, other):\n        self.db.sinterstore(self.key, [self.key, other.key])\n        return self\n\n    def difference_update(self, *others):\n        \"\"\"Update the set, removing elements found in others.\"\"\"\n        self.db.sdiffstore(self.key, [o.key for o in [self.key] + others])\n        \n    def __isub__(self, other):\n        self.db.sdiffstore(self.key, [self.key, other.key])\n        return self\n    \n    def all(self):\n        return self.db.smembers(self.key)\n    members = property(all)\n\n    def copy(self, key):\n        \"\"\"Copy the set to another key and return the new Set.\n\n        WARNING: If the key exists, it overwrites it.\n        \"\"\"\n        copy = Set(key=key, db=self.db)\n        copy.clear()\n        copy |= self\n        return copy\n\n    def __iter__(self):\n        return self.members.__iter__()\n\n    \n    def sinter(self, *other_sets):\n        \"\"\"Performs an intersection between Sets.\n\n        Returns a set of common members. Uses Redis.sinter.\n        \"\"\"\n        return self.db.sinter([self.key] + [s.key for s in other_sets])\n\n    def sunion(self, *other_sets):\n        \"\"\"Union between Sets.\n\n        Returns a set of common members. Uses Redis.sunion.\n        \"\"\"\n        return self.db.sunion([self.key] + [s.key for s in other_sets])\n\n    def sdiff(self, *other_sets):\n        \"\"\"Union between Sets.\n\n        Returns a set of common members. Uses Redis.sdiff.\n        \"\"\"\n        return self.db.sdiff([self.key] + [s.key for s in other_sets])\n\n\n    DELEGATEABLE_METHODS = ('sadd', 'srem', 'spop', 'smembers',\n            'scard', 'sismember', 'srandmember')\n\n\nclass List(Container):\n\n    def all(self):\n        \"\"\"Returns all items in the list.\"\"\"\n        return self.lrange(0, -1)\n    members = property(all)\n\n    def __len__(self):\n        return self.llen()\n\n    def __getitem__(self, index):\n        if isinstance(index, int):\n            return self.lindex(index)\n        elif isinstance(index, slice):\n            indices = index.indices(len(self))\n            return self.lrange(indices[0], indices[1])\n        else:\n            raise TypeError\n\n    def __setitem__(self, index, value):\n        self.lset(index, value)\n\n    def append(self, value):\n        \"\"\"Append the value to the list.\"\"\"\n        self.rpush(value)\n    push = append\n\n    def extend(self, iterable):\n        \"\"\"Extend list by appending elements from the iterable.\"\"\"\n        map(lambda i: self.rpush(i), iterable)\n\n    def count(self, value):\n        \"\"\"Return number of occurrences of value.\"\"\"\n        return self.members.count(value)\n\n    def index(self, value):\n        \"\"\"Return first index of value.\"\"\"\n        return self.all().index(value)\n\n    def pop(self):\n        \"\"\"Remove and return the last item\"\"\"\n        return self.rpop()\n\n    def pop_onto(self, key):\n        \"\"\"\n        Remove an element from the list,\n        atomically add it to the head of the list indicated by key\n        \"\"\"\n        return self.rpoplpush(key)\n\n    def shift(self):\n        \"\"\"Remove and return the first item.\"\"\"\n        return self.lpop()\n\n    def unshift(self, value):\n        \"\"\"Add an element at the beginning of the list.\"\"\"\n        self.lpush(value)\n\n    def remove(self, value, num=1):\n        \"\"\"Remove first occurrence of value.\"\"\"\n        self.lrem(value, num)\n\n    def reverse(self):\n        \"\"\"Reverse in place.\"\"\"\n        r = self[:]\n        r.reverse()\n        self.clear()\n        self.extend(r)\n\n    def copy(self, key):\n        \"\"\"Copy the list to a new list.\n\n        WARNING: If key exists, it clears it before copying.\n        \"\"\"\n        copy = List(key, self.db)\n        copy.clear()\n        copy.extend(self)\n        return copy\n\n    def trim(self, start, end):\n        \"\"\"Trim the list from start to end.\"\"\"\n        self.ltrim(start, end)\n\n    def __iter__(self):\n        return self.members.__iter__()\n\n    def __repr__(self):\n        return \"<%s '%s' %s>\" % (self.__class__.__name__, self.key,\n                self.members)\n\n    DELEGATEABLE_METHODS = ('lrange', 'lpush', 'rpush', 'llen',\n            'ltrim', 'lindex', 'lset', 'lpop', 'lrem', 'rpop', 'rpoplpush')\n\nclass TypedList(object):\n    \"\"\"Create a container to store a list of objects in Redis.\n\n    Arguments:\n        key -- the Redis key this container is stored at\n        target_type -- can be a Python object or a redisco model class.\n\n    Optional Arguments:\n        type_args -- additional args to pass to type constructor (tuple)\n        type_kwargs -- additional kwargs to pass to type constructor (dict)\n\n    If target_type is not a redisco model class, the target_type should\n    also a callable that casts the (string) value of a list element into\n    target_type. E.g. str, unicode, int, float -- using this format:\n\n        target_type(string_val_of_list_elem, *type_args, **type_kwargs)\n\n    target_type also accepts a string that refers to a redisco model.\n    \"\"\"\n\n    def __init__(self, key, target_type, type_args=[], type_kwargs={}, **kwargs):\n        self.list = List(key, **kwargs)\n        self.klass = self.value_type(target_type)\n        self._klass_args = type_args\n        self._klass_kwargs = type_kwargs\n        from models.base import Model\n        self._redisco_model = issubclass(self.klass, Model)\n\n    def value_type(self, target_type):\n        if isinstance(target_type, basestring):\n            t = target_type\n            from models.base import get_model_from_key\n            target_type = get_model_from_key(target_type)\n            if target_type is None:\n                raise ValueError(\"Unknown Redisco class %s\" % t)\n        return target_type\n\n    def typecast_item(self, value):\n        if self._redisco_model:\n            return self.klass.objects.get_by_id(value)\n        else:\n            return self.klass(value, *self._klass_args, **self._klass_kwargs)\n\n    def typecast_iter(self, values):\n        if self._redisco_model:\n            return filter(lambda o: o is not None, [self.klass.objects.get_by_id(v) for v in values])\n        else:\n            return [self.klass(v, *self._klass_args, **self._klass_kwargs) for v in values]\n\n    def all(self):\n        \"\"\"Returns all items in the list.\"\"\"\n        return self.typecast_iter(self.list.all())\n\n    def __len__(self):\n        return len(self.list)\n\n    def __getitem__(self, index):\n        val = self.list[index]\n        if isinstance(index, slice):\n            return self.typecast_iter(val)\n        else:\n            return self.typecast_item(val)\n\n    def typecast_stor(self, value):\n        if self._redisco_model:\n            return value.id\n        else:\n            return value\n\n    def append(self, value):\n        self.list.append(self.typecast_stor(value))\n\n    def extend(self, iter):\n        self.list.extend(map(lambda i: self.typecast_stor(i), iter))\n\n    def __setitem__(self, index, value):\n        self.list[index] = self.typecast_stor(value)\n\n    def __iter__(self):\n        for i in xrange(len(self.list)):\n            yield self[i]\n\n    def __repr__(self):\n        return repr(self.typecast_iter(self.list))\n\nclass SortedSet(Container):\n\n    def add(self, member, score):\n        \"\"\"Adds member to the set.\"\"\"\n        self.zadd(member, score)\n\n    def remove(self, member):\n        \"\"\"Removes member from set.\"\"\"\n        self.zrem(member)\n\n    def incr_by(self, member, increment):\n        \"\"\"Increments the member by increment.\"\"\"\n        self.zincrby(member, increment)\n\n    def rank(self, member):\n        \"\"\"Return the rank (the index) of the element.\"\"\"\n        return self.zrank(member)\n\n    def revrank(self, member):\n        \"\"\"Return the rank of the member in reverse order.\"\"\"\n        return self.zrevrank(member)\n\n    def __getitem__(self, index):\n        if isinstance(index, slice):\n            return self.zrange(index.start, index.stop)\n        else:\n            return self.zrange(index, index)[0]\n\n    def score(self, member):\n        \"\"\"Returns the score of member.\"\"\"\n        return self.zscore(member)\n\n    def __len__(self):\n        return self.zcard()\n\n    def __contains__(self, val):\n        return self.zscore(val) is not None\n\n    @property\n    def members(self):\n        \"\"\"Returns the members of the set.\"\"\"\n        return self.zrange(0, -1)\n\n    @property\n    def revmembers(self):\n        \"\"\"Returns the members of the set in reverse.\"\"\"\n        return self.zrevrange(0, -1)\n\n    def __iter__(self):\n        return self.members.__iter__()\n\n    def __reversed__(self):\n        return self.revmembers.__iter__()\n\n    def __repr__(self):\n        return \"<%s '%s' %s>\" % (self.__class__.__name__, self.key,\n                self.members)\n\n    @property\n    def _min_score(self):\n        return self.zscore(self.__getitem__(0))\n\n    @property\n    def _max_score(self):\n        return self.zscore(self.__getitem__(-1))\n\n    def lt(self, v, limit=None, offset=None):\n        \"\"\"Returns the list of the members of the set that have scores\n        less than v.\n        \"\"\"\n        if limit is not None and offset is None:\n            offset = 0\n        return self.zrangebyscore(self._min_score, \"(%f\" % v,\n                start=offset, num=limit)\n\n    def le(self, v, limit=None, offset=None):\n        \"\"\"Returns the list of the members of the set that have scores\n        less than or equal to v.\n        \"\"\"\n        if limit is not None and offset is None:\n            offset = 0\n        return self.zrangebyscore(self._min_score, v,\n                start=offset, num=limit)\n\n    def gt(self, v, limit=None, offset=None):\n        \"\"\"Returns the list of the members of the set that have scores\n        greater than v.\n        \"\"\"\n        if limit is not None and offset is None:\n            offset = 0\n        return self.zrangebyscore(\"(%f\" % v, self._max_score,\n                start=offset, num=limit)\n\n    def ge(self, v, limit=None, offset=None):\n        \"\"\"Returns the list of the members of the set that have scores\n        greater than or equal to v.\n        \"\"\"\n        if limit is not None and offset is None:\n            offset = 0\n        return self.zrangebyscore(\"(%f\" % v, self._max_score,\n                start=offset, num=limit)\n\n    def between(self, min, max, limit=None, offset=None):\n        \"\"\"Returns the list of the members of the set that have scores\n        between min and max.\n        \"\"\"\n        if limit is not None and offset is None:\n            offset = 0\n        return self.zrangebyscore(min, max,\n                start=offset, num=limit)\n\n    def eq(self, value):\n        \"\"\"Returns the list of the members of the set that have scores\n        equal to value.\n        \"\"\"\n        return self.zrangebyscore(value, value)\n\n    DELEGATEABLE_METHODS = ('zadd', 'zrem', 'zincrby', 'zrank',\n            'zrevrank', 'zrange', 'zrevrange', 'zrangebyscore', 'zcard',\n            'zscore', 'zremrangebyrank', 'zremrangebyscore')\n\n\nclass NonPersistentList(object):\n    def __init__(self, l):\n        self._list = l\n\n    @property\n    def members(self):\n        return self._list\n\n    def __iter__(self):\n        return iter(self.members)\n\n    def __len__(self):\n        return len(self._list)\n\n\nclass Hash(Container, collections.MutableMapping):\n\n    def __getitem__(self, att):\n        return self.hget(att)\n\n    def __setitem__(self, att, val):\n        self.hset(att, val)\n\n    def __delitem__(self, att):\n        self.hdel(att)\n\n    def __len__(self):\n        return self.hlen()\n\n    def __iter__(self):\n        return self.hgetall().__iter__()\n\n    def __contains__(self, att):\n        return self.hexists(att)\n\n    def __repr__(self):\n        return \"<%s '%s' %s>\" % (self.__class__.__name__, self.key, self.hgetall())\n\n    def keys(self):\n        return self.hkeys()\n\n    def values(self):\n        return self.hvals()\n\n    def _get_dict(self):\n        return self.hgetall()\n\n    def _set_dict(self, new_dict):\n        self.clear()\n        self.update(new_dict)\n\n    dict = property(_get_dict, _set_dict)\n\n\n    DELEGATEABLE_METHODS = ('hlen', 'hset', 'hdel', 'hkeys',\n            'hgetall', 'hvals', 'hget', 'hexists', 'hincrby',\n            'hmget', 'hmset')\n"
  },
  {
    "path": "redisco/models/__init__.py",
    "content": "from base import *\nfrom attributes import *\nfrom exceptions import *\n\n__all__ = ['Model', 'Attribute', 'BooleanField', 'IntegerField',\n        'Counter', 'FloatField', 'DateTimeField', 'DateField',\n        'ReferenceField', 'ListField', 'ValidationError', 'from_key',\n        'ValidationError', 'MissingID', 'AttributeNotIndexed',\n        'FieldValidationError', 'BadKeyError']\n"
  },
  {
    "path": "redisco/models/attributes.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nDefines the fields that can be added to redisco models.\n\"\"\"\nimport time\nfrom datetime import datetime, date\nfrom redisco.containers import List\nfrom exceptions import FieldValidationError\n\n__all__ = ['Attribute', 'CharField', 'ListField', 'DateTimeField',\n        'DateField', 'ReferenceField', 'IntegerField',\n        'FloatField', 'BooleanField', 'Counter', 'ZINDEXABLE']\n\n\nclass Attribute(object):\n    \"\"\"Defines an attribute of the model.\n\n    The attribute accepts strings and are stored in Redis as\n    they are - strings.\n\n    Options\n        name      -- alternate name of the attribute. This will be used\n                     as the key to use when interacting with Redis.\n        indexed   -- Index this attribute. Unindexed attributes cannot\n                     be used in queries. Default: True.\n        unique    -- validates the uniqueness of the value of the\n                     attribute.\n        validator -- a callable that can validate the value of the\n                     attribute.\n        default   -- Initial value of the attribute.\n\n    \"\"\"\n    def __init__(self,\n                 name=None,\n                 indexed=True,\n                 required=False,\n                 validator=None,\n                 unique=False,\n                 default=None):\n        self.name = name\n        self.indexed = indexed\n        self.required = required\n        self.validator = validator\n        self.default = default\n        self.unique = unique\n\n    def __get__(self, instance, owner):\n        try:\n            return getattr(instance, '_' + self.name)\n        except AttributeError:\n            if not instance.is_new():\n                val = instance.db.hget(instance.key(), self.name)\n                if val is not None:\n                    val = self.typecast_for_read(val)\n                self.__set__(instance, val)\n                return val\n            else:\n                self.__set__(instance, self.default)\n                return self.default\n\n\n    def __set__(self, instance, value):\n        setattr(instance, '_' + self.name, value)\n\n    def typecast_for_read(self, value):\n        \"\"\"Typecasts the value for reading from Redis.\"\"\"\n        # The redis client encodes all unicode data to utf-8 by default.\n        return value.decode('utf-8')\n\n    def typecast_for_storage(self, value):\n        \"\"\"Typecasts the value for storing to Redis.\"\"\"\n        try:\n            return unicode(value)\n        except UnicodeError:\n            return value.decode('utf-8')\n\n    def value_type(self):\n        return unicode\n\n    def acceptable_types(self):\n        return basestring\n\n    def validate(self, instance):\n        val = getattr(instance, self.name)\n        errors = []\n        # type_validation\n        if val and not isinstance(val, self.acceptable_types()):\n            errors.append((self.name, 'bad type',))\n        # validate first standard stuff\n        if self.required:\n            if val is None or not unicode(val).strip():\n                errors.append((self.name, 'required'))\n        # validate uniquness\n        if val and self.unique:\n            error = self.validate_uniqueness(instance, val)\n            if error:\n                errors.append(error)\n        # validate using validator\n        if self.validator:\n            r = self.validator(self.name, val)\n            if r:\n                errors.extend(r)\n        if errors:\n            raise FieldValidationError(errors)\n\n    def validate_uniqueness(self, instance, val):\n        encoded = self.typecast_for_storage(val)\n        same = len(instance.__class__.objects.filter(**{self.name: encoded}))\n        if same > (0 if instance.is_new() else 1):\n            return (self.name, 'not unique',)\n\n\nclass CharField(Attribute):\n\n    def __init__(self, max_length=255, **kwargs):\n        super(CharField, self).__init__(**kwargs)\n        self.max_length = max_length\n\n    def validate(self, instance):\n        errors = []\n        try:\n            super(CharField, self).validate(instance)\n        except FieldValidationError as err:\n            errors.extend(err.errors)\n        \n        val = getattr(instance, self.name)\n          \n        if val and len(val) > self.max_length:\n            errors.append((self.name, 'exceeds max length'))\n        \n        if errors:\n            raise FieldValidationError(errors)\n\n\nclass BooleanField(Attribute):\n    def typecast_for_read(self, value):\n        return bool(int(value))\n\n    def typecast_for_storage(self, value):\n        if value is None:\n            return \"0\"\n        return \"1\" if value else \"0\"\n\n    def value_type(self):\n        return bool\n\n    def acceptable_types(self):\n        return self.value_type()\n\n\nclass IntegerField(Attribute):\n    def typecast_for_read(self, value):\n        return int(value)\n\n    def typecast_for_storage(self, value):\n        if value is None:\n            return \"0\"\n        return unicode(value)\n\n    def value_type(self):\n        return int\n\n    def acceptable_types(self):\n        return (int, long)\n\n\nclass FloatField(Attribute):\n    def typecast_for_read(self, value):\n        return float(value)\n\n    def typecast_for_storage(self, value):\n        if value is None:\n            return \"0\"\n        return \"%f\" % value\n\n    def value_type(self):\n        return float\n\n    def acceptable_types(self):\n        return self.value_type()\n\n\nclass DateTimeField(Attribute):\n\n    def __init__(self, auto_now=False, auto_now_add=False, **kwargs):\n        super(DateTimeField, self).__init__(**kwargs)\n        self.auto_now = auto_now\n        self.auto_now_add = auto_now_add\n\n    def typecast_for_read(self, value):\n        try:\n            return datetime.fromtimestamp(float(value))\n        except TypeError, ValueError:\n            return None\n\n    def typecast_for_storage(self, value):\n        if not isinstance(value, datetime):\n            raise TypeError(\"%s should be datetime object, and not a %s\" %\n                    (self.name, type(value)))\n        if value is None:\n            return None\n        return \"%d.%d\" % (time.mktime(value.timetuple()),  value.microsecond)\n\n    def value_type(self):\n        return datetime\n\n    def acceptable_types(self):\n        return self.value_type()\n\nclass DateField(Attribute):\n\n    def __init__(self, auto_now=False, auto_now_add=False, **kwargs):\n        super(DateField, self).__init__(**kwargs)\n        self.auto_now = auto_now\n        self.auto_now_add = auto_now_add\n\n    def typecast_for_read(self, value):\n        try:\n            return date.fromtimestamp(float(value))\n        except TypeError, ValueError:\n            return None\n\n    def typecast_for_storage(self, value):\n        if not isinstance(value, date):\n            raise TypeError(\"%s should be date object, and not a %s\" %\n                    (self.name, type(value)))\n        if value is None:\n            return None\n        return \"%f\" % time.mktime(value.timetuple())\n\n    def value_type(self):\n        return date\n\n    def acceptable_types(self):\n        return self.value_type()\n\nclass ListField(object):\n    \"\"\"Stores a list of objects.\n\n    target_type -- can be a Python object or a redisco model class.\n\n    If target_type is not a redisco model class, the target_type should\n    also a callable that casts the (string) value of a list element into\n    target_type. E.g. str, unicode, int, float.\n\n    ListField also accepts a string that refers to a redisco model.\n\n    \"\"\"\n    def __init__(self, target_type,\n                 name=None,\n                 indexed=True,\n                 required=False,\n                 validator=None,\n                 default=None):\n        self._target_type = target_type\n        self.name = name\n        self.indexed = indexed\n        self.required = required\n        self.validator = validator\n        self.default = default or []\n        from base import Model\n        self._redisco_model = (isinstance(target_type, basestring) or\n            issubclass(target_type, Model))\n\n    def __get__(self, instance, owner):\n        try:\n            return getattr(instance, '_' + self.name)\n        except AttributeError:\n            if instance.is_new():\n                val = self.default\n            else:\n                key = instance.key()[self.name]\n                val = List(key).members\n            if val is not None:\n                klass = self.value_type()\n                if self._redisco_model:\n                    val = filter(lambda o: o is not None, [klass.objects.get_by_id(v) for v in val])\n                else:\n                    val = [klass(v) for v in val]\n            self.__set__(instance, val)\n            return val\n\n    def __set__(self, instance, value):\n        setattr(instance, '_' + self.name, value)\n\n    def value_type(self):\n        if isinstance(self._target_type, basestring):\n            t = self._target_type\n            from base import get_model_from_key\n            self._target_type = get_model_from_key(self._target_type)\n            if self._target_type is None:\n                raise ValueError(\"Unknown Redisco class %s\" % t)\n        return self._target_type\n\n    def validate(self, instance):\n        val = getattr(instance, self.name)\n        errors = []\n\n        if val:\n            if not isinstance(val, list):\n                errors.append((self.name, 'bad type'))\n            else:\n                for item in val:\n                    if not isinstance(item, self.value_type()):\n                        errors.append((self.name, 'bad type in list'))\n\n        # validate first standard stuff\n        if self.required:\n            if not val:\n                errors.append((self.name, 'required'))\n        # validate using validator\n        if self.validator:\n            r = self.validator(val)\n            if r:\n                errors.extend(r)\n        if errors:\n            raise FieldValidationError(errors)\n\n\nclass ReferenceField(object):\n    def __init__(self,\n                 target_type,\n                 name=None,\n                 attname=None,\n                 indexed=True,\n                 required=False,\n                 related_name=None,\n                 default=None,\n                 validator=None):\n        self._target_type = target_type\n        self.name = name\n        self.indexed = indexed\n        self.required = required\n        self._attname = attname\n        self._related_name = related_name\n        self.validator = validator\n        self.default = default\n\n    def __set__(self, instance, value):\n        if not isinstance(value, self.value_type()) and \\\n                value is not None:\n            raise TypeError\n        # remove the cached value from the instance\n        if hasattr(instance, '_' + self.name):\n            delattr(instance, '_' + self.name)\n        setattr(instance, self.attname, value.id)\n\n    def __get__(self, instance, owner):\n        try:\n            if not hasattr(instance, '_' + self.name):\n                o = self.value_type().objects.get_by_id(\n                                    getattr(instance, self.attname))\n                setattr(instance, '_' + self.name, o)\n            return getattr(instance, '_' + self.name)\n        except AttributeError:\n            setattr(instance, '_' + self.name, self.default)\n            return self.default\n\n    def value_type(self):\n        return self._target_type\n\n    @property\n    def attname(self):\n        if self._attname is None:\n            self._attname = self.name + '_id'\n        return self._attname\n\n    @property\n    def related_name(self):\n        return self._related_name \n\n    def validate(self, instance):\n        val = getattr(instance, self.name)\n        errors = []\n\n        if val:\n            if not isinstance(val, self.value_type()):\n                errors.append((self.name, 'bad type for reference'))\n\n        # validate first standard stuff\n        if self.required:\n            if not val:\n                errors.append((self.name, 'required'))\n        # validate using validator\n        if self.validator:\n            r = self.validator(val)\n            if r:\n                errors.extend(r)\n        if errors:\n            raise FieldValidationError(errors)\n\n\nclass Counter(IntegerField):\n    def __init__(self, **kwargs):\n        super(Counter, self).__init__(**kwargs)\n        if not kwargs.has_key('default') or self.default is None:\n            self.default = 0\n\n    def __set__(self, instance, value):\n        raise AttributeError(\"can't set a counter.\")\n\n    def __get__(self, instance, owner):\n        if not instance.is_new():\n            v = instance.db.hget(instance.key(), self.name)\n            if v is None:\n                return 0\n            return int(v)\n        else:\n            return 0\n\n\nZINDEXABLE = (IntegerField, DateTimeField, DateField, FloatField, Counter)\n"
  },
  {
    "path": "redisco/models/base.py",
    "content": "import time\nfrom datetime import datetime, date\nimport redisco\nfrom redisco.containers import Set, List, SortedSet, NonPersistentList\nfrom attributes import *\nfrom key import Key\nfrom managers import ManagerDescriptor, Manager\nfrom utils import _encode_key\nfrom exceptions import FieldValidationError, MissingID, BadKeyError\n\n__all__ = ['Model', 'from_key']\n\nZINDEXABLE = (IntegerField, DateTimeField, DateField, FloatField)\n\n##############################\n# Model Class Initialization #\n##############################\n\ndef _initialize_attributes(model_class, name, bases, attrs):\n    \"\"\"Initialize the attributes of the model.\"\"\"\n    model_class._attributes = {}\n    for k, v in attrs.iteritems():\n        if isinstance(v, Attribute):\n            model_class._attributes[k] = v\n            v.name = v.name or k\n\ndef _initialize_referenced(model_class, attribute):\n    \"\"\"Adds a property to the target of a reference field that\n    returns the list of associated objects.\n    \"\"\"\n    # this should be a descriptor\n    def _related_objects(self):\n        return (model_class.objects\n                .filter(**{attribute.attname: self.id}))\n\n    klass = attribute._target_type\n    if isinstance(klass, basestring):\n        return (klass, model_class, attribute)\n    else:\n        related_name = (attribute.related_name or\n                model_class.__name__.lower() + '_set')\n        setattr(klass, related_name,\n                property(_related_objects))\n\ndef _initialize_lists(model_class, name, bases, attrs):\n    \"\"\"Stores the list fields descriptors of a model.\"\"\"\n    model_class._lists = {}\n    for k, v in attrs.iteritems():\n        if isinstance(v, ListField):\n            model_class._lists[k] = v\n            v.name = v.name or k\n\ndef _initialize_references(model_class, name, bases, attrs):\n    \"\"\"Stores the list of reference field descriptors of a model.\"\"\"\n    model_class._references = {}\n    h = {}\n    deferred = []\n    for k, v in attrs.iteritems():\n        if isinstance(v, ReferenceField):\n            model_class._references[k] = v\n            v.name = v.name or k\n            att = Attribute(name=v.attname)\n            h[v.attname] = att\n            setattr(model_class, v.attname, att)\n            refd = _initialize_referenced(model_class, v)\n            if refd:\n                deferred.append(refd)\n    attrs.update(h)\n    return deferred\n\ndef _initialize_indices(model_class, name, bases, attrs):\n    \"\"\"Stores the list of indexed attributes.\"\"\"\n    model_class._indices = []\n    for k, v in attrs.iteritems():\n        if isinstance(v, (Attribute, ListField)) and v.indexed:\n            model_class._indices.append(k)\n    if model_class._meta['indices']:\n        model_class._indices.extend(model_class._meta['indices'])\n\ndef _initialize_counters(model_class, name, bases, attrs):\n    \"\"\"Stores the list of counter fields.\"\"\"\n    model_class._counters = []\n    for k, v in attrs.iteritems():\n        if isinstance(v, Counter):\n            model_class._counters.append(k)\n\ndef _initialize_key(model_class, name):\n    \"\"\"Initializes the key of the model.\"\"\"\n    model_class._key = Key(model_class._meta['key'] or name)\n\n\ndef _initialize_manager(model_class):\n    \"\"\"Initializes the objects manager attribute of the model.\"\"\"\n    model_class.objects = ManagerDescriptor(Manager(model_class))\n\n\nclass ModelOptions(object):\n    \"\"\"Handles options defined in Meta class of the model.\n\n    Example:\n\n        class Person(models.Model):\n            name = models.Attribute()\n\n            class Meta:\n                indices = ('full_name',)\n                db = redis.Redis(host='localhost', port=29909)\n\n    \"\"\"\n    def __init__(self, meta):\n        self.meta = meta\n\n    def get_field(self, field_name):\n        if self.meta is None:\n            return None\n        try:\n            return self.meta.__dict__[field_name]\n        except KeyError:\n            return None\n    __getitem__ = get_field\n\n\n_deferred_refs = []\n\nclass ModelBase(type):\n    \"\"\"Metaclass of the Model.\"\"\"\n\n    def __init__(cls, name, bases, attrs):\n        super(ModelBase, cls).__init__(name, bases, attrs)\n        global _deferred_refs\n        cls._meta = ModelOptions(attrs.pop('Meta', None))\n        deferred = _initialize_references(cls, name, bases, attrs)\n        _deferred_refs.extend(deferred)\n        _initialize_attributes(cls, name, bases, attrs)\n        _initialize_counters(cls, name, bases, attrs)\n        _initialize_lists(cls, name, bases, attrs)\n        _initialize_indices(cls, name, bases, attrs)\n        _initialize_key(cls, name)\n        _initialize_manager(cls)\n        # if targeted by a reference field using a string,\n        # override for next try\n        for target, model_class, att in _deferred_refs:\n            if name == target:\n                att._target_type = cls\n                _initialize_referenced(model_class, att)\n\n\nclass Model(object):\n    __metaclass__ = ModelBase\n\n    def __init__(self, **kwargs):\n        self.update_attributes(**kwargs)\n\n    def is_valid(self):\n        \"\"\"Returns True if all the fields are valid.\n\n        It first validates the fields (required, unique, etc.)\n        and then calls the validate method.\n        \"\"\"\n        self._errors = []\n        for field in self.fields:\n            try:\n                field.validate(self)\n            except FieldValidationError, e:\n                self._errors.extend(e.errors)\n        self.validate()\n        return not bool(self._errors)\n\n    def validate(self):\n        \"\"\"Overriden in the model class.\n\n        Do custom validation here. Add tuples to self._errors.\n\n        Example:\n\n            class Person(Model):\n                name = Attribute(required=True)\n\n                def validate(self):\n                    if name == 'Nemo':\n                        self._errors.append(('name', 'cannot be Nemo'))\n\n        \"\"\"\n        pass\n\n    def update_attributes(self, **kwargs):\n        \"\"\"Updates the attributes of the model.\"\"\"\n        attrs = self.attributes.values() + self.lists.values() \\\n                + self.references.values()\n        for att in attrs:\n            if att.name in kwargs:\n                att.__set__(self, kwargs[att.name])\n\n    def save(self):\n        \"\"\"Saves the instance to the datastore.\"\"\"\n        if not self.is_valid():\n            return self._errors\n        _new = self.is_new()\n        if _new:\n            self._initialize_id()\n        with Mutex(self):\n            self._write(_new)\n        return True\n\n    def key(self, att=None):\n        \"\"\"Returns the Redis key where the values are stored.\"\"\"\n        if att is not None:\n            return self._key[self.id][att]\n        else:\n            return self._key[self.id]\n\n    def delete(self):\n        \"\"\"Deletes the object from the datastore.\"\"\"\n        pipeline = self.db.pipeline()\n        self._delete_from_indices(pipeline)\n        self._delete_membership(pipeline)\n        pipeline.delete(self.key())\n        pipeline.execute()\n\n    def is_new(self):\n        \"\"\"Returns True if the instance is new.\n\n        Newness is based on the presence of the _id attribute.\n        \"\"\"\n        return not hasattr(self, '_id')\n\n    def incr(self, att, val=1):\n        \"\"\"Increments a counter.\"\"\"\n        if att not in self.counters:\n            raise ValueError(\"%s is not a counter.\")\n        self.db.hincrby(self.key(), att, val)\n\n    def decr(self, att, val=1):\n        \"\"\"Decrements a counter.\"\"\"\n        self.incr(att, -1 * val)\n\n\n    @property\n    def attributes_dict(self):\n        \"\"\"Returns the mapping of the model attributes and their\n        values.\n        \"\"\"\n        h = {}\n        for k in self.attributes.keys():\n            h[k] = getattr(self, k)\n        for k in self.lists.keys():\n            h[k] = getattr(self, k)\n        for k in self.references.keys():\n            h[k] = getattr(self, k)\n        return h\n\n\n    @property\n    def id(self):\n        \"\"\"Returns the id of the instance.\n\n        Raises MissingID if the instance is new.\n        \"\"\"\n        if not hasattr(self, '_id'):\n            raise MissingID\n        return self._id\n\n    @id.setter\n    def id(self, val):\n        \"\"\"Returns the id of the instance as a string.\"\"\"\n        self._id = str(val)\n\n    @property\n    def attributes(cls):\n        \"\"\"Return the attributes of the model.\n\n        Returns a dict with models attribute name as keys\n        and attribute descriptors as values.\n        \"\"\"\n        return dict(cls._attributes)\n\n    @property\n    def lists(cls):\n        \"\"\"Returns the lists of the model.\n\n        Returns a dict with models attribute name as keys\n        and ListField descriptors as values.\n        \"\"\"\n        return dict(cls._lists)\n\n    @property\n    def indices(cls):\n        \"\"\"Return a list of the indices of the model.\"\"\"\n        return cls._indices\n\n    @property\n    def references(cls):\n        \"\"\"Returns the mapping of reference fields of the model.\"\"\"\n        return cls._references\n\n    @property\n    def db(cls):\n        \"\"\"Returns the Redis client used by the model.\"\"\"\n        return redisco.get_client()\n\n    @property\n    def errors(self):\n        \"\"\"Returns the list of errors after validation.\"\"\"\n        if not hasattr(self, '_errors'):\n            self.is_valid()\n        return self._errors\n\n    @property\n    def fields(self):\n        \"\"\"Returns the list of field names of the model.\"\"\"\n        return (self.attributes.values() + self.lists.values()\n                + self.references.values())\n\n    @property\n    def counters(cls):\n        \"\"\"Returns the mapping of the counters.\"\"\"\n        return cls._counters\n\n    #################\n    # Class Methods #\n    #################\n\n    @classmethod\n    def exists(cls, id):\n        \"\"\"Checks if the model with id exists.\"\"\"\n        return bool(redisco.get_client().exists(cls._key[str(id)]) or\n                    redisco.get_client().sismember(cls._key['all'], str(id)))\n\n    ###################\n    # Private methods #\n    ###################\n\n    def _initialize_id(self):\n        \"\"\"Initializes the id of the instance.\"\"\"\n        self.id = str(self.db.incr(self._key['id']))\n\n    def _write(self, _new=False):\n        \"\"\"Writes the values of the attributes to the datastore.\n\n        This method also creates the indices and saves the lists\n        associated to the object.\n        \"\"\"\n        pipeline = self.db.pipeline()\n        self._create_membership(pipeline)\n        self._update_indices(pipeline)\n        h = {}\n        # attributes\n        for k, v in self.attributes.iteritems():\n            if isinstance(v, DateTimeField):\n                if v.auto_now:\n                    setattr(self, k, datetime.now())\n                if v.auto_now_add and _new:\n                    setattr(self, k, datetime.now())\n            elif isinstance(v, DateField):\n                if v.auto_now:\n                    setattr(self, k, date.today())\n                if v.auto_now_add and _new:\n                    setattr(self, k, date.today())\n            for_storage = getattr(self, k)\n            if for_storage is not None:\n                h[k] = v.typecast_for_storage(for_storage)\n        # indices\n        for index in self.indices:\n            if index not in self.lists and index not in self.attributes:\n                v = getattr(self, index)\n                if callable(v):\n                    v = v()\n                if v:\n                    try:\n                        h[index] = unicode(v)\n                    except UnicodeError:\n                        h[index] = unicode(v.decode('utf-8'))\n        pipeline.delete(self.key())\n        if h:\n            pipeline.hmset(self.key(), h)\n\n        # lists\n        for k, v in self.lists.iteritems():\n            l = List(self.key()[k], pipeline=pipeline)\n            l.clear()\n            values = getattr(self, k)\n            if values:\n                if v._redisco_model:\n                    l.extend([item.id for item in values])\n                else:\n                    l.extend(values)\n        pipeline.execute()\n\n    ##############\n    # Membership #\n    ##############\n\n    def _create_membership(self, pipeline=None):\n        \"\"\"Adds the id of the object to the set of all objects of the same\n        class.\n        \"\"\"\n        Set(self._key['all'], pipeline=pipeline).add(self.id)\n\n    def _delete_membership(self, pipeline=None):\n        \"\"\"Removes the id of the object to the set of all objects of the\n        same class.\n        \"\"\"\n        Set(self._key['all'], pipeline=pipeline).remove(self.id)\n\n\n    ############\n    # INDICES! #\n    ############\n\n    def _update_indices(self, pipeline=None):\n        \"\"\"Updates the indices of the object.\"\"\"\n        self._delete_from_indices(pipeline)\n        self._add_to_indices(pipeline)\n\n    def _add_to_indices(self, pipeline):\n        \"\"\"Adds the base64 encoded values of the indices.\"\"\"\n        for att in self.indices:\n            self._add_to_index(att, pipeline=pipeline)\n\n    def _add_to_index(self, att, val=None, pipeline=None):\n        \"\"\"\n        Adds the id to the index.\n\n        This also adds to the _indices set of the object.\n        \"\"\"\n        index = self._index_key_for(att)\n        if index is None:\n            return\n        t, index = index\n        if t == 'attribute':\n            pipeline.sadd(index, self.id)\n            pipeline.sadd(self.key()['_indices'], index)\n        elif t == 'list':\n            for i in index:\n                pipeline.sadd(i, self.id)\n                pipeline.sadd(self.key()['_indices'], i)\n        elif t == 'sortedset':\n            zindex, index = index\n            pipeline.sadd(index, self.id)\n            pipeline.sadd(self.key()['_indices'], index)\n            descriptor = self.attributes[att]\n            score = descriptor.typecast_for_storage(getattr(self, att))\n            pipeline.zadd(zindex, self.id, score)\n            pipeline.sadd(self.key()['_zindices'], zindex)\n\n\n    def _delete_from_indices(self, pipeline):\n        \"\"\"Deletes the object's id from the sets(indices) it has been added\n        to and removes its list of indices (used for housekeeping).\n        \"\"\"\n        s = Set(self.key()['_indices'])\n        z = Set(self.key()['_zindices'])\n        for index in s.members:\n            pipeline.srem(index, self.id)\n        for index in z.members:\n            pipeline.zrem(index, self.id)\n        pipeline.delete(s.key)\n        pipeline.delete(z.key)\n\n    def _index_key_for(self, att, value=None):\n        \"\"\"Returns a key based on the attribute and its value.\n\n        The key is used for indexing.\n        \"\"\"\n        if value is None:\n            value = getattr(self, att)\n            if callable(value):\n                value = value()\n        if value is None:\n            return None\n        if att not in self.lists:\n            return self._get_index_key_for_non_list_attr(att, value)\n        else:\n            return self._tuple_for_index_key_attr_list(att, value)\n\n    def _get_index_key_for_non_list_attr(self, att, value):\n        descriptor = self.attributes.get(att)\n        if descriptor and isinstance(descriptor, ZINDEXABLE):\n            sval = descriptor.typecast_for_storage(value)\n            return self._tuple_for_index_key_attr_zset(att, value, sval)\n        elif descriptor:\n            val = descriptor.typecast_for_storage(value)\n            return self._tuple_for_index_key_attr_val(att, val)\n        else:\n            # this is non-attribute index defined in Meta\n            return self._tuple_for_index_key_attr_val(att, value)\n\n    def _tuple_for_index_key_attr_val(self, att, val):\n        return ('attribute', self._index_key_for_attr_val(att, val))\n\n    def _tuple_for_index_key_attr_list(self, att, val):\n        return ('list', [self._index_key_for_attr_val(att, e) for e in val])\n\n    def _tuple_for_index_key_attr_zset(self, att, val, sval):\n        return ('sortedset',\n                (self._key[att], self._index_key_for_attr_val(att, sval)))\n\n    def _index_key_for_attr_val(self, att, val):\n        return self._key[att][_encode_key(val)]\n\n    ##################\n    # Python methods #\n    ##################\n\n    def __hash__(self):\n        return hash(self.key())\n\n    def __eq__(self, other):\n        return isinstance(other, self.__class__) and self.key() == other.key()\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n    def __repr__(self):\n        if not self.is_new():\n            return \"<%s %s>\" % (self.key(), self.attributes_dict)\n        return \"<%s %s>\" % (self.__class__.__name__, self.attributes_dict)\n\n\n\ndef get_model_from_key(key):\n    \"\"\"Gets the model from a given key.\"\"\"\n    _known_models = {}\n    model_name = key.split(':', 2)[0]\n    # populate\n    for klass in Model.__subclasses__():\n        _known_models[klass.__name__] = klass\n    return _known_models.get(model_name, None)\n\n\ndef from_key(key):\n    \"\"\"Returns the model instance based on the key.\n\n    Raises BadKeyError if the key is not recognized by\n    redisco or no defined model can be found.\n    Returns None if the key could not be found.\n    \"\"\"\n    model = get_model_from_key(key)\n    if model is None:\n        raise BadKeyError\n    try:\n        _, id = key.split(':', 2)\n        id = int(id)\n    except ValueError, TypeError:\n        raise BadKeyError\n    return model.objects.get_by_id(id)\n\n\nclass Mutex(object):\n    \"\"\"Implements locking so that other instances may not modify it.\n\n    Code ported from Ohm.\n    \"\"\"\n    def __init__(self, instance):\n        self.instance = instance\n\n    def __enter__(self):\n        self.lock()\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.unlock()\n\n    def lock(self):\n        o = self.instance\n        while not o.db.setnx(o.key('_lock'), self.lock_timeout):\n            lock = o.db.get(o.key('_lock'))\n            if not lock:\n                continue\n            if not self.lock_has_expired(lock):\n                time.sleep(0.5)\n                continue\n            lock = o.db.getset(o.key('_lock'), self.lock_timeout)\n            if not lock:\n                break\n            if self.lock_has_expired(lock):\n                break\n\n    def lock_has_expired(self, lock):\n        return float(lock) < time.time()\n\n    def unlock(self):\n        self.instance.db.delete(self.instance.key('_lock'))\n\n    @property\n    def lock_timeout(self):\n        return \"%f\" % (time.time() + 1.0)\n"
  },
  {
    "path": "redisco/models/exceptions.py",
    "content": "##########\n# ERRORS #\n##########\nclass Error(StandardError):\n    pass\n\nclass ValidationError(Error):\n    pass\n\nclass MissingID(Error):\n    pass\n\nclass AttributeNotIndexed(Error):\n    pass\n\nclass FieldValidationError(Error):\n\n    def __init__(self, errors, *args, **kwargs):\n        super(FieldValidationError, self).__init__(*args, **kwargs)\n        self._errors = errors\n\n    @property\n    def errors(self):\n        return self._errors\n\nclass BadKeyError(Error):\n    pass\n"
  },
  {
    "path": "redisco/models/key.py",
    "content": "class Key(str):\n    def __getitem__(self, key):\n        return Key(\"%s:%s\" % (self, key,))\n"
  },
  {
    "path": "redisco/models/managers.py",
    "content": "from modelset import ModelSet\n\n############\n# Managers #\n############\n\nclass ManagerDescriptor(object):\n    def __init__(self, manager):\n        self.manager = manager\n\n    def __get__(self, instance, owner):\n        if instance != None:\n            raise AttributeError\n        return self.manager\n\n\nclass Manager(object):\n    def __init__(self, model_class):\n        self.model_class = model_class\n\n    def get_model_set(self):\n        return ModelSet(self.model_class)\n\n    def all(self):\n        return self.get_model_set()\n\n    def create(self, **kwargs):\n        return self.get_model_set().create(**kwargs)\n\n    def get_or_create(self, **kwargs):\n        return self.get_model_set().get_or_create(**kwargs)\n\n    def filter(self, **kwargs):\n        return self.get_model_set().filter(**kwargs)\n\n    def exclude(self, **kwargs):\n        return self.get_model_set().exclude(**kwargs)\n\n    def get_by_id(self, id):\n        return self.get_model_set().get_by_id(id)\n\n    def order(self, field):\n        return self.get_model_set().order(field)\n\n    def zfilter(self, **kwargs):\n        return self.get_model_set().zfilter(**kwargs)\n\n\n"
  },
  {
    "path": "redisco/models/modelset.py",
    "content": "\"\"\"\nHandles the queries.\n\"\"\"\nfrom attributes import IntegerField, DateTimeField\nimport redisco\nfrom redisco.containers import SortedSet, Set, List, NonPersistentList\nfrom exceptions import AttributeNotIndexed\nfrom utils import _encode_key\nfrom attributes import ZINDEXABLE\n\n# Model Set\nclass ModelSet(Set):\n    def __init__(self, model_class):\n        self.model_class = model_class\n        self.key = model_class._key['all']\n        self._db = redisco.get_client()\n        self._filters = {}\n        self._exclusions = {}\n        self._zfilters = []\n        self._ordering = []\n        self._limit = None\n        self._offset = None\n\n    #################\n    # MAGIC METHODS #\n    #################\n\n\n    def __getitem__(self, index):\n        if isinstance(index, slice):\n            return map(lambda id: self._get_item_with_id(id), self._set[index])\n        else:\n            id = self._set[index]\n            if id:\n                return self._get_item_with_id(id)\n            else:\n                raise IndexError\n\n    def __repr__(self):\n        if len(self._set) > 30:\n            m = self._set[:30]\n        else:\n            m = self._set\n        s = map(lambda id: self._get_item_with_id(id), m)\n        return \"%s\" % s\n\n    def __iter__(self):\n        for id in self._set:\n            yield self._get_item_with_id(id)\n\n    def __len__(self):\n        return len(self._set)\n\n    def __contains__(self, val):\n        return val.id in self._set\n\n    ##########################################\n    # METHODS THAT RETURN A SET OF INSTANCES #\n    ##########################################\n\n    def get_by_id(self, id):\n        if self.model_class.exists(id):\n            return self._get_item_with_id(id)\n\n    def first(self):\n        try:\n            return self.limit(1).__getitem__(0)\n        except IndexError:\n            return None\n\n\n    #####################################\n    # METHODS THAT MODIFY THE MODEL SET #\n    #####################################\n\n    def filter(self, **kwargs):\n        clone = self._clone()\n        if not clone._filters:\n            clone._filters = {}\n        clone._filters.update(kwargs)\n        return clone\n\n    def exclude(self, **kwargs):\n        clone = self._clone()\n        if not clone._exclusions:\n            clone._exclusions = {}\n        clone._exclusions.update(kwargs)\n        return clone\n\n    def zfilter(self, **kwargs):\n        clone = self._clone()\n        if not clone._zfilters:\n            clone._zfilters = []\n        clone._zfilters.append(kwargs)\n        return clone\n\n    # this should only be called once\n    def order(self, field):\n        fname = field.lstrip('-')\n        if fname not in self.model_class._indices:\n            raise ValueError(\"Order parameter should be an indexed attribute.\")\n        alpha = True\n        if fname in self.model_class._attributes:\n            v = self.model_class._attributes[fname]\n            alpha = not isinstance(v, ZINDEXABLE)\n        clone = self._clone()\n        if not clone._ordering:\n            clone._ordering = []\n        clone._ordering.append((field, alpha,))\n        return clone\n\n    def limit(self, n, offset=0):\n        clone = self._clone()\n        clone._limit = n\n        clone._offset = offset\n        return clone\n\n    def create(self, **kwargs):\n        instance = self.model_class(**kwargs)\n        if instance.save():\n            return instance\n        else:\n            return None\n\n    def all(self):\n        return self._clone()\n\n    def get_or_create(self, **kwargs):\n        opts = {}\n        for k, v in kwargs.iteritems():\n            if k in self.model_class._indices:\n                opts[k] = v\n        o = self.filter(**opts).first()\n        if o:\n            return o\n        else:\n            return self.create(**kwargs)\n\n    #\n\n    @property\n    def db(self):\n        return self._db\n\n    ###################\n    # PRIVATE METHODS #\n    ###################\n\n    @property\n    def _set(self):\n        # For performance reasons, only one zfilter is allowed.\n        if hasattr(self, '_cached_set'):\n            return self._cached_set\n        if self._zfilters:\n            self._cached_set = self._add_zfilters()\n            return self._cached_set\n        s = Set(self.key)\n        self._expire_or_delete = []\n        if self._filters:\n            s = self._add_set_filter(s)\n        if self._exclusions:\n            s = self._add_set_exclusions(s)\n        n = self._order(s.key)\n        self._cached_set = list(self._order(s.key))\n        for key in filter(lambda key: key != self.key, self._expire_or_delete):\n            del self.db[key]\n        return self._cached_set\n\n    def _add_set_filter(self, s):\n        indices = []\n        for k, v in self._filters.iteritems():\n            index = self._build_key_from_filter_item(k, v)\n            if k not in self.model_class._indices:\n                raise AttributeNotIndexed(\n                        \"Attribute %s is not indexed in %s class.\" %\n                        (k, self.model_class.__name__))\n            indices.append(index)\n        new_set_key = \"~%s.%s\" % (\"+\".join([self.key] + indices), id(self))\n        s.intersection(new_set_key, *[Set(n) for n in indices])\n        self._expire_or_delete.append(new_set_key)\n        return Set(new_set_key)\n\n    def _add_set_exclusions(self, s):\n        indices = []\n        for k, v in self._exclusions.iteritems():\n            index = self._build_key_from_filter_item(k, v)\n            if k not in self.model_class._indices:\n                raise AttributeNotIndexed(\n                        \"Attribute %s is not indexed in %s class.\" %\n                        (k, self.model_class.__name__))\n            indices.append(index)\n        new_set_key = \"~%s.%s\" % (\"-\".join([self.key] + indices), id(self))\n        s.difference(new_set_key, *[Set(n) for n in indices])\n        self._expire_or_delete.append(new_set_key)\n        return Set(new_set_key)\n\n    def _add_zfilters(self):\n        k, v = self._zfilters[0].items()[0]\n        try:\n            att, op = k.split('__')\n        except ValueError:\n            raise ValueError(\"zfilter should have an operator.\")\n        index = self.model_class._key[att]\n        desc = self.model_class._attributes[att]\n        zset = SortedSet(index)\n        limit, offset = self._get_limit_and_offset()\n        if isinstance(v, (tuple, list,)):\n            min, max = v\n            min = float(desc.typecast_for_storage(min))\n            max = float(desc.typecast_for_storage(max))\n        else:\n            v = float(desc.typecast_for_storage(v))\n        if op == 'lt':\n            return zset.lt(v, limit, offset)\n        elif op == 'gt':\n            return zset.gt(v, limit, offset)\n        elif op == 'gte':\n            return zset.ge(v, limit, offset)\n        elif op == 'lte':\n            return zset.le(v, limit, offset)\n        elif op == 'in':\n            return zset.between(min, max, limit, offset)\n\n    def _order(self, skey):\n        if self._ordering:\n            return self._set_with_ordering(skey)\n        else:\n            return self._set_without_ordering(skey)\n\n    def _set_with_ordering(self, skey):\n        num, start = self._get_limit_and_offset()\n        old_set_key = skey\n        for ordering, alpha in self._ordering:\n            if ordering.startswith('-'):\n                desc = True\n                ordering = ordering.lstrip('-')\n            else:\n                desc = False\n            new_set_key = \"%s#%s.%s\" % (old_set_key, ordering, id(self))\n            by = \"%s->%s\" % (self.model_class._key['*'], ordering)\n            self.db.sort(old_set_key,\n                         by=by,\n                         store=new_set_key,\n                         alpha=alpha,\n                         start=start,\n                         num=num,\n                         desc=desc)\n            self._expire_or_delete.append(old_set_key)\n            self._expire_or_delete.append(new_set_key)\n            return List(new_set_key)\n\n    def _set_without_ordering(self, skey):\n        # sort by id\n        num, start = self._get_limit_and_offset()\n        old_set_key = skey\n        new_set_key = \"%s#.%s\" % (old_set_key, id(self))\n        self.db.sort(old_set_key,\n                     store=new_set_key,\n                     start=start,\n                     num=num)\n        self._expire_or_delete.append(old_set_key)\n        self._expire_or_delete.append(new_set_key)\n        return List(new_set_key)\n\n    def _get_limit_and_offset(self):\n        if (self._limit is not None and self._offset is None) or \\\n                (self._limit is None and self._offset is not None):\n                    raise \"Limit and offset must be specified\"\n\n        if self._limit is None:\n            return (None, None)\n        else:\n            return (self._limit, self._offset)\n\n    def _get_item_with_id(self, id):\n        instance = self.model_class()\n        instance._id = str(id)\n        return instance\n\n    def _build_key_from_filter_item(self, index, value):\n        desc = self.model_class._attributes.get(index)\n        if desc:\n            value = desc.typecast_for_storage(value)\n        return self.model_class._key[index][_encode_key(value)]\n\n    def _clone(self):\n        klass = self.__class__\n        c = klass(self.model_class)\n        if self._filters:\n            c._filters = self._filters\n        if self._exclusions:\n            c._exclusions = self._exclusions\n        if self._zfilters:\n            c._zfilters = self._zfilters\n        if self._ordering:\n            c._ordering = self._ordering\n        c._limit = self._limit\n        c._offset = self._offset\n        return c\n\n"
  },
  {
    "path": "redisco/models/utils.py",
    "content": "import base64\n\ndef _encode_key(s):\n    try:\n        return base64.b64encode(str(s)).replace(\"\\n\", \"\")\n    except UnicodeError, e:\n        return base64.b64encode(s.encode('utf-8')).replace(\"\\n\", \"\")\n"
  },
  {
    "path": "redisco/models/validation.py",
    "content": ""
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\nimport os\n\nversion = '0.1.3'\n\ntry:\n    from setuptools import setup\nexcept ImportError:\n    from distutils.core import setup\n\ndef read(fname):\n    return open(os.path.join(os.path.dirname(__file__), fname)).read()\n\nsetup(name='redisco',\n      version=version,\n      description='Python Containers and Simple Models for Redis',\n      url='http://github.com/iamteem/redisco',\n      download_url='',\n      long_description=read('README.rst'),\n      author='Tim Medina',\n      author_email='iamteem@gmail.com',\n      maintainer='Tim Medina',\n      maintainer_email='iamteem@gmail.com',\n      keywords=['Redis', 'model', 'container'],\n      license='MIT',\n      packages=['redisco', 'redisco.models'],\n      test_suite='tests.all_tests',\n      classifiers=[\n        'Development Status :: 4 - Beta',\n        'Environment :: Console',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: MIT License',\n        'Operating System :: OS Independent',\n        'Programming Language :: Python'],\n    )\n\n"
  },
  {
    "path": "tests/__init__.py",
    "content": "import os\nimport unittest\nfrom connection import ConnectionTestCase\nfrom containers import (SetTestCase, ListTestCase, TypedListTestCase, \n        SortedSetTestCase, HashTestCase)\nfrom models import (ModelTestCase, DateFieldTestCase, FloatFieldTestCase,\n        BooleanFieldTestCase, ListFieldTestCase, ReferenceFieldTestCase,\n        DateTimeFieldTestCase, CounterFieldTestCase, CharFieldTestCase,\n        MutexTestCase,)\n\nimport redisco\nREDIS_DB = int(os.environ.get('REDIS_DB', 10)) # WARNING TESTS FLUSHDB!!!\nREDIS_PORT = int(os.environ.get('REDIS_PORT', 6380))\nredisco.connection_setup(host=\"localhost\", port=REDIS_PORT, db=REDIS_DB)\n\ntyped_list_suite = unittest.TestLoader().loadTestsFromTestCase(TypedListTestCase)\n\ndef all_tests():\n    suite = unittest.TestSuite()\n    suite.addTest(unittest.makeSuite(ConnectionTestCase))\n    suite.addTest(unittest.makeSuite(SetTestCase))\n    suite.addTest(unittest.makeSuite(ListTestCase))\n    suite.addTest(unittest.makeSuite(TypedListTestCase))\n    suite.addTest(unittest.makeSuite(SortedSetTestCase))\n    suite.addTest(unittest.makeSuite(ModelTestCase))\n    suite.addTest(unittest.makeSuite(DateFieldTestCase))\n    suite.addTest(unittest.makeSuite(FloatFieldTestCase))\n    suite.addTest(unittest.makeSuite(BooleanFieldTestCase))\n    suite.addTest(unittest.makeSuite(ListFieldTestCase))\n    suite.addTest(unittest.makeSuite(ReferenceFieldTestCase))\n    suite.addTest(unittest.makeSuite(DateTimeFieldTestCase))\n    suite.addTest(unittest.makeSuite(CounterFieldTestCase))\n    suite.addTest(unittest.makeSuite(MutexTestCase))\n    suite.addTest(unittest.makeSuite(HashTestCase))\n    suite.addTest(unittest.makeSuite(CharFieldTestCase))\n    return suite\n"
  },
  {
    "path": "tests/bm_profile.py",
    "content": "from bm_write import *\n\nimport hotshot\nprof = hotshot.Profile(\"enum\")\nd = datetime.fromtimestamp(1571044985)\nPerson._db.select(11)\nbef = Person.objects.zfilter(date__lt=d)\nprof.addinfo(\"part\", \"1\")\nprof.start()\nlen(bef)\nprof.stop()\nprof.addinfo(\"part\", \"2\")\naft = Person.objects.zfilter(date__gte=d)\nprof.start()\nlen(aft)\nprof.stop()\nprof.close()\n\nimport hotshot.stats\nstats = hotshot.stats.load(\"enum\")\nstats.strip_dirs()\nstats.sort_stats('time', 'calls')\nstats.print_stats(50)\n\n"
  },
  {
    "path": "tests/bm_write.py",
    "content": "#!/usr/bin/env python\nimport time\nimport random\nfrom redisco import models\nfrom redisco.connection import _get_client\nfrom datetime import datetime\nimport timeit\nfrom functools import partial\n\nclass Person(models.Model):\n    first_name = models.Attribute()\n    last_name = models.Attribute()\n    address = models.Attribute()\n    description = models.Attribute()\n    created_at = models.DateTimeField(auto_now_add=True)\n    date = models.DateTimeField()\n\nif __name__ == '__main__':\n    db = _get_client()\n    db.select(11)\n    db.flushdb()\n\n    def rand_date():\n        s = random.randrange(883584000, 1577808000)\n        return datetime.fromtimestamp(s)\n\n\n    def init():\n        persons = []\n        for i in range(100000):\n            p = Person(first_name=random.choice(('Tim', 'Alec', 'Jim')),\n                       last_name=random.choice(('Medina', 'Baldwin', \"O'brien\")),\n                       address=\"36174 Kraig Well\",\n                       description=\"cum et corporis hic dolorem ullam omnis aut quis enim quisquam rem earum est commodi at asperiores autem id magni consectetur dignissimos vero ut et perferendis sequi voluptas voluptatibus assumenda molestiae tempore debitis et consequuntur ipsa voluptatum ut facilis officia dolores quia fuga quia aliquam architecto tenetur iure velit dicta ad alias et corrupti est sit quod possimus quasi accusantium est qui totam autem ea optio veritatis et et nostrum modi quibusdam natus laboriosam aut aspernatur et vel eaque soluta rerum recusandae vitae qui quo voluptatem exercitationem deserunt non placeat ut inventore numquam et enim mollitia harum labore deleniti quia doloribus ipsam nisi sed nihil laudantium quas dolor occaecati eum in aut ex distinctio minima quia accusamus ut consequatur eos adipisci repellendus voluptas expedita impedit beatae est fugiat officiis sint minus aliquid culpa libero nihil omnis aperiam excepturi atque dolor sunt reprehenderit magnam itaque repellat provident eos et eum sed odio fugit nesciunt dolorum sapiente qui pariatur sit iusto tempora ipsum maiores dolores nobis doloremque non facere praesentium explicabo aut dolorem qui veniam nemo ut suscipit eveniet voluptatem eligendi esse quis ab eius necessitatibus a delectus repudiandae molestiae vel aut ut qui consequatur quidem perspiciatis qui quaerat saepe neque animi voluptate non omnis in consequatur cupiditate quo ducimus velit rerum dolore nam et quos illum laborum id sunt ea molestias voluptas et est sint quam sit porro sed unde rerum voluptates temporibus odit nulla ratione blanditiis amet voluptatem aut cumque quae iste error similique voluptatem illo maxime reiciendis incidunt\"\n                       )\n            p.date = rand_date()\n            persons.append(p)\n        return persons\n\n    def save_persons(persons):\n        for person in persons:\n            person.save()\n\n    tsave = partial(save_persons, init())\n\n    def enum_persons():\n        for p in Person.objects.all():\n            p.first_name\n            p.last_name\n            p.address\n            p.description\n\n    n = datetime.now()\n\n    def filter_lt(n):\n        res = Person.objects.zfilter(date__lt=n)\n        for p in res:\n            p.date\n        print \"Found %d lt\" % len(res)\n\n    def filter_gte(n):\n        res = Person.objects.zfilter(date__gte=n)\n        for p in res:\n            p.date\n        print \"Found %d gte\" % len(res)\n\n    tfilter_lt = partial(filter_lt, n)\n    tfilter_gte = partial(filter_gte, n)\n\n    st = timeit.timeit(tsave, number=1)\n    en = timeit.timeit(enum_persons, number=1)\n    f1 = timeit.timeit(tfilter_lt, number=1)\n    f2 = timeit.timeit(tfilter_gte, number=1)\n\n    print \"Save %f\" % st\n    print \"enum %f\" % en\n    print \"Filter lt %f\" % f1\n    print \"Filter gte %f\" % f2\n"
  },
  {
    "path": "tests/connection.py",
    "content": "import unittest\nimport redisco\n\nfrom distutils.version import LooseVersion as Version\n\nclass ConnectionTestCase(unittest.TestCase):\n\n    def test_redis_version(self):\n        self.assertTrue(Version(redisco.redis.__version__) >= '2')\n\n    def test_redis_server(self):\n        redis_server_version = redisco.connection.info()['redis_version']\n        self.assertTrue(Version(redis_server_version) > '1.2')\n\n"
  },
  {
    "path": "tests/containers.py",
    "content": "import unittest\nimport redisco\nfrom redisco import containers as cont\n\nclass SetTestCase(unittest.TestCase):\n    def setUp(self):\n        self.client = redisco.get_client()\n        self.client.flushdb()\n\n    def tearDown(self):\n        self.client.flushdb()\n\n    def test_common_operations(self):\n        fruits = cont.Set(key='fruits')\n        fruits.add('apples')\n        fruits.add('oranges')\n        self.assertEqual(set(['apples', 'oranges']), fruits.all())\n\n        # remove\n        fruits.discard('apples')\n        self.assertEqual(set(['oranges']), fruits.all())\n        self.assertRaises(KeyError, fruits.remove, 'apples')\n\n        # in\n        self.assertTrue('oranges' in fruits)\n        self.assertTrue('apples' not in fruits)\n\n        # len\n        self.assertEqual(1, len(fruits))\n\n        # pop\n        self.assertEqual('oranges', fruits.pop())\n\n        # copy\n        fruits.add('apples')\n        fruits.add('oranges')\n        basket = fruits.copy('basket')\n        self.assertEqual(set(['apples', 'oranges']), basket.all())\n\n        # update\n        o = cont.Set('o', self.client)\n        o.add('kiwis')\n        fruits.update(o)\n        self.assertEqual(set(['kiwis', 'apples', 'oranges']),\n                fruits.all())\n\n    def test_comparisons(self):\n        all_pls = cont.Set(key='ProgrammingLanguages')\n        my_pls = cont.Set(key='MyPLs')\n        o_pls = cont.Set(key='OPLs')\n        all_pls.add('Python')\n        all_pls.add('Ruby')\n        all_pls.add('PHP')\n        all_pls.add('Lua')\n        all_pls.add('Java')\n        all_pls.add('Pascal')\n        all_pls.add('C')\n        all_pls.add('C++')\n        all_pls.add('Haskell')\n        all_pls.add('C#')\n        all_pls.add('Go')\n\n        my_pls.add('Ruby')\n        my_pls.add('Python')\n        my_pls.add('Lua')\n        my_pls.add('Haskell')\n\n        o_pls.add('Ruby')\n        o_pls.add('Python')\n        o_pls.add('Lua')\n        o_pls.add('Haskell')\n\n        # equality\n        self.assertNotEqual(my_pls, all_pls)\n        self.assertEqual(o_pls, my_pls)\n\n        fruits = cont.Set(key='fruits')\n        fruits.add('apples')\n        fruits.add('oranges')\n\n        # disjoint\n        self.assertTrue(fruits.isdisjoint(o_pls))\n        self.assertFalse(all_pls.isdisjoint(o_pls))\n\n        # subset\n        self.assertTrue(my_pls < all_pls)\n        self.assertTrue(all_pls > my_pls)\n        self.assertTrue(o_pls >= my_pls)\n        self.assertTrue(o_pls <= my_pls)\n        self.assertTrue(my_pls.issubset(all_pls))\n        self.assertTrue(my_pls.issubset(o_pls))\n        self.assertTrue(o_pls.issubset(my_pls))\n\n        # union\n        s = fruits.union(\"fruits|mypls\", my_pls)\n        self.assertEqual(set(['Ruby', 'Python', 'Lua', 'Haskell', 'apples',\n            'oranges']), s.members)\n\n        # intersection\n        inter = fruits.intersection('fruits&mypls', my_pls)\n        self.assertEqual(set([]), inter.members)\n\n        # difference\n        s = fruits.difference('fruits-my_pls', my_pls)\n        self.assertEqual(set(['apples', 'oranges']), s.members)\n\n\n    def test_operations_with_updates(self):\n        abc = cont.Set('abc', self.client)\n        for c in 'abc':\n            abc.add(c)\n\n        def_ = cont.Set('def', self.client)\n        for c in 'def':\n            def_.add(c)\n\n        # __ior__\n        abc |= def_\n        self.assertEqual(set(['a', 'b', 'c', 'd', 'e', 'f']),\n                abc.all())\n\n        abc &= def_\n        self.assertEqual(set(['d', 'e', 'f']), abc.all())\n\n        for c in 'abc':\n            abc.add(c)\n        abc -= def_\n        self.assertEqual(set(['a', 'b', 'c']), abc.all())\n\n    def test_methods_that_should_return_new_sets(self):\n        abc = cont.Set('abc', self.client)\n        for c in 'abc':\n            abc.add(c)\n\n        def_ = cont.Set('def', self.client)\n        for c in 'def':\n            def_.add(c)\n\n        # new_key as a set should raise error\n        # only strings are allowed as keys\n        new_set = cont.Set('new_set')\n        self.assertRaises(ValueError, abc.union, new_set, def_)\n        self.assertRaises(ValueError, abc.difference, new_set, def_)\n        self.assertRaises(ValueError, abc.intersection, new_set, def_)\n\n        self.assert_(isinstance(abc.union('new_set', def_), cont.Set))\n        self.assert_(isinstance(abc.intersection('new_set', def_), cont.Set))\n        self.assert_(isinstance(abc.difference('new_set', def_), cont.Set))\n\n\n    def test_access_redis_methods(self):\n        s = cont.Set('new_set')\n        s.sadd('a')\n        s.sadd('b')\n        s.srem('b')\n        self.assertEqual('a', s.spop())\n        s.sadd('a')\n        self.assert_('a' in s.smembers())\n        s.sadd('b')\n        self.assertEqual(2, s.scard())\n        self.assert_(s.sismember('a'))\n        self.client.sadd('other_set', 'a')\n        self.client.sadd('other_set', 'b')\n        self.client.sadd('other_set', 'c')\n        self.assert_(s.srandmember() in set(['a', 'b']))\n\n    def test_sinter(self):\n        abc = cont.Set(\"abc\")\n        def_ = cont.Set(\"def\")\n        abc.add('a')\n        abc.add('b')\n        abc.add('c')\n        def_.add('d')\n        def_.add('e')\n        def_.add('f')\n\n        self.assertEqual(set([]), abc.sinter(def_))\n        def_.add('b')\n        def_.add('c')\n\n        self.assertEqual(set(['b', 'c']), abc.sinter(def_))\n\n    def test_sunion(self):\n        abc = cont.Set(\"abc\")\n        def_ = cont.Set(\"def\")\n        abc.add('a')\n        abc.add('b')\n        abc.add('c')\n        def_.add('d')\n        def_.add('e')\n        def_.add('f')\n\n        self.assertEqual(set(['a', 'b', 'c', 'd', 'e', 'f']),\n                abc.sunion(def_))\n\n    def test_susdiff(self):\n        abc = cont.Set(\"abc\")\n        def_ = cont.Set(\"def\")\n        abc.add('a')\n        abc.add('b')\n        abc.add('c')\n        def_.add('c')\n        def_.add('b')\n        def_.add('f')\n\n        self.assertEqual(set(['a',]),\n                abc.sdiff(def_))\n\n\nclass ListTestCase(unittest.TestCase):\n    def setUp(self):\n        self.client = redisco.get_client()\n        self.client.flushdb()\n\n    def tearDown(self):\n        self.client.flushdb()\n\n    def test_common_operations(self):\n        alpha = cont.List('alpha', self.client)\n\n        # append\n        alpha.append('a')\n        alpha.append('b')\n\n        # len\n        self.assertEqual(2, len(alpha))\n\n        num = cont.List('num', self.client)\n        num.append('1')\n        num.append('2')\n\n        # extend and iter\n        alpha.extend(num)\n        self.assertEqual(['a', 'b', '1', '2'], alpha.all())\n        alpha.extend(['3', '4'])\n        self.assertEqual(['a', 'b', '1', '2', '3', '4'], alpha.all())\n\n        # contains\n        self.assertTrue('b' in alpha)\n        self.assertTrue('2' in alpha)\n        self.assertTrue('5' not in alpha)\n\n        # shift and unshift\n        num.unshift('0')\n        self.assertEqual(['0', '1', '2'], num.members)\n        self.assertEqual('0', num.shift())\n        self.assertEqual(['1', '2'], num.members)\n\n        # push and pop\n        num.push('4')\n        self.assertEqual('4', num.pop())\n        self.assertEqual(['1', '2'], num.members)\n\n        # trim\n        alpha.trim(0, 1)\n        self.assertEqual(['a', 'b'], alpha.all())\n\n        # remove\n        alpha.remove('b')\n        self.assertEqual(['a'], alpha.all())\n\n        # setitem\n        alpha[0] = 'A'\n        self.assertEqual(['A'], alpha.all())\n\n        # iter\n        alpha.push('B')\n        for e, a in zip(alpha, ['A', 'B']):\n            self.assertEqual(a, e)\n        self.assertEqual(['A', 'B'], list(alpha))\n\n        # slice\n        alpha.extend(['C', 'D', 'E'])\n        self.assertEqual(['A', 'B', 'C', 'D', 'E'], alpha[:])\n        self.assertEqual(['B', 'C'], alpha[1:2])\n\n        alpha.reverse()\n        self.assertEqual(['E', 'D', 'C', 'B', 'A'], list(alpha))\n\n    def test_pop_onto(self):\n        a = cont.List('alpha')\n        b = cont.List('beta')\n        a.extend(range(10))\n\n        # test pop_onto\n        a_snap = list(a.members)\n        while True:\n            v = a.pop_onto(b.key)\n            if not v:\n                break\n            else:\n                self.assertTrue(v not in a.members)\n                self.assertTrue(v in b.members)\n\n        self.assertEqual(a_snap, b.members)\n\n        # test rpoplpush\n        b_snap = list(b.members)\n        while True:\n            v = b.rpoplpush(a.key)\n            if not v:\n                break\n            else:\n                self.assertTrue(v in a.members)\n                self.assertTrue(v not in b.members)\n\n        self.assertEqual(b_snap, a.members)\n        \n\n\n    def test_delegateable_methods(self):\n        l = cont.List('mylist')\n        self.assertEqual([], l.lrange(0, -1))\n        l.rpush('b')\n        l.rpush('c')\n        l.lpush('a')\n        self.assertEqual(['a', 'b', 'c'], l.lrange(0, -1))\n        self.assertEqual(3, l.llen())\n        l.ltrim(1, 2)\n        self.assertEqual(['b', 'c'], l.lrange(0, -1))\n        self.assertEqual('c', l.lindex(1))\n        self.assertEqual(1, l.lset(0, 'a'))\n        self.assertEqual(1, l.lset(1, 'b'))\n        self.assertEqual(['a', 'b'], l.lrange(0, -1))\n        self.assertEqual('a', l.lpop())\n        self.assertEqual('b', l.rpop())\n\nclass TypedListTestCase(unittest.TestCase):\n    def setUp(self):\n        self.client = redisco.get_client()\n        self.client.flushdb()\n\n    def tearDown(self):\n        self.client.flushdb()\n\n    def test_basic_types(self):\n        alpha = cont.TypedList('alpha', unicode, type_args=('UTF-8',))\n        monies = u'\\u0024\\u00a2\\u00a3\\u00a5'\n        alpha.append(monies)\n        val = alpha[-1]\n        self.assertEquals(monies, val)\n\n        beta = cont.TypedList('beta', int)\n        for i in xrange(1000):\n            beta.append(i)\n        for i, x in enumerate(beta):\n            self.assertEquals(i, x)\n\n        charlie = cont.TypedList('charlie', float)\n        for i in xrange(100):\n            val = 1 * pow(10, i*-1)\n            charlie.append(val)\n        for i, x in enumerate(charlie):\n            val = 1 * pow(10, i*-1)\n            self.assertEquals(x, val)\n\n    def test_model_type(self):\n        from redisco import models\n        class Person(models.Model):\n            name = models.Attribute()\n            friend = models.ReferenceField('Person')\n\n        iamteam = Person.objects.create(name='iamteam')\n        clayg = Person.objects.create(name='clayg', friend=iamteam)\n\n        l = cont.TypedList('friends', 'Person')\n        l.extend(Person.objects.all())\n\n        for person in l:\n            if person.name == 'clayg':\n                self.assertEquals(iamteam, clayg.friend)\n            else:\n                # this if failing for some reason ???\n                #self.assertEquals(person.friend, clayg) \n                pass\n\n\nclass SortedSetTestCase(unittest.TestCase):\n    def setUp(self):\n        self.client = redisco.get_client()\n        self.client.flushdb()\n\n    def tearDown(self):\n        self.client.flushdb()\n\n    def test_everything(self):\n        zorted = cont.SortedSet(\"Person:age\")\n        zorted.add(\"1\", 29)\n        zorted.add(\"2\", 39)\n        zorted.add(\"3\", '15')\n        zorted.add(\"4\", 35)\n        zorted.add(\"5\", 98)\n        zorted.add(\"6\", 5)\n        self.assertEqual(6, len(zorted))\n        self.assertEqual(35, zorted.score(\"4\"))\n        self.assertEqual(0, zorted.rank(\"6\"))\n        self.assertEqual(5, zorted.revrank(\"6\"))\n        self.assertEqual(3, zorted.rank(\"4\"))\n        self.assertEqual([\"6\", \"3\", \"1\", \"4\"], zorted.le(35))\n\n        zorted.add(\"7\", 35)\n        self.assertEqual([\"4\", \"7\"], zorted.eq(35))\n        self.assertEqual([\"6\", \"3\", \"1\"], zorted.lt(30))\n        self.assertEqual([\"4\", \"7\", \"2\", \"5\"], zorted.gt(30))\n\n    def test_delegateable_methods(self):\n        zset = cont.SortedSet(\"Person:all\")\n        zset.zadd(\"1\", 1)\n        zset.zadd(\"2\", 2)\n        zset.zadd(\"3\", 3)\n        zset.zadd(\"4\", 4)\n        self.assertEqual(4, zset.zcard())\n        self.assertEqual(4, zset.zscore('4'))\n        self.assertEqual(['1', '2', '3', '4'], list(zset))\n        self.assertEqual(zset.zrange(0, -1), list(zset))\n        self.assertEqual(['4', '3', '2', '1'], zset.zrevrange(0, -1))\n        self.assertEqual(list(reversed(zset)), zset.zrevrange(0, -1))\n        self.assertEqual(list(reversed(zset)), list(zset.__reversed__()))\n\n\nclass HashTestCase(unittest.TestCase):\n    def setUp(self):\n        self.client = redisco.get_client()\n        self.client.flushdb()\n\n    def tearDown(self):\n        self.client.flushdb()\n\n    def test_basic(self):\n        h = cont.Hash('hkey')\n        self.assertEqual(0, len(h))\n        h['name'] = \"Richard Cypher\"\n        h['real_name'] = \"Richard Rahl\"\n\n        pulled = self.client.hgetall('hkey')\n        self.assertEqual({'name': \"Richard Cypher\",\n            'real_name': \"Richard Rahl\"}, pulled)\n\n        self.assertEqual({'name': \"Richard Cypher\",\n            'real_name': \"Richard Rahl\"}, h.dict)\n\n        self.assertEqual(['name', 'real_name'], h.keys())\n        self.assertEqual([\"Richard Cypher\", \"Richard Rahl\"],\n            h.values())\n\n        del h['name']\n        pulled = self.client.hgetall('hkey')\n        self.assertEqual({'real_name': \"Richard Rahl\"}, pulled)\n        self.assert_('real_name' in h)\n        h.dict = {\"new_hash\": \"YEY\"}\n        self.assertEqual({\"new_hash\": \"YEY\"}, h.dict)\n\n    def test_delegateable_methods(self):\n        h = cont.Hash('my_hash')\n        h.hincrby('Red', 1)\n        h.hincrby('Red', 1)\n        h.hincrby('Red', 2)\n        self.assertEqual(4, int(h.hget('Red')))\n        h.hmset({'Blue': 100, 'Green': 19, 'Yellow': 1024})\n        self.assertEqual(['100', '19'], h.hmget(['Blue', 'Green']))\n\nif __name__ == \"__main__\":\n    import sys\n    unittest.main(argv=sys.argv)\n"
  },
  {
    "path": "tests/models.py",
    "content": "# -*- coding: utf-8 -*-\nimport time\nfrom threading import Thread\nimport base64\nimport redis\nimport redisco\nimport unittest\nfrom datetime import date\nfrom redisco import models\nfrom redisco.models.base import Mutex\n\nclass Person(models.Model):\n    first_name = models.CharField()\n    last_name = models.CharField()\n\n    def full_name(self):\n        return \"%s %s\" % (self.first_name, self.last_name,)\n\n    class Meta:\n        indices = ['full_name']\n\n\nclass RediscoTestCase(unittest.TestCase):\n    def setUp(self):\n        self.client = redisco.get_client()\n        self.client.flushdb()\n\n    def tearDown(self):\n        self.client.flushdb()\n\n\nclass ModelTestCase(RediscoTestCase):\n\n    def test_key(self):\n        self.assertEqual('Person', Person._key)\n\n    def test_is_new(self):\n        p = Person(first_name=\"Darken\", last_name=\"Rahl\")\n        self.assertTrue(p.is_new())\n\n    def test_CharFields(self):\n        person = Person(first_name=\"Granny\", last_name=\"Goose\")\n        self.assertEqual(\"Granny\", person.first_name)\n        self.assertEqual(\"Goose\", person.last_name)\n\n    def test_save(self):\n        person1 = Person(first_name=\"Granny\", last_name=\"Goose\")\n        person1.save()\n        person2 = Person(first_name=\"Jejomar\")\n        person2.save()\n\n        self.assertEqual('1', person1.id)\n        self.assertEqual('2', person2.id)\n\n        jejomar = Person.objects.get_by_id('2')\n        self.assertEqual(None, jejomar.last_name)\n\n    def test_unicode(self):\n        p = Person(first_name=u\"Niña\", last_name=\"Jose\")\n        self.assert_(p.save())\n        g = Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        self.assert_(g)\n\n        p = Person.objects.filter(first_name=u\"Niña\").first()\n        self.assert_(p)\n        self.assert_(isinstance(p.full_name(), unicode))\n        self.assertEqual(u\"Niña Jose\", p.full_name())\n\n    def test_repr(self):\n        person1 = Person(first_name=\"Granny\", last_name=\"Goose\")\n        self.assertEqual(\"<Person {'first_name': 'Granny', 'last_name': 'Goose'}>\",\n                repr(person1))\n\n        self.assert_(person1.save())\n        self.assertEqual(\"<Person:1 {'first_name': 'Granny', 'last_name': 'Goose'}>\",\n                repr(person1))\n\n    def test_update(self):\n        person1 = Person(first_name=\"Granny\", last_name=\"Goose\")\n        person1.save()\n\n        p = Person.objects.get_by_id('1')\n        p.first_name = \"Morgan\"\n        p.last_name = None\n        assert p.save()\n\n        p = Person.objects.get_by_id(p.id)\n        self.assertEqual(\"Morgan\", p.first_name)\n        self.assertEqual(None, p.last_name)\n\n    def test_default_CharField_val(self):\n        class User(models.Model):\n            views = models.IntegerField(default=199)\n            liked = models.BooleanField(default=True)\n            disliked = models.BooleanField(default=False)\n\n        u = User()\n        self.assertEqual(True, u.liked)\n        self.assertEqual(False, u.disliked)\n        self.assertEqual(199, u.views)\n        assert u.save()\n\n        u = User.objects.all()[0]\n        self.assertEqual(True, u.liked)\n        self.assertEqual(False, u.disliked)\n        self.assertEqual(199, u.views)\n\n\n    def test_getitem(self):\n        person1 = Person(first_name=\"Granny\", last_name=\"Goose\")\n        person1.save()\n        person2 = Person(first_name=\"Jejomar\", last_name=\"Binay\")\n        person2.save()\n\n        p1 = Person.objects.get_by_id(1)\n        p2 = Person.objects.get_by_id(2)\n\n        self.assertEqual('Jejomar', p2.first_name)\n        self.assertEqual('Binay', p2.last_name)\n\n        self.assertEqual('Granny', p1.first_name)\n        self.assertEqual('Goose', p1.last_name)\n\n    def test_manager_create(self):\n        person = Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n\n        p1 = Person.objects.get_by_id(1)\n        self.assertEqual('Granny', p1.first_name)\n        self.assertEqual('Goose', p1.last_name)\n\n    def test_indices(self):\n        person = Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        db = person.db\n        key = person.key()\n        ckey = Person._key\n\n        index = 'Person:first_name:%s' % base64.b64encode(\"Granny\").replace(\"\\n\", \"\")\n        self.assertTrue(index in db.smembers(key['_indices']))\n        self.assertTrue(\"1\" in db.smembers(index))\n\n\n    def test_delete(self):\n        Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        Person.objects.create(first_name=\"Clark\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Mommy\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Kent\")\n\n        for person in Person.objects.all():\n            person.delete()\n\n        self.assertEqual(0, self.client.scard('Person:all'))\n\n        class Event(models.Model):\n            name = models.CharField(required=True)\n            created_on = models.DateField(required=True)\n\n        from datetime import date\n\n        Event.objects.create(name=\"Event #1\", created_on=date.today())\n        Event.objects.create(name=\"Event #2\", created_on=date.today())\n        Event.objects.create(name=\"Event #3\", created_on=date.today())\n        Event.objects.create(name=\"Event #4\", created_on=date.today())\n\n        for event in Event.objects.all():\n            event.delete()\n\n        self.assertEqual(0, self.client.zcard(\"Event:created_on\"))\n\n\n    def test_filter(self):\n        Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        Person.objects.create(first_name=\"Clark\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Mommy\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Kent\")\n        persons = Person.objects.filter(first_name=\"Granny\")\n\n        self.assertEqual('1', persons[0].id)\n        self.assertEqual(3, len(persons))\n\n        persons = Person.objects.filter(first_name=\"Clark\")\n        self.assertEqual(1, len(persons))\n\n        # by index\n        persons = Person.objects.filter(full_name=\"Granny Mommy\")\n        self.assertEqual(1, len(persons))\n        self.assertEqual(\"Granny Mommy\", persons[0].full_name())\n\n\n    def test_exclude(self):\n        Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        Person.objects.create(first_name=\"Clark\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Mommy\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Kent\")\n        persons = Person.objects.exclude(first_name=\"Granny\")\n\n        self.assertEqual('2', persons[0].id)\n        self.assertEqual(1, len(persons))\n\n        persons = Person.objects.exclude(first_name=\"Clark\")\n        self.assertEqual(3, len(persons))\n\n        # by index\n        persons = Person.objects.exclude(full_name=\"Granny Mommy\")\n        self.assertEqual(3, len(persons))\n        self.assertEqual(\"Granny Goose\", persons[0].full_name())\n        self.assertEqual(\"Clark Kent\", persons[1].full_name())\n        self.assertEqual(\"Granny Kent\", persons[2].full_name())\n\n        # mixed\n        Person.objects.create(first_name=\"Granny\", last_name=\"Pacman\")\n        persons = (Person.objects.filter(first_name=\"Granny\")\n                    .exclude(last_name=\"Mommy\"))\n        self.assertEqual(3, len(persons))\n\n\n    def test_first(self):\n        Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        Person.objects.create(first_name=\"Clark\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Mommy\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Kent\")\n        granny = Person.objects.filter(first_name=\"Granny\").first()\n        self.assertEqual('1', granny.id)\n        lana = Person.objects.filter(first_name=\"Lana\").first()\n        self.assertFalse(lana)\n\n\n    def test_iter(self):\n        Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        Person.objects.create(first_name=\"Clark\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Mommy\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Kent\")\n\n        for person in Person.objects.all():\n            self.assertTrue(person.full_name() in (\"Granny Goose\",\n                \"Clark Kent\", \"Granny Mommy\", \"Granny Kent\",))\n\n    def test_sort(self):\n        Person.objects.create(first_name=\"Zeddicus\", last_name=\"Zorander\")\n        Person.objects.create(first_name=\"Richard\", last_name=\"Cypher\")\n        Person.objects.create(first_name=\"Richard\", last_name=\"Rahl\")\n        Person.objects.create(first_name=\"Kahlan\", last_name=\"Amnell\")\n\n        res = Person.objects.order('first_name').all()\n        self.assertEqual(\"Kahlan\", res[0].first_name)\n        self.assertEqual(\"Richard\", res[1].first_name)\n        self.assertEqual(\"Richard\", res[2].first_name)\n        self.assertEqual(\"Zeddicus Zorander\", res[3].full_name())\n\n        res = Person.objects.order('-full_name').all()\n        self.assertEqual(\"Zeddicus Zorander\", res[0].full_name())\n        self.assertEqual(\"Richard Rahl\", res[1].full_name())\n        self.assertEqual(\"Richard Cypher\", res[2].full_name())\n        self.assertEqual(\"Kahlan Amnell\", res[3].full_name())\n\n    def test_all(self):\n        person1 = Person(first_name=\"Granny\", last_name=\"Goose\")\n        person1.save()\n        person2 = Person(first_name=\"Jejomar\", last_name=\"Binay\")\n        person2.save()\n\n        all = Person.objects.all()\n        self.assertEqual(list([person1, person2]), list(all))\n\n    def test_limit(self):\n        Person.objects.create(first_name=\"Zeddicus\", last_name=\"Zorander\")\n        Person.objects.create(first_name=\"Richard\", last_name=\"Cypher\")\n        Person.objects.create(first_name=\"Richard\", last_name=\"Rahl\")\n        Person.objects.create(first_name=\"Kahlan\", last_name=\"Amnell\")\n\n        res = Person.objects.order('first_name').all().limit(3)\n        self.assertEqual(3, len(res))\n        self.assertEqual(\"Kahlan\", res[0].first_name)\n        self.assertEqual(\"Richard\", res[1].first_name)\n        self.assertEqual(\"Richard\", res[2].first_name)\n\n        res = Person.objects.order('first_name').limit(3, offset=1)\n        self.assertEqual(3, len(res))\n        self.assertEqual(\"Richard\", res[0].first_name)\n        self.assertEqual(\"Richard\", res[1].first_name)\n        self.assertEqual(\"Zeddicus\", res[2].first_name)\n\n\n    def test_integer_field(self):\n        class Character(models.Model):\n            n = models.IntegerField()\n            m = models.CharField()\n\n        Character.objects.create(n=1998, m=\"A\")\n        Character.objects.create(n=3100, m=\"b\")\n        Character.objects.create(n=1, m=\"C\")\n\n        chars = Character.objects.all()\n        self.assertEqual(3, len(chars))\n        self.assertEqual(1998, chars[0].n)\n        self.assertEqual(\"A\", chars[0].m)\n\n    def test_sort_by_int(self):\n        class Exam(models.Model):\n            score = models.IntegerField()\n            total_score = models.IntegerField()\n\n            def percent(self):\n                return int((float(self.score) / self.total_score) * 100)\n\n            class Meta:\n                indices = ('percent',)\n\n        Exam.objects.create(score=9, total_score=100)\n        Exam.objects.create(score=99, total_score=100)\n        Exam.objects.create(score=75, total_score=100)\n        Exam.objects.create(score=33, total_score=100)\n        Exam.objects.create(score=95, total_score=100)\n\n        exams = Exam.objects.order('score')\n        self.assertEqual([9, 33, 75, 95, 99,], [exam.score for exam in exams])\n        filtered = Exam.objects.zfilter(score__in=(10, 96))\n        self.assertEqual(3, len(filtered))\n\n\n    def test_filter_date(self):\n        from datetime import datetime\n\n        class Post(models.Model):\n            name = models.CharField()\n            date = models.DateTimeField()\n\n        dates = (\n            datetime(2010, 1, 20, 1, 40, 0),\n            datetime(2010, 2, 20, 1, 40, 0),\n            datetime(2010, 1, 26, 1, 40, 0),\n            datetime(2009, 12, 21, 1, 40, 0),\n            datetime(2010, 1, 10, 1, 40, 0),\n            datetime(2010, 5, 20, 1, 40, 0),\n        )\n\n        i = 0\n        for date in dates:\n            Post.objects.create(name=\"Post#%d\" % i, date=date)\n            i += 1\n\n        self.assertEqual([Post.objects.get_by_id(4)],\n                list(Post.objects.filter(date=\n                    datetime(2009, 12, 21, 1, 40, 0))))\n\n        lt = (0, 2, 3, 4)\n        res = [Post.objects.get_by_id(l + 1) for l in lt]\n        self.assertEqual(set(res),\n                set(Post.objects.zfilter(\n                    date__lt=datetime(2010, 1, 30))))\n\n    def test_validation(self):\n        class Person(models.Model):\n            name = models.CharField(required=True)\n        p = Person(name=\"Kokoy\")\n        self.assertTrue(p.is_valid())\n\n        p = Person()\n        self.assertFalse(p.is_valid())\n        self.assertTrue(('name', 'required') in p.errors)\n\n    def test_validation_of_unique_fields(self):\n        class Person(models.Model):\n            name = models.CharField(required=True, unique=True)\n\n        p = Person(name=\"Lincoln\")\n        self.assertTrue(p.is_valid())\n        self.assert_(p.save())\n\n        p_from_db = Person.objects.get_by_id(p.id)\n        self.assertTrue(p.is_valid())\n\n    def test_errors(self):\n        class Person(models.Model):\n            name = models.CharField(required=True, unique=True)\n        p = Person.objects.create(name=\"Chuck\")\n        self.assertFalse(p.errors)\n\n        p = Person(name=\"John\")\n        self.assertFalse(p.errors)\n        p.name = \"Chuck\" # name should be unique\n        # this doesn't work:\n        #self.assertEquals(not p.errors, p.is_valid())\n        # but this works:\n        self.assertEqual(p.is_valid(), not p.errors)\n\n    def test_custom_validation(self):\n        class Ninja(models.Model):\n            def validator(field_name, age):\n                if not age or age >= 10:\n                    return ((field_name, 'must be below 10'),)\n            age = models.IntegerField(required=True, validator=validator)\n\n        nin1 = Ninja(age=9)\n        self.assertTrue(nin1.is_valid())\n\n        nin2 = Ninja(age=10)\n        self.assertFalse(nin2.is_valid())\n        self.assertTrue(('age', 'must be below 10') in nin2.errors)\n\n    def test_overriden_validation(self):\n        class Ninja(models.Model):\n            age = models.IntegerField(required=True)\n\n            def validate(self):\n                if self.age >= 10:\n                    self._errors.append(('age', 'must be below 10'))\n\n\n        nin1 = Ninja(age=9)\n        self.assertTrue(nin1.is_valid())\n\n        nin2 = Ninja(age=10)\n        self.assertFalse(nin2.is_valid())\n        self.assertTrue(('age', 'must be below 10') in nin2.errors)\n\n    def test_load_object_from_key(self):\n        class Schedule(models.Model):\n            att = models.CharField()\n\n        class PaperType(models.Model):\n            att = models.CharField()\n\n        assert Schedule.objects.create(att=\"dinuguan\")\n        assert Schedule.objects.create(att=\"chicharon\")\n        assert Schedule.objects.create(att=\"Pizza\")\n        assert Schedule.objects.create(att=\"Pasta\")\n        assert Schedule.objects.create(att=\"Veggies\")\n\n        assert PaperType.objects.create(att=\"glossy\")\n        assert PaperType.objects.create(att=\"large\")\n        assert PaperType.objects.create(att=\"huge\")\n        assert PaperType.objects.create(att=\"A6\")\n        assert PaperType.objects.create(att=\"A9\")\n\n        o = models.from_key(\"Schedule:1\")\n        assert o\n        self.assertEqual('1', o.id)\n        self.assertEqual(Schedule, type(o))\n        o = models.from_key(\"PaperType:1\")\n        self.assertEqual('1', o.id)\n        self.assertEqual(PaperType, type(o))\n        o = models.from_key(\"Schedule:4\")\n        self.assertEqual('4', o.id)\n        self.assertEqual(Schedule, type(o))\n        o = models.from_key(\"PaperType:5\")\n        self.assertEqual('5', o.id)\n        self.assertEqual(PaperType, type(o))\n        o = models.from_key(\"PaperType:6\")\n        self.assertTrue(o is None)\n\n        def boom():\n            models.from_key(\"some arbitrary key\")\n        from redisco.models.exceptions import BadKeyError\n        self.assertRaises(BadKeyError, boom)\n\n    def test_uniqueness_validation(self):\n        class Student(models.Model):\n            student_id = models.CharField(unique=True)\n\n        student = Student.objects.create(student_id=\"042231\")\n        self.assert_(student)\n\n        student = Student(student_id=\"042231\")\n        self.assertFalse(student.is_valid())\n        self.assert_(('student_id', 'not unique') in student.errors)\n\n        student = Student()\n        self.assertTrue(student.is_valid())\n\n    def test_long_integers(self):\n        class Tweet(models.Model):\n            status_id = models.IntegerField()\n\n        t = Tweet(status_id=int(u'14782201061'))\n        self.assertTrue(t.is_valid())\n        t.save()\n\n        t = Tweet.objects.get_by_id(t.id)\n        self.assertEqual(14782201061, t.status_id)\n\n\n    def test_slicing(self):\n        Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        Person.objects.create(first_name=\"Clark\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Mommy\")\n        Person.objects.create(first_name=\"Lois\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Jonathan\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Martha\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Lex\", last_name=\"Luthor\")\n        Person.objects.create(first_name=\"Lionel\", last_name=\"Luthor\")\n\n        # no slice\n        a = Person.objects.all()\n        self.assertEqual(8, len(a))\n        self.assertEqual(Person.objects.get_by_id('1'), a[0])\n        self.assertEqual(\"Lionel Luthor\", a[7].full_name())\n\n        a = Person.objects.all()[3:]\n        self.assertEqual(5, len(a))\n        self.assertEqual(Person.objects.get_by_id('4'), a[0])\n        self.assertEqual(\"Lionel Luthor\", a[4].full_name())\n\n        a = Person.objects.all()[:6]\n        self.assertEqual(6, len(a))\n        self.assertEqual(Person.objects.get_by_id('1'), a[0])\n        self.assertEqual(\"Martha Kent\", a[5].full_name())\n\n        a = Person.objects.all()[2:6]\n        self.assertEqual(4, len(a))\n        self.assertEqual(Person.objects.get_by_id('3'), a[0])\n        self.assertEqual(\"Martha Kent\", a[3].full_name())\n\n    def test_get_or_create(self):\n        Person.objects.create(first_name=\"Granny\", last_name=\"Goose\")\n        Person.objects.create(first_name=\"Clark\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Granny\", last_name=\"Mommy\")\n        Person.objects.create(first_name=\"Lois\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Jonathan\", last_name=\"Kent\")\n        Person.objects.create(first_name=\"Martha\", last_name=\"Kent\")\n\n        p = Person.objects.get_or_create(first_name=\"Lois\",\n                last_name=\"Kent\")\n        self.assertEqual('4', p.id)\n        p = Person.objects.get_or_create(first_name=\"Jonathan\",\n                last_name=\"Weiss\")\n        self.assertEqual('7', p.id)\n\n\n    def test_customizable_key(self):\n        class Person(models.Model):\n            name = models.CharField()\n\n            class Meta:\n                key = 'People'\n\n        p = Person(name=\"Clark Kent\")\n        self.assert_(p.is_valid())\n        self.assert_(p.save())\n\n        self.assert_('1' in self.client.smembers('People:all'))\n\n\nclass Event(models.Model):\n    name = models.CharField(required=True)\n    date = models.DateField(required=True)\n\nclass DateFieldTestCase(RediscoTestCase):\n\n    def test_CharField(self):\n        event = Event(name=\"Legend of the Seeker Premiere\",\n                      date=date(2008, 11, 12))\n        self.assertEqual(date(2008, 11, 12), event.date)\n\n    def test_saved_CharField(self):\n        instance = Event.objects.create(name=\"Legend of the Seeker Premiere\",\n                      date=date(2008, 11, 12))\n        assert instance\n        event = Event.objects.get_by_id(instance.id)\n        assert event\n        self.assertEqual(date(2008, 11, 12), event.date)\n\n    def test_invalid_date(self):\n        event = Event(name=\"Event #1\")\n        event.date = 1\n        self.assertFalse(event.is_valid())\n        self.assertTrue(('date', 'bad type') in event.errors)\n\n    def test_indexes(self):\n        d = date.today()\n        Event.objects.create(name=\"Event #1\", date=d)\n        self.assertTrue('1' in self.client.smembers(Event._key['all']))\n        # zfilter index\n        self.assertTrue(self.client.exists(\"Event:date\"))\n        # other field indices\n        self.assertEqual(2, self.client.scard(\"Event:1:_indices\"))\n        for index in self.client.smembers(\"Event:1:_indices\"):\n            self.assertTrue(index.startswith(\"Event:date\") or\n                    index.startswith(\"Event:name\"))\n\n    def test_auto_now(self):\n        class Report(models.Model):\n            title = models.CharField()\n            created_on = models.DateField(auto_now_add=True)\n            updated_on = models.DateField(auto_now=True)\n\n        r = Report(title=\"My Report\")\n        assert r.save()\n        r = Report.objects.filter(title=\"My Report\")[0]\n        self.assertTrue(isinstance(r.created_on, date))\n        self.assertTrue(isinstance(r.updated_on, date))\n        self.assertEqual(date.today(), r.created_on)\n\n\nclass CharFieldTestCase(RediscoTestCase):\n\n    def test_max_length(self):\n        class Person(models.Model):\n            name = models.CharField(max_length=20, required=True)\n\n        p = Person(name='The quick brown fox jumps over the lazy dog.')\n\n        self.assertFalse(p.is_valid())\n        self.assert_(('name', 'exceeds max length') in p.errors)\n\n\nclass Student(models.Model):\n    name = models.CharField(required=True)\n    average = models.FloatField(required=True)\n\nclass FloatFieldTestCase(RediscoTestCase):\n    def test_CharField(self):\n        s = Student(name=\"Richard Cypher\", average=86.4)\n        self.assertEqual(86.4, s.average)\n\n    def test_saved_CharField(self):\n        s = Student.objects.create(name=\"Richard Cypher\",\n                      average=3.14159)\n        assert s\n        student = Student.objects.get_by_id(s.id)\n        assert student\n        self.assertEqual(3.14159, student.average)\n\n    def test_indexing(self):\n        Student.objects.create(name=\"Richard Cypher\", average=3.14159)\n        Student.objects.create(name=\"Kahlan Amnell\", average=92.45)\n        Student.objects.create(name=\"Zeddicus Zorander\", average=99.99)\n        Student.objects.create(name=\"Cara\", average=84.91)\n        good = Student.objects.zfilter(average__gt=50.0)\n        self.assertEqual(3, len(good))\n        self.assertTrue(\"Richard Cypher\",\n                Student.objects.filter(average=3.14159)[0].name)\n\n\n\nclass Task(models.Model):\n    name = models.CharField()\n    done = models.BooleanField()\n\nclass BooleanFieldTestCase(RediscoTestCase):\n    def test_CharField(self):\n        t = Task(name=\"Cook dinner\", done=False)\n        assert t.save()\n        self.assertFalse(t.done)\n\n    def test_saved_CharField(self):\n        t = Task(name=\"Cook dinner\", done=False)\n        assert t.save()\n\n        t = Task.objects.all()[0]\n        self.assertFalse(t.done)\n        t.done = True\n        assert t.save()\n\n        t = Task.objects.all()[0]\n        self.assertTrue(t.done)\n\n    def test_indexing(self):\n        assert Task.objects.create(name=\"Study Lua\", done=False)\n        assert Task.objects.create(name=\"Read News\", done=True)\n        assert Task.objects.create(name=\"Buy Dinner\", done=False)\n        assert Task.objects.create(name=\"Visit Sick Friend\", done=False)\n        assert Task.objects.create(name=\"Play\", done=True)\n        assert Task.objects.create(name=\"Sing a song\", done=False)\n        assert Task.objects.create(name=\"Pass the Exam\", done=True)\n        assert Task.objects.create(name=\"Dance\", done=False)\n        assert Task.objects.create(name=\"Code\", done=True)\n        done = Task.objects.filter(done=True)\n        unfin = Task.objects.filter(done=False)\n        self.assertEqual(4, len(done))\n        self.assertEqual(5, len(unfin))\n\n\n\nclass ListFieldTestCase(RediscoTestCase):\n    def test_basic(self):\n        class Cake(models.Model):\n            name = models.CharField()\n            ingredients = models.ListField(str)\n            sizes = models.ListField(int)\n\n        Cake.objects.create(name=\"StrCake\",\n                            ingredients=['strawberry', 'sugar', 'dough'],\n                            sizes=[1, 2, 5])\n        Cake.objects.create(name=\"Normal Cake\",\n                            ingredients=['sugar', 'dough'],\n                            sizes=[1, 3, 5])\n        Cake.objects.create(name=\"No Sugar Cake\",\n                            ingredients=['dough'],\n                            sizes=[])\n        cake = Cake.objects.all()[0]\n        self.assertEqual(['strawberry', 'sugar', 'dough'],\n                cake.ingredients)\n        with_sugar = Cake.objects.filter(ingredients='sugar')\n        self.assertTrue(2, len(with_sugar))\n        self.assertEqual([1, 2, 5], with_sugar[0].sizes)\n        self.assertEqual([1, 3, 5], with_sugar[1].sizes)\n\n        size1 = Cake.objects.filter(sizes=str(2))\n        self.assertEqual(1, len(size1))\n\n        cake.sizes = None\n        cake.ingredients = None\n        assert cake.save()\n\n        cake = Cake.objects.get_by_id(cake.id)\n        self.assertEqual([], cake.sizes)\n        self.assertEqual([], cake.ingredients)\n\n    def test_list_of_reference_fields(self):\n        class Book(models.Model):\n            title = models.CharField(required=True)\n            date_published = models.DateField(required=True)\n\n        class Author(models.Model):\n            name = models.CharField(required=True)\n            books = models.ListField(Book)\n\n        book = Book.objects.create(\n                title=\"University Physics With Modern Physics\",\n                date_published=date(2007, 4, 2))\n        assert book\n\n        author1 = Author.objects.create(name=\"Hugh Young\",\n                books=[book])\n        author2 = Author.objects.create(name=\"Roger Freedman\",\n                books=[book])\n\n        assert author1\n        assert author2\n        author1 = Author.objects.get_by_id(1)\n        author2 = Author.objects.get_by_id(2)\n        self.assertTrue(book in author1.books)\n        self.assertTrue(book in author2.books)\n\n        book = Book.objects.create(\n                title=\"University Physics With Modern Physics Paperback\",\n                date_published=date(2007, 4, 2))\n\n        author1.books.append(book)\n        assert author1.save()\n\n        author1 = Author.objects.get_by_id(1)\n        self.assertEqual(2, len(author1.books))\n\n    def test_lazy_reference_field(self):\n        class User(models.Model):\n            name = models.CharField()\n            likes = models.ListField('Link')\n\n            def likes_link(self, link):\n                if self.likes is None:\n                    self.likes = [link]\n                    self.save()\n                else:\n                    if link not in self.likes:\n                        self.likes.append(link)\n                        self.save()\n\n        class Link(models.Model):\n            url = models.CharField()\n\n        user = User.objects.create(name=\"Lion King\")\n        assert Link.objects.create(url=\"http://google.com\")\n        assert Link.objects.create(url=\"http://yahoo.com\")\n        assert Link.objects.create(url=\"http://github.com\")\n        assert Link.objects.create(url=\"http://bitbucket.org\")\n\n        links = Link.objects.all().limit(3)\n\n        for link in links:\n            user.likes_link(link)\n\n        user = User.objects.get_by_id(1)\n        self.assertEqual(\"http://google.com\", user.likes[0].url)\n        self.assertEqual(\"http://yahoo.com\", user.likes[1].url)\n        self.assertEqual(\"http://github.com\", user.likes[2].url)\n        self.assertEqual(3, len(user.likes))\n\n\nclass ReferenceFieldTestCase(RediscoTestCase):\n    def test_basic(self):\n        class Word(models.Model):\n            placeholder = models.CharField()\n\n        class Character(models.Model):\n            n = models.IntegerField()\n            m = models.CharField()\n            word = models.ReferenceField(Word)\n\n        Word.objects.create()\n        word = Word.objects.all()[0]\n        Character.objects.create(n=32, m='a', word=word)\n        Character.objects.create(n=33, m='b', word=word)\n        Character.objects.create(n=34, m='c', word=word)\n        Character.objects.create(n=34, m='d')\n        for char in Character.objects.all():\n            if char.m != 'd':\n                self.assertEqual(word, char.word)\n            else:\n                self.assertEqual(None, char.word)\n        a, b, c, d = list(Character.objects.all())\n        self.assertTrue(a in word.character_set)\n        self.assertTrue(b in word.character_set)\n        self.assertTrue(c in word.character_set)\n        self.assertTrue(d not in word.character_set)\n        self.assertEqual(3, len(word.character_set))\n\n    def test_reference(self):\n        class Department(models.Model):\n            name = models.Attribute(required=True)\n\n        class Person(models.Model):\n            name = models.Attribute(required=True)\n            manager = models.ReferenceField('Person', related_name='underlings')\n            department = models.ReferenceField(Department)\n\n        d1 = Department.objects.create(name='Accounting')\n        d2 = Department.objects.create(name='Billing')\n        p1 = Person.objects.create(name='Joe', department=d1)\n        p2 = Person.objects.create(name='Jack', department=d2)\n        self.assertEqual(p1.department_id, p1.department.id)\n        self.assertEqual(p2.department_id, p2.department.id)\n\n    def test_lazy_reference_field(self):\n        class User(models.Model):\n            name = models.CharField()\n            address = models.ReferenceField('Address')\n\n        class Address(models.Model):\n            street_address = models.CharField()\n            city = models.CharField()\n            zipcode = models.CharField()\n\n        address = Address.objects.create(street_address=\"32/F Redisville\",\n                city=\"NoSQL City\", zipcode=\"1.3.18\")\n        assert address\n        user = User.objects.create(name=\"Richard Cypher\", address=address)\n        assert user\n\n        a = Address.objects.all()[0]\n        u = User.objects.all()[0]\n        self.assertTrue(u in a.user_set)\n        self.assertEqual(\"32/F Redisville\", u.address.street_address)\n        self.assertEqual(\"NoSQL City\", u.address.city)\n        self.assertEqual(\"1.3.18\", u.address.zipcode)\n\n\nclass DateTimeFieldTestCase(RediscoTestCase):\n\n    def test_basic(self):\n        from datetime import datetime\n        n = datetime(2009, 12, 31)\n        class Post(models.Model):\n            title = models.CharField()\n            date_posted = models.DateTimeField()\n            created_at = models.DateTimeField(auto_now_add=True)\n        post = Post(title=\"First!\", date_posted=n)\n        assert post.save()\n        post = Post.objects.get_by_id(post.id)\n        self.assertEqual(n, post.date_posted)\n        assert post.created_at\n\n\nclass CounterFieldTestCase(RediscoTestCase):\n\n    def test_basic(self):\n        class Post(models.Model):\n            title = models.CharField()\n            body = models.CharField(indexed=False)\n            liked = models.Counter()\n\n        post = Post.objects.create(title=\"First!\",\n                body=\"Lorem ipsum\")\n        self.assert_(post)\n        post.incr('liked')\n        post.incr('liked', 2)\n        post = Post.objects.get_by_id(post.id)\n        self.assertEqual(3, post.liked)\n        post.decr('liked', 2)\n        post = Post.objects.get_by_id(post.id)\n        self.assertEqual(1, post.liked)\n\n\nclass MutexTestCase(RediscoTestCase):\n\n    def setUp(self):\n        super(MutexTestCase, self).setUp()\n        self.p1 = Person.objects.create(first_name=\"Dick\")\n        self.p2 = Person.objects.get_by_id(self.p1.id)\n\n    def test_instance_should_not_modify_locked(self):\n        time1, time2 = {}, {}\n\n        def f1(person, t):\n            with Mutex(person):\n                time.sleep(0.4)\n                t['time'] = time.time()\n\n        def f2(person, t):\n            with Mutex(person):\n                t['time'] = time.time()\n\n        t1 = Thread(target=f1, args=(self.p1, time1,))\n        t2 = Thread(target=f2, args=(self.p2, time2,))\n        t1.start()\n        time.sleep(0.1)\n        t2.start()\n        t1.join()\n        t2.join()\n        self.assert_(time2['time'] > time1['time'])\n\n    def test_lock_expired(self):\n        Mutex(self.p1).lock()\n        with Mutex(self.p2):\n            self.assert_(True)\n"
  }
]