{ "info": { "author": "Lovely Systems", "author_email": "office@lovelysystems.com", "bugtrack_url": null, "classifiers": [], "description": "Lovely Appengine Packages\n*************************\n\n================\nlovely.gae.async\n================\n\nThis package executes jobs asynchronously, it uses the appengine\ntaskqueue to exectue the jobs.\n\n >>> from lovely.gae.async import defer, get_tasks\n\nThe defer function executes a handler asynchronously as a job. We\ncreate 3 jobs that have the same signature.\n\n >>> import time\n >>> for i in range(3):\n ... print defer(time.sleep, [0.3])\n \n None\n None\n\nLet us have a look on what jobs are there. Note that there is only one\nbecause all the 3 jobs we added were the same.\n\n >>> len(get_tasks())\n 1\n\nIf we change the signature of the job, a new one will be added.\n\n >>> added = defer(time.sleep, [0.4])\n >>> len(get_tasks())\n 2\n\nNormally jobs are automatically executed by the taskqueueapi, we have\na test method which executes the jobs and returns the number of jobs\ndone.\n\n >>> run_tasks()\n 2\n\nNow we can add the same signature again.\n\n >>> added = defer(time.sleep, [0.4])\n >>> run_tasks()\n 1\n\nWe can also set only_once to false to execute a worker many times with\nthe same signature.\n\n >>> from pprint import pprint\n >>> defer(pprint, ['hello'], once_only=False)\n \n >>> defer(pprint, ['hello'], once_only=False)\n \n >>> run_tasks()\n 'hello'\n 'hello'\n 2\n\n\n\n\n\n==========================\nDB Custom Property Classes\n==========================\n\nTyped Lists\n===========\n\nThis property converts model instances to keys and ensures a length.\n\n >>> from lovely.gae.db.property import TypedListProperty\n >>> from google.appengine.ext import db\n\nLet us create a model\n\n >>> class Yummy(db.Model): pass\n >>> class Bummy(db.Model): pass\n\nWe can now reference Yummy instances with our property. Note that we\ncan also use the kind name as an argument of the kind.\n\n >>> class Dummy(db.Model):\n ... yummies = TypedListProperty(Yummy)\n ... bummies = TypedListProperty('Bummy', length=3)\n\nThe kind arguement needs to be a subclass kind name or a db.Model.\n\n >>> TypedListProperty(object)\n Traceback (most recent call last):\n ...\n ValueError: Kind needs to be a subclass of db.Model\n\n >>> dummy = Dummy()\n >>> dummy_key = dummy.put()\n >>> yummy1 = Yummy()\n >>> yummy1_key = yummy1.put()\n >>> dummy.yummies = [yummy1]\n\nWe cannot set any other type.\n\n >>> bummy1 = Bummy()\n >>> bummy1_key = bummy1.put()\n >>> dummy.yummies = [bummy1]\n Traceback (most recent call last):\n ...\n BadValueError: Wrong kind u'Bummy'\n\nThe length needs to match if defined (see above).\n\n >>> dummy.bummies = [bummy1]\n Traceback (most recent call last):\n ...\n BadValueError: Wrong length need 3 got 1\n\n >>> dummy.bummies = [bummy1, bummy1, bummy1]\n >>> dummy_key == dummy.put()\n True\n\nCase-Insensitive String Property\n================================\n\nThis property allows searching for the lowercase prefix in a\ncase-insensitive manner. This is usefull for autocomplete\nimplementations where we do not want to have a seperate property just\nfor searching.\n\n >>> from lovely.gae.db.property import IStringProperty\n >>> class Foo(db.Model):\n ... title = IStringProperty()\n\n >>> f1 = Foo(title='Foo 1')\n >>> kf1 = f1.put()\n >>> f2 = Foo(title='Foo 2')\n >>> kf2 = f2.put()\n >>> f3 = Foo(title='foo 3')\n >>> kf3 = f3.put()\n >>> f4 = Foo(title=None)\n >>> kf4 = f4.put()\n\nThe property does not allow the special seperator character which is\njust one below the highest unicode character,\n\n >>> f3 = Foo(title='Foo 3' + IStringProperty.SEPERATOR)\n Traceback (most recent call last):\n ...\n BadValueError: Not all characters in property title\n\nNote that if we want to do an exact search, we have to use a special\nfilter that can be created by the property instance.\n\n >>> [f.title for f in Foo.all().filter('title =', 'foo 1')]\n []\n\nThe \"equal\" filter arguments can be computed with a special method on\nthe property.\n\n >>> ef = Foo.title.equals_filter('Foo 1')\n >>> ef\n ('title =', u'foo 1\\xef\\xbf\\xbcFoo 1')\n\n >>> [f.title for f in Foo.all().filter(*ef)]\n [u'Foo 1']\n\nLet us try with inequallity, e.g. prefix search. Prefix search is\nnormally done with two filters using the highest unicode character.\n\nSearch for all that starts with \"fo\" case-insensitive.\n\n >>> q = Foo.all()\n >>> q = q.filter('title >=', 'fo')\n >>> q = q.filter('title <', 'fo' + u'\\xEF\\xBF\\xBD')\n >>> [f.title for f in q]\n [u'Foo 1', u'Foo 2', u'foo 3']\n\nSearch for all that starts with 'foo 1'\n\n >>> q = Foo.all()\n >>> q = q.filter('title >=', 'foo 1')\n >>> q = q.filter('title <', 'foo 1' + u'\\xEF\\xBF\\xBD')\n >>> [f.title for f in q]\n [u'Foo 1']\n\n >>> q = Foo.all()\n >>> q = q.filter('title >=', 'foo 2')\n >>> q = q.filter('title <=', 'foo 2' + u'\\xEF\\xBF\\xBD')\n >>> [f.title for f in q]\n [u'Foo 2']\n\n >>> q = Foo.all()\n >>> q = q.filter('title >=', 'foo 3')\n >>> q = q.filter('title <=', 'foo 3' + u'\\xEF\\xBF\\xBD')\n >>> [f.title for f in q]\n [u'foo 3']\n\n\nPickle Property\n===============\n\nA pickle property can hold any pickleable python object.\n\n >>> from lovely.gae.db.property import PickleProperty\n\n >>> class Pickie(db.Model):\n ... data = PickleProperty()\n\n >>> pickie = Pickie(data={})\n >>> pickie.data\n {}\n >>> kp = pickie.put()\n >>> pickie.data\n {}\n >>> pickie = db.get(kp)\n >>> pickie.data\n {}\n >>> pickie.data = {'key':501*\"x\"}\n >>> kp = pickie.put()\n >>> pickie.data\n {'key': 'xx...xx'}\n\nIf the value is not pickleable we get a validation error.\n\n >>> pickie = Pickie(data=dict(y=lambda x:x))\n Traceback (most recent call last):\n BadValueError: Property 'data' must be pickleable:\n (Can't pickle at ...>:\n it's not found as __main__.)\n\n\nSafe ReferenceProperty\n======================\n\n >>> from lovely.gae.db.property import SafeReferenceProperty\n\nWe use a new model with a gae reference and our safe reference.\n\n >>> class Refie(db.Model):\n ... ref = db.ReferenceProperty(Yummy, collection_name='ref_ref')\n ... sfref = SafeReferenceProperty(Yummy, collection_name='sfref_ref')\n\n >>> refie = Refie()\n >>> refie.sfref is None\n True\n >>> refie.ref is None\n True\n\nAn object to be referenced.\n\n >>> refyummy1 = Yummy()\n >>> ignore = refyummy1.put()\n\nSet the references to our yummy object.\n\n >>> refie.sfref = refyummy1\n >>> refie.sfref\n \n >>> refie.ref = refyummy1\n >>> refie.ref\n \n\n >>> refieKey = refie.put()\n\nNow we delete the referenced object.\n\n >>> refyummy1.delete()\n\nAnd reload our referencing object.\n\n >>> refie = db.get(refieKey)\n\nThe gae reference raises an exception.\n\n >>> refie.ref\n Traceback (most recent call last):\n Error: ReferenceProperty failed to be resolved\n\nWe catch the logs here.\n\n >>> import logging\n >>> from StringIO import StringIO\n >>> log = StringIO()\n >>> handler = logging.StreamHandler(log)\n >>> logger = logging.getLogger('lovely.gae.db')\n >>> logger.setLevel(logging.INFO)\n >>> logger.addHandler(handler)\n\nOur safe reference returns None.\n\n >>> pos = log.tell()\n >>> refie.sfref is None\n True\n\nLet's see what the log contains.\n\n >>> log.seek(pos)\n >>> print log.read()\n Unresolved Reference for \"Refie._sfref\" set to None\n\nAccessing the stale property once again we will see it was reset to None::\n\n >>> pos = log.tell()\n >>> refie.sfref is None\n True\n\n >>> log.seek(pos)\n >>> print log.read() == ''\n True\n\nThe property get set to None if the reference points to a dead object but only\nif the property is not required::\n\n >>> class Requy(db.Model):\n ... sfref = SafeReferenceProperty(Yummy, collection_name='req_sfref_ref',\n ... required=True)\n\n >>> refyummy1 = Yummy()\n >>> ignore = refyummy1.put()\n\n >>> requy = Requy(sfref = refyummy1)\n >>> requyKey = requy.put()\n\n >>> requy.sfref\n \n\n >>> refyummy1.delete()\n\n >>> requy = db.get(requyKey)\n\n >>> pos = log.tell()\n >>> requy.sfref is None\n True\n\n >>> log.seek(pos)\n >>> print log.read()\n Unresolved Reference for \"Requy._sfref\" will remain because it is required\n\n=====================\nBatch marker creation\n=====================\n\nThis packages provides the possibility to create markers for every N\nobjects of a given query. This is useful to create batched html pages\nor to generate jobs for every N objects.\n\nA list of attribute values are created that represent the end of a\nbatch at any given position in a given query. The result is stored in\nmemcache and the key is provided to a callback function.\n\n >>> from lovely.gae import batch\n\nLet us create some test objects.\n\n >>> from google.appengine.ext import db\n >>> class Stub(db.Model):\n ... c_time = db.DateTimeProperty(auto_now_add=True, required=True)\n ... name = db.StringProperty()\n ... age = db.IntegerProperty()\n ... state = db.IntegerProperty()\n ... def __repr__(self):\n ... return '' % self.name\n\n >>> for i in range(1,13):\n ... s = Stub(name=str(i), age=i, state=i%2)\n ... sk = s.put()\n\n >>> Stub.all().count()\n 12\n\nFirst we make sure that we have no tasks in the queue for testing.\n\n >>> from lovely.gae.async import get_tasks\n >>> len(get_tasks())\n 0\n\nSo for example if we want to know any 100th key of a given kind we\ncould calculate it like shown below. Note that we provide the pprint function\nas a callback, so we get the memcache key in the output.\n\nThe calculate_markers function returns the memcache key that will be\nused to store the result when the calculation is completed.\n\n >>> from pprint import pprint\n >>> mc_key = batch.create_markers('Stub', callback=pprint)\n >>> mc_key\n 'create_markers:...-...-...'\n\nA task gets created.\n\n >>> tasks = get_tasks()\n >>> len(tasks)\n 1\n\nLet us run the task.\n\n >>> run_tasks(1)\n 1\n\nWe now have another task left for the callback, which is actually the\npprint function.\n\n >>> run_tasks()\n 'create_markers:...-...-...'\n 1\n\nWe should now have a result. The result shows that we need no batches\nfor the given batch size (because we only have 12 objects).\n\n >>> from google.appengine.api import memcache\n >>> memcache.get(mc_key)\n []\n\nLet us use another batch size. This time without callback.\n\n >>> mc_key = batch.create_markers('Stub', batchsize=1)\n >>> run_tasks()\n 1\n\nWe now have exatly 12 keys, because the batch size was 1.\n\n >>> len(memcache.get(mc_key))\n 12\n\nThe default attributes returned are the keys.\n\n >>> memcache.get(mc_key)\n [datastore_types.Key.fro...\n\nWe can also use other attributes. Let us get items batched by c_time\ndescending. Note that it is not checked if values are not unique, so\nif a non-unique attribute is used it might be the case that batch\nranges contains objects twice.\n\n >>> mc_key = batch.create_markers('Stub',\n ... attribute='c_time',\n ... order='desc',\n ... batchsize=3)\n >>> run_tasks()\n 1\n >>> markers = memcache.get(mc_key)\n >>> markers\n [datetime.datetime(...\n >>> len(markers)\n 4\n >>> sorted(markers, reverse=True) == markers\n True\n\n >>> mc_key = batch.create_markers('Stub',\n ... attribute='c_time',\n ... order='asc',\n ... batchsize=3)\n >>> run_tasks()\n 1\n >>> markers = memcache.get(mc_key)\n >>> markers\n [datetime.datetime(...\n >>> len(markers)\n 4\n >>> sorted(markers) == markers\n True\n\n\nWe can also pass filters to be applied to the query for the batch like this:\n\n >>> mc_key = batch.create_markers('Stub',\n ... filters=[('state', 0)],\n ... attribute='c_time',\n ... order='asc',\n ... batchsize=3)\n >>> run_tasks()\n 1\n >>> markers = memcache.get(mc_key)\n >>> len(markers)\n 2", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://code.google.com/p/lovely-gae/", "keywords": "appengine datastore backup batch utilities", "license": "Apache License 2.0", "maintainer": null, "maintainer_email": null, "name": "lovely.gae", "package_url": "https://pypi.org/project/lovely.gae/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/lovely.gae/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://code.google.com/p/lovely-gae/" }, "release_url": "https://pypi.org/project/lovely.gae/1.0.0a2/", "requires_dist": null, "requires_python": null, "summary": "Appengine related Python Packages from Lovely Systems", "version": "1.0.0a2" }, "last_serial": 794365, "releases": { "0.5.0a2": [ { "comment_text": "", "digests": { "md5": "3d4fb6ee1174bba44be80b3352dfbd73", "sha256": "de295bb9809c0d10695a23105cc652296fb45fe66c9d08311964a066b51ac6b4" }, "downloads": -1, "filename": "lovely.gae-0.5.0a2.tar.gz", "has_sig": false, "md5_digest": "3d4fb6ee1174bba44be80b3352dfbd73", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 27949, "upload_time": "2009-09-01T17:06:29", "url": "https://files.pythonhosted.org/packages/59/99/71816650bcf67c79628c0257cff1c5c78913f72bca1733070815a80072e9/lovely.gae-0.5.0a2.tar.gz" } ], "0.5.0a3": [ { "comment_text": "", "digests": { "md5": "dcc023abd29ebecdc45caff99bcad70f", "sha256": "eb68e07b886bbf52a1ddbc8127291a043cb3ea07b6f1bfecb771dd50d1705984" }, "downloads": -1, "filename": "lovely.gae-0.5.0a3.tar.gz", "has_sig": false, "md5_digest": "dcc023abd29ebecdc45caff99bcad70f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28204, "upload_time": "2009-09-02T10:29:44", "url": "https://files.pythonhosted.org/packages/b6/e4/b38677e73884320366399d7bf1c81b9cde469f3f12592a080b0491306a23/lovely.gae-0.5.0a3.tar.gz" } ], "0.5.0a4": [ { "comment_text": "", "digests": { "md5": "2aa37fff9c4b30f02695a99ffe023e02", "sha256": "1ecee36eec184772b437455073d030b5fbeaf94d1263206a313cb3480f2459e1" }, "downloads": -1, "filename": "lovely.gae-0.5.0a4.tar.gz", "has_sig": false, "md5_digest": "2aa37fff9c4b30f02695a99ffe023e02", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28405, "upload_time": "2009-09-08T08:50:55", "url": "https://files.pythonhosted.org/packages/92/5f/67cb847544f2b1a821ff33b12d891f2dba7c11b15903f2c0eb97c65bdd7b/lovely.gae-0.5.0a4.tar.gz" } ], "0.5.0a5": [ { "comment_text": "", "digests": { "md5": "016431a8f08f8614f7dde4e9c227a0d5", "sha256": "228f7e76edf01273c9b7d07ced84075712b556fddfc351f6570334cdb85f1875" }, "downloads": -1, "filename": "lovely.gae-0.5.0a5.tar.gz", "has_sig": false, "md5_digest": "016431a8f08f8614f7dde4e9c227a0d5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34225, "upload_time": "2010-01-04T11:58:55", "url": "https://files.pythonhosted.org/packages/12/55/5330fe931c8e3bc76ab322013789e06090c4dcfe99705388a63b17164563/lovely.gae-0.5.0a5.tar.gz" } ], "0.5.0a6": [ { "comment_text": "", "digests": { "md5": "ec8e33d4b010a9551e8645de13f9f004", "sha256": "e9c7cbc230a3bfaf4bdffd0627f57a92118a8e1a2b541b457907ec958132a39f" }, "downloads": -1, "filename": "lovely.gae-0.5.0a6.tar.gz", "has_sig": false, "md5_digest": "ec8e33d4b010a9551e8645de13f9f004", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34453, "upload_time": "2010-01-05T14:34:21", "url": "https://files.pythonhosted.org/packages/77/59/36a3598e97b42ec5d9b04326642f5c2cd638452ffeb34f5b513077f46572/lovely.gae-0.5.0a6.tar.gz" } ], "0.5.0a8": [ { "comment_text": "", "digests": { "md5": "78ae7bcb1c563c0a0e7c34ff9e33f109", "sha256": "7d54df329f7eb319a03918086df3a3651bdc73e66f40bc9ac24fc5315a6a8e4e" }, "downloads": -1, "filename": "lovely.gae-0.5.0a8.tar.gz", "has_sig": false, "md5_digest": "78ae7bcb1c563c0a0e7c34ff9e33f109", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35448, "upload_time": "2010-05-17T11:09:55", "url": "https://files.pythonhosted.org/packages/0c/f4/95969d55fc2dd231fa6d1c24427a62968ff81763703a7c0762fa62ac1193/lovely.gae-0.5.0a8.tar.gz" } ], "1.0.0a1": [ { "comment_text": "", "digests": { "md5": "c8c8eaf06ea4a5be6c4f3d86d91144b2", "sha256": "1f7d196db7d406406358d580e650d4322edef793998c0da403ca61bf41e8fa9f" }, "downloads": -1, "filename": "lovely.gae-1.0.0a1.tar.gz", "has_sig": false, "md5_digest": "c8c8eaf06ea4a5be6c4f3d86d91144b2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23297, "upload_time": "2010-06-11T11:24:54", "url": "https://files.pythonhosted.org/packages/cf/4a/63c0c7f6c3cc1eb3afb44f5fbb8cfe8c2a40c18a9c6e8c83f85381c6cb1d/lovely.gae-1.0.0a1.tar.gz" } ], "1.0.0a2": [ { "comment_text": "", "digests": { "md5": "59f5858de18b1f7cf0cadb7a67a844ea", "sha256": "44768188e14cef5967700491ad141bfccdfb4a57cf784132a68194863ed38cdd" }, "downloads": -1, "filename": "lovely.gae-1.0.0a2.tar.gz", "has_sig": false, "md5_digest": "59f5858de18b1f7cf0cadb7a67a844ea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23332, "upload_time": "2010-06-11T12:45:49", "url": "https://files.pythonhosted.org/packages/70/f7/3085010612aab92592163b7d7323029f0fc4cb8316bffe6684f48f429ae7/lovely.gae-1.0.0a2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "59f5858de18b1f7cf0cadb7a67a844ea", "sha256": "44768188e14cef5967700491ad141bfccdfb4a57cf784132a68194863ed38cdd" }, "downloads": -1, "filename": "lovely.gae-1.0.0a2.tar.gz", "has_sig": false, "md5_digest": "59f5858de18b1f7cf0cadb7a67a844ea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23332, "upload_time": "2010-06-11T12:45:49", "url": "https://files.pythonhosted.org/packages/70/f7/3085010612aab92592163b7d7323029f0fc4cb8316bffe6684f48f429ae7/lovely.gae-1.0.0a2.tar.gz" } ] }