{"id":25974531,"url":"https://github.com/dwtj/twisteddeferredstutorial","last_synced_at":"2025-03-05T02:31:47.285Z","repository":{"id":20753409,"uuid":"24037921","full_name":"dwtj/TwistedDeferredsTutorial","owner":"dwtj","description":"Introduces Twisted by explaining asynchronous programming and Deferreds.","archived":false,"fork":false,"pushed_at":"2015-01-24T19:12:40.000Z","size":185,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-08-14T10:10:47.037Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dwtj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-15T01:07:13.000Z","updated_at":"2023-08-14T10:10:47.037Z","dependencies_parsed_at":"2022-07-21T08:02:33.193Z","dependency_job_id":null,"html_url":"https://github.com/dwtj/TwistedDeferredsTutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwtj%2FTwistedDeferredsTutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwtj%2FTwistedDeferredsTutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwtj%2FTwistedDeferredsTutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwtj%2FTwistedDeferredsTutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dwtj","download_url":"https://codeload.github.com/dwtj/TwistedDeferredsTutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241953482,"owners_count":20048193,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-05T02:31:46.772Z","updated_at":"2025-03-05T02:31:47.253Z","avatar_url":"https://github.com/dwtj.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Twisted Tutorial\n================\n\nThis tutorial introduces the [Twisted](https://twistedmatrix.com) networking library for Python. We focus on explaining two concepts which any programmer should understand before trying to make or modify any Twisted application: *asynchronous programming* and *Deferreds*.\n\nIf you are already familiar with asynchronous programming (a.k.a. event-driven programming, event-loop programming, reactive programming, the reactor pattern) feel free to jump ahead to [the Twisted-specific stuff](#deferreds).\n\n\n\n\nOther Great Twisted References\n------------------------------\n\nThe [official Twisted documentation](https://twistedmatrix.com/documents/current/core/index.html) can help you get started understanding how Twisted works, but I've found it to be more useful as an API reference. The resources which I've found that best explain how Twisted is meant to be used are:\n\n- [Krondo Twisted Introduction](http://krondo.com/?page_id=1327)\n- [Architecture of Open Source Applications - Twisted](http://www.aosabook.org/en/twisted.html)\n\n\n\n\n\nThe Asynchronous Programming Paradigm\n-------------------------------------\n\n### What It Isn't ###\n\nMy first mistake when coming to Twisted was not understanding the distinction between multi-threaded programming and asynchronous programming. \n\nIn **multi-threaded programming** the programmer gets work done by managing multiple threads of execution running within a single process. Each of these threads is given some time to run according to the whims of the thread scheduler. (Depending on the programming environment, this scheduler may be in userspace or in the kernel.)\n\nWhen programming within the multi-threaded paradigm, the programmer must always imagine the possiblity that program execution could jump from one place in one thread to (almost) any other place in any other thread. This can make the programmer's job rather difficult, because it requires that he or she needs to often worry about locking data or carefully synchronizing state between threads.\n\n\n\n\n### What It Is ###\n\n**Asynchronous programming** is quite different. In this paradigm, the programmer must set up a number of event handlers to be executed in response to appropriate triggering events. Ideally, an event handler is a relatively short-running reaction to some occasionally-occurring event (e.g. a system timer, a network message's arrival, and user input). Events happen every once in a while, or they may not happen at all.\n\nThere is (generally) just one thread of execution which is handling all of these events. Each event is handled in turn, one-by-one. The whole process looks like this:\n\n(1) The event-loop chooses a single event, call it `ev`, to be handled. This `ev` is chosen from the set of recently fired events, each of which is waiting to be handled.\n\n(2) The event-loop calls the event handler for `ev` (possibly with some just-arrived data). This handler is run to completion.\n\n(3) Upon completion, execution returns to the event loop, and the whole process is repeated.\n\nThe key is that in the asynchronous paradigm, the programmer never needs to worry about a scheduler non-deterministically jumping between threads of execution: only one thread is needed to manage a very wide variety of events.\n\nSo, one can think of the asynchronous programmer's job in the following way: he or she must give commands which set up the system to react to events shortly after they occur.\n\n\n\n\n### What's in a Name? ###\n\nThe Twisted literature seems to usually use the term \"asynchronous programming\", but I personally, I find the terms **event-driven programming** and **event-loop programming** to be much more descriptive terms for this programming paradigm.\n\n\n\n\n### What's It Good For? ###\n\nAsynchronous programming is a very appealing alternative to multi-threaded programming when trying to solving certain kinds of problems, in particular, when making [I/O-bound systems](http://en.wikipedia.org/wiki/I/O_bound). It is used very often in both GUI programming and networking programming.\n\n\n\n\n\n\nDeferreds\n---------\n\n[Deferreds](https://twistedmatrix.com/documents/current/core/howto/defer.html) are used by Twisted applications to set up work that needs to be done once an event has occured. Let's get a sense of this abstraction using a few simple examples.\n\n\n### Deferreds can be fired by a system timer. ###\n\nConsider the [`print_later.py`](print_later.py) program:\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.python}\n#!/usr/bin/env python\n\nfrom __future__ import print_function\nfrom twisted.internet import reactor, task\n\ndef print_later(mesg):\n    ''' Prints the given `mesg` after 1 second has passed. '''\n    task.deferLater(reactor, 1, print, mesg)\n\nprint_later(\"The future is now!\")\nreactor.run()\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy calling `deferLater`, we are setting up some (admittedly trivial) work to be done after a time. In this case, we are telling the `reactor` to wait 1 second before it should call the `print` function with `mesg` as its argument. (If you are familiar with Javascript, then this is just like [`setTimeout()`](http://www.w3schools.com/js/js_timing.asp)\n\nThe `reactor` is Twisted's way of implementing the event-loop. It monitors events and executes any callbacks which have been assigned to handle these events. Note that once you call `reactor`, the event-loop takes over. Your program effectively blocks on that line until the reactor shuts down. In our example program, we have not given the reactor any instruction to shut itself down. You can shut it down manually by typing `Ctrl-C`.\n\nWhen I first read about the `reactor`, it gave me the mental image of a nuclear reactor powering a program. But this is wrong metaphor. The `reactor` is just the object which is there to *react* to and handle events as they occur.\n\n\n\n\n### Twisted application code can create and fire its own deferreds. ###\n\nIn the example above, `deferLater` is using a `Deferred` object under the hood in order to perform the callback to `print`. However, this is an extremely limited use of deferreds. In order to understand how they are used more generally, let's rewrite this program as [`print_later_twice.py`](print_later_twice.py) such that we manually construct and fire our deferred.\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.python}\n#!/usr/bin/env python\n\nfrom __future__ import print_function\nfrom twisted.internet import reactor, defer\n\ndef print_and_passthrough(result):\n    print(result)\n    return result\n\ndef print_later_twice(mesg):\n    ''' Prints the given `mesg` twice after 1 second has passed. '''\n    d = defer.Deferred()\n    d.addCallback(print_and_passthrough)\n    d.addCallback(print_and_passthrough)\n    reactor.callLater(1, d.callback, mesg)\n\nprint_later_twice(\"The future is now!\")\nreactor.run()\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nHere we can more clearly see the key momemnts involved in the lifetime of a `Deferred` object:\n\n- **Line 12:** A generic `Deferred` object is constructed.\n- **Lines 13-14:** A sequence of callback functions is added to this `Deferred`'s callback-chain.\n- **Line 15:** The `Deferred` is fired using its `callback()` method with a `result`. (In this case, we are telling `reactor` to fire `Deferred d` with `mesg` after 1 second of waiting.)\n\nWhen the `Deferred` is fired, the `reactor` will run each callback which has been added to the `Deferred`'s callback-chain, one callback after the other. The object with which the `Deferred` was fired will be passed as the `result` of the first callback function. The argument to subsequent callbacks will be the return value of the preceeding callback.\n\nIn our example, our first callback just passes `result` on to the second callback without any changes. However, it can be useful to think of callbacks as stages in a pipeline to incrementally transform an initial input into some output\n\n\n\n\n### Deferreds use callbacks to process input from a blocking source. ###\n\nIn the previous examples, we have demonstrated how to make the reactor create a timer event to fire a deferred. However, in `Twisted` applications the most common kind of event which fires a deferred is the arrival of input from some blocking source. A key example is input arriving from a network connection.\n\nSo, a `Deferred` object is a way of encapsulating\n\n1. a `result` of some process (e.g. network process) that might not be available yet, and\n2. a sequence of functions (i.e. the callback chain) for processing this `result` once it arrives.\n\nIt can be useful to think of the callback chain as a sequence of functions called to *react* to the `result` once it becomes available. When using the Twisted API, is usually the reactor's responsibility to fire a `Deferred` object once a `result` becomes available, and it is usually the client code's responsibility to adds callbacks to specify how to react.\n\nConsider the [`blocking_input.py`](blocking_input.py) program. It registers with the reactor a number of web pages to be downloaded. Then when a particular web page has been downloaded, the reactor will fire the corresponding `Deferred` object, calling each of the callbacks on its callback chain.\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.python}\n#!/usr/bin/env python\n\nfrom __future__ import print_function\nfrom twisted.internet import reactor, defer\nfrom twisted.web import client\n\nurls = [ 'http://www.google.com'\n       , 'http://www.twitter.com'\n       , 'http://www.facebook.com'\n       , 'http://www.apple.com'\n       , 'http://www.oracle.com'\n       , 'a_bad_url' ]\n\npages = {}\n\ndef request_page(url):\n\n    # Create three closures and make them callbacks/errbacks for handling\n    # the `getPage` request.\n\n    def save_result(result):\n        pages[url] = result\n        return result\n\n    def report_success(result):\n        print('Successfully downloaded `{}`.'.format(url))\n        return result\n\n    def report_failure(failure):\n        print('Failed to download `{}`.'.format(url))\n        return None\n\n    d = client.getPage(url)\n    d.addCallbacks(report_success, report_failure)\n    d.addCallback(save_result)\n\nfor url in urls:\n    request_page(url)\n\nreactor.run()\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDownloading a web page is a blocking I/O operation: it can take a relatively long time time to complete. There is only one thread running, but we don't ever want our program to be idle if there is other work to be done. The key to obtaining asynchronous (i.e. event-driven) behavior is the `Deferred` object returned by calling `getPage()`.\n\nThe first time that I ran this program, it printed the following:\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nFailed to download `a_bad_url`.\nSuccessfully downloaded `http://www.apple.com`.\nSuccessfully downloaded `http://www.oracle.com`.\nSuccessfully downloaded `http://www.google.com`.\nSuccessfully downloaded `http://www.facebook.com`.\nSuccessfully downloaded `http://www.twitter.com`.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNotice that the order in which we made the requests is not the order in which we made the `getPage()` requests: `result`s are not processed synchronously. Rather, the order in which processing occurs depends upon\n\n- the order in which each download completes, and\n- the whims of the `reactor`.\n\nSo, when we call `getPage()`, the thread of execution which makes the request does not block on the request while downloading. Instead, a `Deferred` is returned immediately. It is with this `Deferred` object that our program will later obtain the page itself. The API says that the reactor will fire this `Deferred` with the downloaded page as its `result` when the download is complete.\n\nIn the example above, the first callback added to each `Deferred` is the `report_success()` closure function; the second callback is the `save_result()` closure function. (`report_failure()`, on the other hand, is an `errback`. In this case, `report_failure()` is what is called instead of `report_success()` when the page could not be downloaded. This is what happens in the case of our request to `a_bad_url`.)\n\nSo, once a page is successfully downloaded, the associated `Deferred` fires, and that `Deferred`'s `report_success()` prints a message with the associated `url`. Immediately after this, the `save_result()` callback is called, which saves the page into a shared data structure. Then, since the callback chain is complete, execution returns to the reactor, where another ready task can be selected and executed.\n\nNotice that modifying this global data structure with these multiple callback chains cannot possibly possibly cause race condition here, because the reactions to events are not happening simultaneously. The pages are processed one-by-one by a single thread. Here we see Twisted is using the asynchronous programming paradigm instead of the multi-threaded programming paradigm.\n\n\n\n## What's Next ##\n\nTo learn how to use Twisted there are still a number of essential concepts to learn about. In particular, I would recommend researching:\n\n- the `Deferred` object's callback chain, in particular, specifics about `errbacks` which were only briefly mentioned here,\n- the `Protocol` class, and\n- the `ProtocolFactory` class.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwtj%2Ftwisteddeferredstutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdwtj%2Ftwisteddeferredstutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwtj%2Ftwisteddeferredstutorial/lists"}