File: programming-guide.rst

package info (click to toggle)
python-txaio 25.6.1-2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 528 kB
  • sloc: python: 2,464; makefile: 226; sh: 48
file content (130 lines) | stat: -rw-r--r-- 5,410 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
Programming Guide
=================

This section is a work in progress and suggestions are welcome.


Explict Event Loops
-------------------

Twisted has a single, global reactor (for now). As such, txaio was built with a single, global (but configurable) event-loop. However, asyncio supports multiple event-loops.

After version 2.7.0 it is possible to use txaio with multiple event-loops, and thereby offer asyncio users the chance to pass one. Of course, it's still not possible to use multiple event-loops at once with Twisted.

To start using multiple event-loops with txaio, use `txaio.with_config` to return a new "instance" of the txaio API with the given config (the only thing you can configure currently is the event-loop). On Twisted, it's an error if you try to use a different reactor.

The object returned by `txaio.with_config` is a drop-in replacement for every `txaio.*` call, so you can go from code like this::

    import txaio
    f = txaio.create_future()

...and instead make your code do look like this::

    import asyncio
    import txaio
    txa = txaio.with_config(loop=asyncio.new_event_loop())
    f = txa.create_future()

If you're doing this inside a class, you could use ``self._txa`` or similar instead. This gives you an easy path to opt-in to this multiple event-loop API:

   - replace all ``txaio.*`` calls to use an object, like ``self._txa``.

   - assign this to the txaio module (``self._txa = txaio``) or use
     the new API right away (``self._txa = txaio.with_config()``)

   - add a public API to your library to pass in an event loop

   - when this is used, you set ``self._txa = txaio.with_config(loop=loop)``

See the example in ``examples/multiloop.py``.


Logging
-------

If you are developing a new application, you can take advantage of more structured logging by using txaio's APIs throughout. This API is similar to `Twisted's logging <https://twistedmatrix.com/documents/current/core/howto/logger.html>`_ in many ways, but not identical. If you're integrating txaio into existing code, it should "play nicely" with the ``logging`` module, Twisted's newest logger, and the pre-15.2.0 "legacy" Twisted logger.

To create an object suitable for logging, call :func:`txaio.make_logger`. This will return an instance which has a series of methods indicating the "severity" or "level" of the log -- see :class:`txaio.interfaces.ILogger` for an example and more details.

So, given some code like::

    import txaio
    txaio.use_twisted()

    class Bunny(object):
        log = txaio.make_logger()

        def hop(self, times=1):
            self.log.trace("Bunny.hop(times={times})", times=times)
            self.log.debug("Hopping {times} times.", times=times)
            try:
                1 / 0
            except Exception:
                fail = txaio.create_failure()
                self.log.critical(txaio.failure_format_traceback(fail))

    print("output before start_logging")
    txaio.start_logging(level='debug')
    print("output after start_logging")
    jack = Bunny()
    jack.hop(42)

Then you should see output approximately like this::

    output before start_logging
    2016-01-21T01:02:03-0100 output after start_logging
    2016-01-21T01:02:03-0100 Hopping 42 times.
    2016-01-21T01:02:03-0100 Traceback (most recent call last):
      File "logging-example.py", line 21, in <module>
        jack.hop(42)
    --- <exception caught here> ---
      File "logging-example.py", line 12, in hop
        raise RuntimeError("Fox spotted!")
    exceptions.RuntimeError: Fox spotted!


Note that the ``trace``-level message wasn't logged. If you don't like to see full tracebacks except with debugging, you can use this idiom::

    self.log.critical(txaio.failure_message(fail))
    self.log.debug(txaio.failure_format_traceback(fail))

It's worth noting the code doesn't change at all if you do ``.use_asyncio()`` at the top instead -- of course this is the whole point of ``txaio``!


Logging Interoperability
------------------------

When you're using libraries that are already doing logging, but not using the ``txaio`` APIs, you shouldn't need to do anything. For example::

    import txaio
    txaio.use_twisted()


    def existing_code():
        from twisted.python import log
        log.msg("A legacy Twisted logger message")

    txaio.start_logging(level='debug')
    existing_code()

If you're using ``asyncio`` (or just built-in Python logging), it could look like this::

    import txaio
    txaio.use_asyncio()


    def existing_code():
        import logging
        log = logging.getLogger("roy")
        log.info("Python stdlib message: %s", "txaio was here")

    txaio.start_logging(level='debug')
    existing_code()


Starting Logging Yourself
-------------------------

If you are already starting your favourite logging system yourself (be that Twiste'd logger via ``globalLogBeginner`` or Python stdlib logging), any library using txaio's logging should play nicely with it. **Not** ever calling `txaio.start_logging` has a slight drawback, however: as part of setting up logging, we re-bind all the "unused" logging methods to do-nothing. For example, if the log level is set to ``'info'`` than the ``.debug`` method on all txaio-created logger instances becomes a no-op.

For fully-worked examples of this, look in ``examples/log_interop_stdlib.py`` and ``examples/log_interop_twisted.py``.