GithubHelp home page GithubHelp logo

twisted / tubes Goto Github PK

View Code? Open in Web Editor NEW
55.0 17.0 22.0 963 KB

A series of tubes.

Home Page: https://twisted.github.io/tubes/

License: Other

Python 99.18% Shell 0.82%
python twisted async backpressure network networking

tubes's Introduction

Tubes

Latest Version Latest Docs https://travis-ci.org/twisted/tubes.svg?branch=master

"Tubes" is a data-processing and flow-control engine for event-driven programs.

Presently based primarily on Twisted, its core data structures are fairly framework-agnostic and could be repurposed to work with any event-driven container.

tubes's People

Contributors

ambv avatar ashfall avatar damola12345 avatar glyph avatar graingert avatar hawkowl avatar jmyles avatar mgorny avatar ralphm avatar tomprince avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tubes's Issues

Add strToBytes and bytesToStr tubes

It seems likely to be useful to have some tubes that encode and decode strs.

(Mostly because of #67, otherwise it'd be a 1-liner to inline create such a thing)

Add a helper for connecting a series

It seems a bit tedious (and procedural) to need to write

def connectStuff(flow):
    flow.fount.flowTo(stuff).flowTo(flow.fount.drain)

Specifically if I already have a series and just want to listen and flow through that series, rather than just passing that series in somewhere (to a Listener?) I have to manually rig it up as above. Is this not common enough to automate?

(If such a thing already exists maybe this is another doc request)

Put links to the source definitions from the api docs, just like in twisted's docs

I've found the direct links to the source code in twisted to be an invaluable aid to understanding, well, just about everything. What the doc is talking about, what implications of the docs are actually true, to say nothing of a ready-made example of what I need to do if I need something somewhat different than what's available.

I suspect, for instance, that I only managed to figure out how to build my conch-based call-in support monster because of the quick linking. (If said monster is unpalatable, forget I said anything :p)

While the source is obviously sitting right near-by my code in my properly configured virtualenv, it's still a bit of a nontrivial half-second-to-three-second speed bump to find my place, and leaves open the possibility of looking at a similarly named but very different thing, with the associated confusion caused thereby. I don't know of any troublesome examples of that in tubes yet, but I hope you agree it's only a matter of time.

Should tubes have gimmicky syntax?

tubes.tube.series seems likely to be pretty common.

If you get pretty drunk, does it seem like maybe something like tube > tube > tube do series(tube, tube, tube)?

detect and error on flow control cycles

If you construct a fan.In that feeds a fan.Out which in turn feeds that same fan.In (even if this is indirected through a number of intermediary Tubes) it will create a flow graph that will be permanently paused, never passing any traffic. This is an inscrutable failure mode. It would be nice to see immediately that you have set up a cycle and get an exception.

It might be interesting to implement this in terms of a general-purpose interface for getting topology information from a Fount or Drain (maybe a Node interface with two interfaces: "downstreams" and "upstreams"). This sort of interface could also be used for visualizations (graphviz here we come)

Intro docs: WTF is "flow"

In the intro docs, under "Getting Connected: an Echo Server", after the example, which looks like this:

def echoFlow(flow):
    flow.fount.flowTo(flow.drain)

the next paragraph makes the outrageous claim that "In the above example, echoFlow takes two things". IT TAKES ONE THING, or I'm insane (I'm going to assume the former).

I get stuck here wondering if I can't count, and then when I accept that I'm simply being misled, I'm left wondering the degree to which this evil plot to confuse me is trying to drive me nuts, which them makes me wonder if perhaps I'm already nuts, and I should have assumed the latter.

WTF is flow? Is it a type I should know about? Or some random thingo with these two attributes attached to it? OMG I DON'T KNOW! And then it says here that the function is a "flow". WHAT?!? Why is the argument called the thing that the function is?

Anyway, I feel like I'd be feeling a lot less stress if this function actually took two arguments:

def echoFlow(fount, drain):
    fount.flowTo(drain)

Then you can say it takes two thing, and even call the function a flow and I'd be cool with all of that.

Peace be unto you.

Can a function be decorated "tube"? Or only a class?

