{ "info": { "author": "Michael Sparks (sparkslabs)", "author_email": "sparks.m@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "Python-Guild\n============\n\nAbstract\n--------\n\nGuild is a basic pipelineable Actor system, currently based around\nthreads and a developer friendly syntax. In particular it introduces 2\nnotions to an actor system - the idea of late binding, and of having\ncommon names (or aliases) for the purposes of enabling pipelining.\n\nIt's inspired by Kamaelia, but with all the ugly parts removed.\n\nThe ability to have dynamic actor methods would be useful.\n\nMichael.\n\nIntroduction/Overview\n---------------------\n\nAt present the documentation is the code itself. That's not great\ndocumentation, so this file consists of an overview of Guild.\n\nGuild is a python library for creating thread based applications.\n\nThreads are represented using actors - objects with threadsafe methods.\nCalling a method puts a message on an inbound queue for execution within\nthe thread. Guild actors can also have stub actor methods, representing\noutput. These are stub methods which are expected to be rebound to actor\nmethods on other actors. These stub methods are called late bind\nmethods. This allows pipelines of Guild actors to be created in a\nsimilar way to Unix pipelines.\n\nAdditionally, Guild actors can be active or reactive. A reactive actor\nperforms no actions until a message is received. An active guild actor\ncan be active in two main ways: it can either repeatedly perform an\naction, or more complex behaviour can use a generator in a coroutine\nstyle. The use of a generator allows Guild actors to be stopped in a\nsimpler fashion than traditional python threads. Finally, all Guild\nactors provide a default 'output' late-bindable method, to cover the\ncommon case of single input, single output.\n\nFinally, Guild actors are just python objects and actors with additional\nfunctionality - it's designed to fit in with your code, not the other\nway round. This post covers some simple examples of usage of Guild, and\nhow it differs (slightly) from traditional actors.\n\nGetting and Installing\n----------------------\n\nInstallation is pretty simple:\n\n::\n\n $ git clone https://github.com/sparkslabs/guild\n $ cd guild\n $ sudo python setup.py install\n\nIf you'd prefer to build, install and use a debian package:\n\n::\n\n $ git clone https://github.com/sparkslabs/guild\n $ cd guild\n $ make deb\n $ sudo dpkg -i ../python-guild_1.0.0_all.deb\n\nExample: viewing a webcam\n-------------------------\n\nThis example shows the use of two actors - webcam capture, and image\ndisplay. The thing to note here is that we could easily add other actors\ninto the mix - for network serving, recording, analysis, etc. If we did,\nthe examples below can be reused as is.\n\nFirst of all the code, then a brief discussion.\n\n::\n\n import pygame, pygame.camera, time\n from guild.actor import *\n pygame.camera.init()\n\n class Camera(Actor):\n def gen_process(self):\n camera = pygame.camera.Camera(pygame.camera.list_cameras()[0])\n camera.start()\n while True:\n yield 1\n frame = camera.get_image()\n self.output(frame)\n time.sleep(1.0/50)\n\n class Display(Actor):\n def __init__(self, size):\n super(Display, self).__init__()\n self.size = size\n\n def process_start(self):\n self.display = pygame.display.set_mode(self.size)\n\n @actor_method\n def show(self, frame):\n self.display.blit(frame, (0,0))\n pygame.display.flip()\n\n input = show\n\n camera = Camera().go()\n display = Display( (800,600) ).go()\n pipeline(camera, display)\n time.sleep(30)\n stop(camera, display)\n wait_for(camera, display)\n\nIn this example, Camera is an active actor. That is it sits there,\nperiodically grabbing frames from the webcam. To do this, it uses a\ngenerator as a main loop. This allows the fairly basic behaviour of\ngrabbing frames for output to be clearly expressed. Note also this actor\ndoes use the normal blocking sleep function.\n\nThe Display Actor initialises by capturing the passed parameters. Once\nthe actor has started, its process\\_start method is called, enabling it\nto create a display, it then sits and waits for messages. These arrive\nwhen a caller calls the actor method 'show' or its alias 'input'. When\nthat happens the upshot is that the show method is called, but in a\nthreadsafe way - and it simply displays the image.\n\nThe setup/tear down code shows the following:\n\n- Creation of, and starting of, the Camera actor\n- Creation and start of the display\n- Linking the output of the Camera to the Display\n- The main thread then waits for 30 seconds - ie it allows the program\n to run for 30 seconds.\n- The camera and display actors are then stopped\n- And the main thread waits for the child threads to exit before\n exitting itself.\n\nThis could be simplified (and will be), but it shows that even though\nthe actors had no specific shut down code, they shut down cleanly this\nway.\n\nExample: following multiple log files looking for events\n--------------------------------------------------------\n\nThis example follows two log files, and grep/output lines matching a\ngiven pattern. In particular, it maps to this kind of command line:\n\n::\n\n $ (tail -f x.log & tail -f y.log) | grep pants\n\nThis example shows that there are still some areas that would benefit\nfrom additional syntactic sugar when it comes to wiring together\npipelines. In particular, this example should be writable together like\nthis:\n\n::\n\n Pipeline( Parallel( Follow(\"x.log\"), Follow(\"y.log\"),\n Grep(\"pants\"),\n Printer() ).run()\n\nHowever, I haven't implemented the necessary chassis yet (they will be).\n\nOnce again, first the code, then a discussion.\n\n::\n\n from guild.actor import *\n import re, sys, time\n\n class Follow(Actor):\n def __init__(self, filename):\n super(Follow, self).__init__()\n self.filename = filename\n self.f = None\n\n def gen_process(self):\n self.f = f = file(self.filename)\n f.seek(0,2) # seek to end\n while True:\n yield 1\n line = f.readline()\n if not line: # no data, so wait\n time.sleep(0.1)\n else:\n self.output(line)\n\n def onStop(self):\n if self.f:\n self.f.close()\n\n class Grep(Actor):\n def __init__(self, pattern):\n super(Grep, self).__init__()\n self.regex = re.compile(pattern)\n\n @actor_method\n def input(self, line):\n if self.regex.search(line):\n self.output(line)\n\n class Printer(Actor):\n @actor_method\n def input(self, line):\n sys.stdout.write(line)\n sys.stdout.flush()\n\n follow1 = Follow(\"x.log\").go()\n follow2 = Follow(\"y.log\").go()\n grep = Grep(\"pants\").go()\n printer = Printer().go()\n\n pipeline(follow1, grep, printer)\n pipeline(follow2, grep)\n wait_KeyboardInterrupt()\n stop(follow1, follow2, grep, printer)\n wait_for(follow1, follow2, grep, printer)\n\nAs you can see, like the bash example, we have two actors that\ntail/follow two different log files. These both feed into the same\n'grep' actor that matches the given pattern, and these are finally\npassed to a Printer actor for display. Each actor shows slightly\ndifferent aspects of Guild's model.\n\n- **Follow** is an active actor. It captures the filename to follow in\n the initialiser, and creates a placeholder for the associated file\n handle. The main loop them follows the file, calling its output\n method when it has a line. Finally, it will continue doing this until\n its .stop() method is called. When it is, the generator is killed\n (via a StopIteration exception being passed in), and the actor's\n onStop method is called allowing the actor to close the file.\n\n- **Grep** is a simple reactive actor with some setup. In particular,\n it takes the pattern provided, compiles a regex matcher using it.\n Then any actor call to its input method results in any matching lines\n to be passed through via its output method.\n\n- **Printer** is a simple reactive actor. Any actor call to its input\n method results in the data passed in being sent to stdout.\n\nWork in progress\n^^^^^^^^^^^^^^^^\n\n**It is worth noting that Guild at present is not a mature library\nyet,** **but is sufficiently useful for lots of tasks.** In particular,\none area Guild will improve on in - specifying coordination more\ncompactly. For example, the Camera example could become:\n\n::\n\n Pipeline( Camera(), Display( (800,600) ) ).run()\n\nThat's a work in progress however, adding with other chassis, and other\nuseful parts of kamaelia.\n\nWhat are actors?\n----------------\n\nActors are threads with a mailbox allowing them to receive and act upon\nmessages. In the above webcam example, it has 2 threads, one for\ncapturing images, and one for display. Images from the webcam end up in\nthe mailbox for the display, which displays images it receives. Often\nactor libraries wrap up the action of sending a message to the mailbox\nof an actor via a method on the thread object.\n\nThe examples above demonstrate this above via the decorated methods:\n\n- Display.show, Grep.input, Printer.input\n\nAll of these methods - when called by a client of the actor - take all\nthe arguments passed in, along with their function and place on the\nactor's mailbox (a thread safe queue). The actor then has a main loop\nthat checks this mailbox and executes the method within the thread.\n\nHow does Guild differ from the actor model?\n-------------------------------------------\n\nIn a traditional actor model, the code in the camera Actor might look\nlike this:\n\n::\n\n import pygame, pygame.camera, time\n from guild.actor import *\n pygame.camera.init()\n\n class Camera(Actor):\n def __init__(self, display):\n super(Camera, self).__init__()\n self.display = display\n\n def gen_process(self):\n camera = pygame.camera.Camera(pygame.camera.list_cameras()[0])\n camera.start()\n while True:\n yield 1\n frame = camera.get_image()\n self.display.show(frame)\n time.sleep(1.0/50)\n\n- **NB: This is perfectly valid in Guild.** If you don't want to use\n the idea of late bound methods or pipelining, then it can be used\n like any other actor library.\n\nIf you did this, the display code would not need any changes. The\nstart-up code that links things together though would now need to look\nlike this:\n\n::\n\n display = Display( (800,600) ).go()\n camera = Camera(display).go()\n # No pipeline line anymore\n time.sleep(30)\n stop(camera, display)\n wait_for(camera, display)\n\nOn the surface of things, this looks like a simplification, and on one\nlevel it is - we've removed one line from the program start-up code. Our\ncamera object however now has its destination embedded at object\ninitialisation and it's also become more complex, with zero increase in\nflexibility. In fact I'd argue you've *lost* flexibility, but I'll leave\nwhy for later.\n\nFor example, suppose we want to record the images to disk, we can do\nthis by adding a third actor that can sit in the middle of others:\n\n::\n\n import time, os\n class FrameStore(Actor):\n def __init__(self, directory='Images', base='snap'):\n super(FrameStore, self).__init__()\n self.directory = directory\n self.base = base\n self.count = 0\n\n def process_start(self):\n os.makedir(self.directory)\n try:\n os.makedirs(\"Images\")\n except OSError, e:\n if e.errno != 17: raise\n\n @actor_method\n def input(self, frame):\n self.count += 1\n now = time.strftime(\"%Y%m%d-%H%M%S\",time.localtime())\n filename = \"%s/%s-%s-%05d.jpg\" % (self.directory, self.base, now, self.count)\n pygame.image.save(frame, filename)\n self.output(frame)\n\nThis could then be used in a Guild pipeline system this way:\n\n::\n\n camera = Camera().go()\n framestore = FrameStore().go()\n display = Display( (800,600) ).go()\n pipeline(camera, framestore, display) \n time.sleep(30)\n stop(camera, framestore, display) \n wait_for(camera, framestore, display)\n\nIt's for this reason that Guild supports late bindable actor methods.\n\nWhat's happening here is that the definition of Actor includes this:\n\n::\n\n class Actor(object):\n #...\n @late_bind_safe\n def output(self, *argv, **argd):\n pass\n\nThat means every actor has available \"output\" as a late bound actor\nmethod.\n\nThis pipeline called:\n\n::\n\n pipeline(camera, display)\n\nEssentially does this:\n\n::\n\n camera.bind(\"output\", display, \"input\")\n\nThis transforms to a threadsafe version of this:\n\n::\n\n camera.output = display.input\n\nAs a result, it replaces the call camera.output with a call to\ndisplay.input for us - meaning that it is as efficient to do\ncamera.output as it is to do self.display.show in the example above -\nbut significantly more flexible.\n\nThere are lots of fringe benefits of this - which are best discussed in\nlater posts, but this does indicate best how Guild differs from the\nusual actor model.\n\nWhy write and release this?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn late 2013/early 2013, I was working on a project with an aim of\ninvestigating various ideas relating to of the Internet of Things. (In\nparticular, which definition of that really mattered to us, why, and what\noptions it provided)\n\nAs part of that project, I wrote a small/just big enough library\nsuitable for testing some ideas I'd had regarding integrating some ideas\nin Kamaelia, with the syntactic sugar in the actor model. Essentially,\nto map Kamaelia's inboxes and messages to traditional actor methods, and\nmaps outboxes to late bound actor methods. Use of standard names and/or\naliases would allow pipelining.\n\nGuild was the result, and it's proven itself useful in a couple of\nprojects, hence its packaging as a standalone library. Like all such\nthings, it's a work in progress, but it also has a cleaner to use\nversion of Kamaelia's STM code, and includes some of the more useful\ncomponents like pipelines and backplanes.\n\nIf you find it useful or spot a typo, please let me know.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://sparkslabs.com/michael/", "keywords": null, "license": "Apache Software License", "maintainer": null, "maintainer_email": null, "name": "guild", "package_url": "https://pypi.org/project/guild/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/guild/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://sparkslabs.com/michael/" }, "release_url": "https://pypi.org/project/guild/1.1.3/", "requires_dist": null, "requires_python": null, "summary": "An Actor library supporting concurrency using pipelinable pythonic actors", "version": "1.1.3" }, "last_serial": 2687601, "releases": { "1.0.2": [ { "comment_text": "", "digests": { "md5": "b639af2415fc795f16e246de536f860a", "sha256": "96c36bb3bf24c6e06bbb41bb2b235c2d4ee5d4c5282f103923c77c790a443778" }, "downloads": -1, "filename": "guild-1.0.2.tar.gz", "has_sig": false, "md5_digest": "b639af2415fc795f16e246de536f860a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19632, "upload_time": "2015-07-28T15:27:10", "url": "https://files.pythonhosted.org/packages/7c/e0/d37b1e693f120fd4a7a5762264a5160d9f749d9a34bd9e8351e8bed62e5c/guild-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "77cdb4d887f26a26d71991aeb6356fa2", "sha256": "7e70c66e2f2bc36ec543ac0deb6c8ec0ff84763fff6816cdf3e93364cea64b1a" }, "downloads": -1, "filename": "guild-1.0.3.tar.gz", "has_sig": false, "md5_digest": "77cdb4d887f26a26d71991aeb6356fa2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44840, "upload_time": "2015-07-28T16:30:08", "url": "https://files.pythonhosted.org/packages/ec/e4/2a823c212eb5f47831a0fc2ae9367bab08a80c0d067ff7c7994c7ca12024/guild-1.0.3.tar.gz" } ], "1.0.3-1": [ { "comment_text": "", "digests": { "md5": "65bd40920b73435ed9d873d3373dfaff", "sha256": "7a20f3d2a1ac2ee518799ef2a7c0e4b84cd30404265ad9bfa747abbea9b89541" }, "downloads": -1, "filename": "guild-1.0.3-1.tar.gz", "has_sig": false, "md5_digest": "65bd40920b73435ed9d873d3373dfaff", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44831, "upload_time": "2015-07-28T16:32:43", "url": "https://files.pythonhosted.org/packages/44/d5/840565f3378f0bcea060b962087c6c20aedf5b67d00fd2e156878548047e/guild-1.0.3-1.tar.gz" } ], "1.1.3": [ { "comment_text": "", "digests": { "md5": "b327d1892b9c925306cc7a368f6da784", "sha256": "1b0ebcf95c2664106c86c414fded535ab8073e041a554b55d8a324607951121a" }, "downloads": -1, "filename": "guild-1.1.3.tar.gz", "has_sig": false, "md5_digest": "b327d1892b9c925306cc7a368f6da784", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 53243, "upload_time": "2017-03-07T00:55:05", "url": "https://files.pythonhosted.org/packages/27/9f/6fe265a35cf46f2a49731fa1076171f51d0b052c97e95291c93e3943821f/guild-1.1.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b327d1892b9c925306cc7a368f6da784", "sha256": "1b0ebcf95c2664106c86c414fded535ab8073e041a554b55d8a324607951121a" }, "downloads": -1, "filename": "guild-1.1.3.tar.gz", "has_sig": false, "md5_digest": "b327d1892b9c925306cc7a368f6da784", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 53243, "upload_time": "2017-03-07T00:55:05", "url": "https://files.pythonhosted.org/packages/27/9f/6fe265a35cf46f2a49731fa1076171f51d0b052c97e95291c93e3943821f/guild-1.1.3.tar.gz" } ] }