From the docstring on tube:

    """
    L{tube} is a class decorator which declares a given class to be an
    implementer of L{ITube} and fills out any methods or attributes which are
    not present on the decorated type with null-implementation methods (those
    which return None) and None attributes.

    @param cls: A class with some or all of the attributes or methods described
        by L{ITube}.

However, the readme shows:

@tube
def numbersToLines(value):
    yield str(value).encode("ascii")

Indeed, when I try to decorate a function, I get:

zope.interface.exceptions.BrokenMethodImplementation: The implementation of started violates its because implementation requires too many arguments.     

Intro doc appears to suggest that @tube accepts callables. It appears not to

The intro doc does things like

@tube
def foo(item):
    yield item

which presumably would create a simple tube using that function as the received, but that does not actually appear to be a thing tubes.tube.tube does (and doing so just complains that the resulting object does not have a started attribute when you hook it up to a flow).

Introduction Doc Examples are Incomplete / Possibly Non-functional

(Carried over from IRC to an actual bug tracker :/)

The API doc links from e.g. https://tubes.readthedocs.org/en/latest/tube.html#an-introduction-to-tubes point at twistedmatrix.com when they seem to want to point at twisted.github.io now.

Also, the second example (with a Reverser) seems incomplete, or at least, reading through it, I cannot yet tell how to make it work.

If I put

from tubes.protocol import factoryFromFlow

from twisted.internet.endpoints import serverFromString
from twisted.internet.defer import Deferred

def reverseFlow(fount, drain):
    from tubes.framing import bytesToLines, linesToBytes
    lineReverser = series(bytesToLines(), Reverser(), linesToBytes())
    fount.flowTo(lineReverser).flowTo(drain)

class Reverser(object):
    def received(self, item):
        yield b"".join(reversed(item))

def main(reactor, listenOn="stdio:"):
    endpoint = serverFromString(reactor, listenOn)
    endpoint.listen(factoryFromFlow(reverseFlow))
    return Deferred()

if __name__ == '__main__':
    from twisted.internet.task import react
    from sys import argv
    react(main, argv[1:])

in a file and run it, I get a thing that silently does nothing, seemingly.

There's possibly a mix of an upstream issue here, but after some quick debugging:

if I stop listening on stdio, now I get tracebacks on stdout when I make a connection:

/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/endpoints.py:29: DeprecationWarning: twisted.internet.interfaces.IStreamClientEndpointStringParser was deprecated in Twisted 14.0.0: This interface has been superseded by IStreamClientEndpointStringParserWithReactor.
  from twisted.internet.interfaces import (
2015-11-19T14:51:22-0500 [-] Factory starting on 1234
2015-11-19T14:51:22-0500 [twisted.internet.protocol.Factory#info] Starting factory <twisted.internet.protocol.Factory instance at 0x0000000108c6c720>
asdf
Unhandled Error
Traceback (most recent call last):
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/python/log.py", line 84, in callWithContext
    return context.call({ILogContext: newCtx}, func, *args, **kw)
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/python/context.py", line 118, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/python/context.py", line 81, in callWithContext
    return func(*args,**kw)
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/selectreactor.py", line 149, in _doReadOrWrite
    why = getattr(selectable, method)()
--- <exception caught here> ---
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/tcp.py", line 1074, in doRead
    protocol.makeConnection(transport)
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/protocol.py", line 494, in makeConnection
    self.connectionMade()
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/tubes/protocol.py", line 231, in connectionMade
    self._flow(self._fount, self._drain)
  File "send.py", line 23, in reverseFlow
    lineReverser = series(bytesToLines(), Reverser(), linesToBytes())
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/tubes/tube.py", line 220, in series
    drains = map(IDrain, tubes)
  File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/zope/interface/interface.py", line 142, in __call__
    raise TypeError("Could not adapt", obj, self)
exceptions.TypeError: ('Could not adapt', <__main__.Reverser object at 0x0000000108c59fa0>, <InterfaceClass tubes.itube.IDrain>)

2015-11-19T14:51:27-0500 [twisted.internet.protocol.Factory] Unhandled Error
        Traceback (most recent call last):
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/python/log.py", line 84, in callWithContext
            return context.call({ILogContext: newCtx}, func, *args, **kw)
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/python/context.py", line 118, in callWithContext
            return self.currentContext().callWithContext(ctx, func, *args, **kw)
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/python/context.py", line 81, in callWithContext
            return func(*args,**kw)
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/selectreactor.py", line 149, in _doReadOrWrite
            why = getattr(selectable, method)()
        --- <exception caught here> ---
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/tcp.py", line 1074, in doRead
            protocol.makeConnection(transport)
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/protocol.py", line 494, in makeConnection
            self.connectionMade()
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/tubes/protocol.py", line 231, in connectionMade
            self._flow(self._fount, self._drain)
          File "send.py", line 23, in reverseFlow
            lineReverser = series(bytesToLines(), Reverser(), linesToBytes())
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/tubes/tube.py", line 220, in series
            drains = map(IDrain, tubes)
          File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/zope/interface/interface.py", line 142, in __call__
            raise TypeError("Could not adapt", obj, self)
        exceptions.TypeError: ('Could not adapt', <__main__.Reverser object at 0x0000000108c59fa0>, <InterfaceClass tubes.itube.IDrain>)

which seems to indicate that that isn't in fact enough to get a working IDrain?

It's also really off-putting I think to new users if tracebacks are completely silenced there. That was my first 5 minutes with Tubes unfortunately and I had literally no idea what was going wrong, and also there were 2 logging frameworks to disect to figure out where my tracebacks went (along the way I discovered that if I added both:

globalLogPublisher.addObserver(textFileLogObserver(sys.stderr))

and

log.startLogging(sys.stderr)

to try and get both frameworks to not swallow anything, I get instead an infinite stream of recursion on stderr which I think is the two of them fighting with each other:

# ... truncated
2015-11-19 14:58:12-0500 [-]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/defer.py", line 121, in execute
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/defer.py", line 121, in execute
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/defer.py", line 121, in execute
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/defer.py", line 121, in execute
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/defer.py", line 121, in execute
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/defer.py", line 121, in execute
2015-11-19 14:58:12-0500 [-] UNFORMATTABLE OBJECT WRITTEN TO LOG with fmt '%(log_legacy)s', MESSAGE LOST
2015-11-19 14:58:12-0500 [-]     result = callable(*args, **kw)
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error]     result = callable(*args, **kw)
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]     result = callable(*args, **kw)
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]     result = callable(*args, **kw)
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]     result = callable(*args, **kw)
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]     result = callable(*args, **kw)
2015-11-19 14:58:12-0500 [-] UNFORMATTABLE OBJECT WRITTEN TO LOG with fmt '%(log_legacy)s', MESSAGE LOST
2015-11-19 14:58:12-0500 [-]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/posixbase.py", line 478, in listenTCP
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/posixbase.py", line 478, in listenTCP
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/posixbase.py", line 478, in listenTCP
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/posixbase.py", line 478, in listenTCP
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/posixbase.py", line 478, in listenTCP
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]   File "/Users/Julian/.local/share/virtualenvs/kafka_faf/site-packages/twisted/internet/posixbase.py", line 478, in listenTCP
2015-11-19 14:58:12-0500 [-] UNFORMATTABLE OBJECT WRITTEN TO LOG with fmt '%(log_legacy)s', MESSAGE LOST
2015-11-19 14:58:12-0500 [-]     p.startListening()
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error]     p.startListening()
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]     p.startListening()
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]     p.startListening()
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]     p.startListening()
2015-11-19 14:58:12-0500 [-] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error] 2015-11-19T14:58:12-0500 [stderr#error]     p.startListening()
# ... truncated

-- it might be that not having tracebacks shown is really an upstream issue which is "maybe tracebacks should be going to stderr when listening on a stdio endpoint? or something?"

Sorry to file such a tangled issue report, as I learn a bit more about tubes over the next hour I'll hopefully be able to untangle, but that was a brain dump of the first 10 minutes or so.

strports for protocols?

(I remember somewhere this idea being brought up but don't remember where that was)

It seems like it might be nice to -- just like endpoints do so for listening+connecting -- have injectable ways of describing what framing is being used on those endpoints, whether it belongs in tubes or otherwise.

E.g., if I decide to run a server using line-delimited messages for testing, I'd like to write something like line:(stdio:):line to get a series that does bytesToLines -> something -> linesToBytes and then maybe later when I go to prod decide I want int32netstring:(stdio:):line because now there's a server on the other end.

Thoughts?

Where do the exceptions go?

exctube.txt

I would expect flowStopped to be called on each participant in the flow. It seems that stopped is called on the tube with the buggy received method but the exception doesn't appear to be propagated beyond that.

backpressure policy drain/fount for "Drop"

Sometimes, when encountering backpressure – assuming that the output type of the given fount is a discrete message and not part of a continuous stream like IFragment – the thing the fount actually wants to do instead of delivering things is to drop them. For example, in the case of a fan.Out keeping a bunch of observers up to date about the current state of something, you may want to allow some of those observers to just not see messages while their respective buffers are full.

See #35

replace conversion of flow-function → factory with conversion of endpoint → fount of founts

Presently on trunk, tubes are hooked up to the outside world (i.e.: Twisted) by converting a "flow function", a function taking a fount and drain, to a Factory, which is then hooked up to Twisted.

However, this is limiting in one major regard: the backpressure of not accepting future connections is impossible to model properly.

A better way to conceptualize a listening socket is a fount whose outputType is an object with both Fount and Drain attributes. This way, we can flow the stream of incoming connections to a "listening" drain, which can exert backpressure (most obviously by simply limiting the number of concurrently active connections).

As it happens, this is also a feature missing from Twisted which makes it hard to implement fairness in queueing. By making tubes treat listening sockets and connected sockets consistently, we can open up a whole new area of correct-by-default behavior under high levels of load.

Add IOFount

Add an IOFount class that makes a fount from a readable FLO.

The code below works, but it's doing a blocking read on the data, and should be streaming instead.

@implementer(IFount)
@attrs(frozen=False)
class IOFount(object):
    """
    Fount that reads from a file-like-object.
    """

    outputType = ISegment

    _source = attrib()  # type: BinaryIO

    drain = attrib(
        validator=optional(provides(IDrain)), default=None, init=False
    )  # type: IDrain
    _paused = attrib(validator=instance_of(bool), default=False, init=False)


    def __attrs_post_init__(self):
        # type: () -> None
        self._pauser = Pauser(self._pause, self._resume)


    def _flowToDrain(self):
        # type: () -> None
        if self.drain is not None and not self._paused:
            data = self._source.read()
            if data:
                self.drain.receive(data)
            self.drain.flowStopped(Failure(StopIteration()))


    # FIXME: this should stream.
    def flowTo(self, drain):
        # type: (IDrain) -> IFount
        result = beginFlowingTo(self, drain)
        self._flowToDrain()
        return result


    def pauseFlow(self):
        # type: () -> None
        return self._pauser.pause()


    def stopFlow(self):
        # type: () -> None
        return self._pauser.resume()


    def _pause(self):
        # type: () -> None
        self._paused = True


    def _resume(self):
        # type: () -> None
        self._paused = False
        self._flowToDrain()

NoneType object has no attribute pause

I have no explicit pause / unpause logic in my code but I reliably get this after the first value dribbles through the tube:

2017-05-02T13:49:41-0400 [twisted.internet.defer#critical] 
	Traceback (most recent call last):
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 651, in _runCallbacks
	    current.result = callback(current.result, *args, **kw)
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 1030, in _cbDeferred
	    self.callback(self.resultList)
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 457, in callback
	    self._startRunCallbacks(result)
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 565, in _startRunCallbacks
	    self._runCallbacks()
	--- <exception caught here> ---
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 651, in _runCallbacks
	    current.result = callback(current.result, *args, **kw)
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/tubes/undefer.py", line 28, in done
	    pause.unpause()
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/tubes/kit.py", line 39, in unpause
	    self._friendPauser._actuallyResume()
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/tubes/_siphon.py", line 184, in _actuallyResume
	    fp.unpause()
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/tubes/kit.py", line 39, in unpause
	    self._friendPauser._actuallyResume()
	  File "/home/exarkun/Environments/kubetop/local/lib/python2.7/site-packages/tubes/_siphon.py", line 184, in _actuallyResume
	    fp.unpause()
	exceptions.AttributeError: 'NoneType' object has no attribute 'unpause'

tubes Router is broken

Besides the broken docstring for the Router as reported in #21 the Router also generally appears not to work; or am I missing something simple here?

see my attempt at creating unit tests for Router:
https://github.com/david415/tubes/tree/add_routing_tests.0
https://github.com/david415/tubes/blob/add_routing_tests.0/tubes/test/test_routing.py

When the Router's newRoute() method is called this results in:

$ trial tubes.test.test_routing
tubes.test.test_routing
  TestBasicRouter
    test_basic_router ...                                               [ERROR]

===============================================================================
[ERROR]
Traceback (most recent call last):
  File "/home/user/tubes/tubes/test/test_routing.py", line 64, in test_basic_router
    evenOddTube.addRoutes()
  File "/home/user/tubes/tubes/test/test_routing.py", line 41, in addRoutes
    self.evenRoute = self.newRoute()
  File "/home/user/tubes/tubes/routing.py", line 179, in newRoute
    fount = self._out.newFount().flowTo(received)
  File "/home/user/tubes/tubes/fan.py", line 253, in flowTo
    return beginFlowingTo(self, drain)
  File "/home/user/tubes/tubes/kit.py", line 104, in beginFlowingTo
    return drain.flowingFrom(fount)
exceptions.AttributeError: '_Tubule' object has no attribute 'flowingFrom'

tubes.test.test_routing.TestBasicRouter.test_basic_router
-------------------------------------------------------------------------------
Ran 1 tests in 0.018s

FAILED (errors=1)

backpressure policy drain/fount for "Stop"

Sometimes, when a fount experiences backpressure, the right thing to do is to just give up immediately. For example, in a pub/sub service using fan.Out, if one of the drains is falling behind and tells its fount to pauseFlow, we may instead want to simply stopFlow.

Accidentally swapping bytesToLines and linesToBytes produces terrible error message

If you do the above, which I assume everyone will do at one point or another, you get the horrid:

Traceback (most recent call last):
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/julian/Development/pkg/pkg/_cli.py", line 33, in crawl
    react(_crawl, [endpoint])
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/twisted/internet/task.py", line 908, in react
    finished = main(_reactor, *argv)
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/twisted/internet/defer.py", line 1532, in unwindGenerator
    return _inlineCallbacks(None, gen, Deferred())
--- <exception caught here> ---
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/twisted/internet/defer.py", line 1386, in _inlineCallbacks
    result = g.send(result)
  File "/home/julian/Development/pkg/pkg/_cli.py", line 40, in _crawl
    fount.flowTo(Listener(crawlerFlow))
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/tubes/protocol.py", line 353, in flowTo
    self._aFlowFunction(f, d)
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/tubes/protocol.py", line 411, in aFlowFunction
    listening.impl.drain.receive(Flow(fount, drain))
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/tubes/listening.py", line 93, in receive
    item.drain))
  File "/home/julian/Development/pkg/pkg/_cli.py", line 53, in crawlerFlow
    flow.fount.flowTo(crawler).flowTo(flow.drain)
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/tubes/_siphon.py", line 215, in flowTo
    result = beginFlowingTo(self, drain)
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/tubes/kit.py", line 104, in beginFlowingTo
    return drain.flowingFrom(fount)
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/tubes/protocol.py", line 107, in flowingFrom
    beginFlowingFrom(self, fount)
  File "/home/julian/.local/share/virtualenvs/pkg/site-packages/tubes/kit.py", line 128, in beginFlowingFrom
    fount=fount, drain=drain))
exceptions.TypeError: the output of <Fount for <tubes.framing._CarriageReturnRemover object at 0x0000000001a24e90>>, <InterfaceClass tubes.itube.IFrame>, is not compatible with the required input type of <tubes.protocol._TransportDrain object at 0x0000000001a24ec8>, <InterfaceClass tubes.itube.ISegment>

which is pretty hard to decypher.

Add fountToBytes, bytesToFount

Add fountToBytes, bytesToFount functions:

def fountToBytes(fount):
    # type: (IFount) -> Deferred[bytes]
    def collect(chunks):
        # type: (Iterable[bytes]) -> bytes
        return b"".join(chunks)

    d = fountToDeferred(fount)
    d.addCallback(collect)
    return d


def bytesToFount(data):
    # type: (bytes) -> IFount
    …

These are at least useful in testing, so that one can create a fount with some data that can can be fed into a tube, or get out the data pouring out of a tube.

Why is tube a decorator

The comment in tubes.tube.tube goes into a bit of detail on why it's better than subclassing. That seems great!

But why's it a decorator, instead of just being a function:

tube(
    started=lambda self: None,
    stopped=lambda self: None,
    received=lambda self: None,
    inputType=None,
    outputType=None,
) -> class implementing ITube

(This would also satisfy #67)

fix flowFountFromEndpoint pause propagation

Greetings,

tldr;

exceptions.AttributeError: _ProtocolPlumbing instance has no attribute 'pauseProducing'

We seem to be exercising a bug in flowFountFromEndpoint with our tubes coroutine pipeline and unit test here:

https://github.com/david415/bananaphone/tree/add-tubes.0
https://github.com/david415/bananaphone/blob/56c871e697c9050e85cf4d9fa8df059deb6f4a0a/bananaphone.py#L901-L934
https://github.com/david415/bananaphone/blob/56c871e697c9050e85cf4d9fa8df059deb6f4a0a/test/test_utils.py#L17-L33

(virtenv-tubes)user@python-dev1:~/bananaphone$ trial test/test_utils.py
test_utils
  CoCoTubeDrainTests
    test_drain_changeWordSize ... coroutine drain flowing from
coroutine fount flow to
coroutine drain receive
coroutine drain receive
                                         [OK]
  TubeEndpointProxyTests
    test_tube_proxy ... args
['markov', '/usr/share/dict/words', 'words', 'sha1', 2]
                                                [ERROR]

===============================================================================
[ERROR]
Traceback (most recent call last):
  File "/home/user/virtenv-tubes/local/lib/python2.7/site-packages/tubes/protocol.py", line 400, in listening
    listening.impl = _FountImpl(portObject, aFlowFunction, preListen)
  File "/home/user/virtenv-tubes/local/lib/python2.7/site-packages/tubes/protocol.py", line 331, in __init__
    self._pauser = Pauser(portObject.pauseProducing,
exceptions.AttributeError: _ProtocolPlumbing instance has no attribute 'pauseProducing'

test_utils.TubeEndpointProxyTests.test_tube_proxy
-------------------------------------------------------------------------------
Ran 2 tests in 0.035s

FAILED (errors=1, successes=1)

fanchat.py totally doesn't work at all

Actually running sketches/fanchat.py produces confusingly broken behavior. I haven't conclusively traced everything to a single source, but I will enumerate some problems related to its dysfunction here:

  • fan.In doesn't update outstanding aggregate pauses when drains are added or removed
  • fan.Out doesn't update its list of drains even when a drain has told its fount to stopFlow
  • some of the commands echo back to the client when they shouldn't
  • the channels defaultdict can't construct Channel objects because the constructor takes an argument
  • _TransportDrain.flowingFrom breaks when self.fount is not None
  • python fanchat.py and then ^C results in a traceback since _OutFount.stopFlow is not idempotent

and probably others

backpressure policy drain/fount for "Roll"

Sometimes when a drain starts experiencing backpressure, it wants its fount to roll up all the messages that can't be delivered into a single message, compressing them. For example, an IRC consumer might want to batch together N messages into a message that says "missed 10 messages due to slow connection".

replace Failure with Exception or something else as "reason" for flowStopped.

Failure is a big, messy object and it would be good to use it only if we absolutely need its functionality. We don't. flowStopped needs to know how the flow was ended. If the flow was ended due to an application exception, said exception should be unconditionally logged. Flows may end for a variety of reasons which are not exceptions though. It may be worthwhile to have a specific Reason type, or we may just want to go with an Exception instance.

_ProtocolPlumbing does not implement IHalfCloseableProtocol, leading to the misleading appearance of horrifying race conditions during testing

If you were to make a script of JSON commands for fanchat and use the stdio endpoint to hook them together into a little test, then do something like

$ cat fcscript.txt | python fanchat.py

you would get very weird non-deterministic behavior where half of the output would vanish.

This is because, in the absence of a protocol providing IHalfCloseableProtocol as the protocol, "EOF" means "shut down both read and write halves of the connection".

Understandably, further writes to the StandardIO would be lost.

To remedy this, IHalfCloseableProtocol should be implemented - which, by the way, would also allow us to separate the fount and drain termination on transports which support such a distinction.

set up pyflakes

we should run pyflakes over the codebase in an automatic fashion

add parallelism to undefer.py

It should be able to consume N Deferreds at a time and add callbacks to all of them. Perhaps even allow them to come back out of order?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.