2
  1. i have simple python echo server which uses sockets , and echoes random numbers to clients .
  2. i have another program which prints every 2 second values to stdout.

If it was just a script i could have redirected like this 'stdout' | python script.py , and fetch it in the script like this data = sys.stdin.read(). But it's a server , and it wont work like this.

Question: How to write such a python server, that it could listen/fetch values which come to stdout on Debian, and pass them further. I am not asking for a complete code. Just some ideas on getting stdout, considering the program is a server.

Continuosly coming values > stdout > server.py > send values further

Mike Pennington
  • 41,899
  • 19
  • 136
  • 174
Viktor
  • 580
  • 2
  • 14
  • 29
  • is the other program that prints the values on the machine sending random numbers or is it on a machine listening to the numbers been sent? – tMC Apr 30 '11 at 21:22
  • Right now server.py is sending random numbers , just for test. But i need server.py to continuosly fetch stdout values being sent every 2 seconds. I just made up the other program which for examples generates values and sends them to the server through stdout. – Viktor Apr 30 '11 at 21:27
  • You can read from stdin with `sys.stdin.read()` or like you would any other file handle .readlines() etc – tMC Apr 30 '11 at 21:29

1 Answers1

4

I think you need something similar to Python subprocess. In that example, consider dmesg as your number generator, and the grep as your socket server; however, you will just open a PIPE in the socket server instead of spawning a shell command.

Assume you have two processes, the talker and server (in Python 2.x)...

talker.py

"""
Call as talker.py <INTEGER_DELAY>
"""

import time
import sys

assert sys.version_info < (3, 0, 0)

DELAY = int(sys.argv[1])
ii = 1
while True:
    sys.stdout.write("Talk every %i seconds, blabbed %i times\n" % (DELAY, ii))
    sys.stdout.flush()
    ii += 1
    time.sleep(DELAY)

server.py

#!/usr/bin/env python

from subprocess import Popen, PIPE
from os import kill
import signal
import sys

assert sys.version_info < (3, 0, 0)

talkpipe = Popen(['python', 'talker.py', '3'],
    shell=False, stdout=PIPE)
try:
    while True:
        line = talkpipe.stdout.readline()
        print "SERVER HEARD", line.strip()
except KeyboardInterrupt:
    print "Killing child..."
    kill(talkpipe.pid, signal.SIGTERM)

Running this with ./server.py yields...

[mpenning@Bucksnort tmp]$ python server.py
SERVER HEARD Talk every 3 seconds, blabbed 1 times
SERVER HEARD Talk every 3 seconds, blabbed 2 times
SERVER HEARD Talk every 3 seconds, blabbed 3 times
SERVER HEARD Talk every 3 seconds, blabbed 4 times
^CTraceback (most recent call last):
  File "talker.py", line 11, in <module>
    time.sleep(DELAY)
KeyboardInterrupt
Killing child...
[mpenning@Bucksnort tmp]$

The (more robust) Python3 versions of these scripts are...

talker.py


"""
talker.py: run talker.py with continuous stdout stream and control-C handling.

talker.py was written for Python3, but it should be fine under Python 2.X...
- To run under python 2.x, set `ENFORCE_PYTHON_3 = 0`

Execute with: 'python talker.py <DELAY_INTEGER>'
"""

import signal
import types
import time
import sys
import os

CATCH_CONTROL_C = 1
ENFORCE_PYTHON_3 = 1
BOUNDARY_CONDITIONS_SET = 0

def custom_signal_handler(signum, stackframe):
    assert isinstance(signum, int)
    assert isinstance(stackframe, types.FrameType)
    assert isinstance(BOUNDARY_CONDITIONS_SET, int) and BOUNDARY_CONDITIONS_SET >= 1

    print("---> custom_signal_handler() got signal: %s <---" % signum)
    if signum == 0:
        #  signal.SIG_DFL
        print(repr(stackframe))

    elif signum == 2:
        #  signal.SIGINT
        assert CATCH_CONTROL_C >= 1
        raise KeyboardInterrupt(repr(stackframe))

    else:
        raise NotImplementedError("Cannot handle signal %s" % signal.signum)

def set_boundary_conditions():

    global BOUNDARY_CONDITIONS_SET
    global DELAY_INTEGER

    assert isinstance(BOUNDARY_CONDITIONS_SET, int)
    assert BOUNDARY_CONDITIONS_SET <= 0

    if ENFORCE_PYTHON_3 == 1:
        assert tuple(sys.version_info) >= (3, 0, 0)

    # len(sys.argv) will be 1 if this script is imported into python
    # len(sys.argv) will be >= 2 if this script was called from CLI w/ args
    if len(sys.argv) == 1:
        # Called from an import statement...
        assert sys.argv[0] == ''
        DELAY_INTEGER = 1

    elif len(sys.argv) >= 2:
        # Called from the command-line... sys.argv[0] is the file name...
        assert sys.argv[0] == os.path.basename(__file__).strip()
        print("FOO", sys.argv[0])
        DELAY_INTEGER = int(sys.argv[1])
        assert DELAY_INTEGER > 0

    else:
        raise ValueError("DELAY_INTEGER was not specified correctly in the CLI")

    if CATCH_CONTROL_C >= 1:
        assert custom_signal_handler

        # Send control-C to the custom signal handler...
        signal.signal(signal.SIGINT, custom_signal_handler)
        print("    The current handler for signal.SIGINT is %s" % custom_signal_handler)

    else:
        assert signal.SIG_DFL
        # signal.SIGINT (control-C) is 2...
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        print("The current handler for signal.SIGINT is signal.SIG_DFL")

    BOUNDARY_CONDITIONS_SET = 1


def loop_talking_over_stdout():
    """
    Write messages to stdout with a variable delay...

    This function (loop_talking_over_stdout()) is a blocking call...
    """

    assert BOUNDARY_CONDITIONS_SET >= 1

    ii = 1
    while True:
        sys.stdout.write("Talk every %i seconds, blabbed %i times\n" % (
            DELAY_INTEGER, ii))
        sys.stdout.flush()
        ii += 1
        time.sleep(DELAY_INTEGER)

if __name__=="__main__":
    print("running python %s" % list(sys.version_info))
    set_boundary_conditions()
    loop_talking_over_stdout()

server.py

#!/usr/bin/env python3

from subprocess import Popen, PIPE
from os import kill
import signal
import shlex
import sys
import os

assert sys.version_info >= (3, 0, 0,)

def custom_signal_handler(signum, stackframe):
    assert isinstance(signum, int)

    print("Got signal: %s" % signum)
    raise KeyboardInterrupt(repr(stackframe))

# Send control-C to the custom signal handler...
signal.signal(signal.SIGINT, custom_signal_handler)

TIMEOUT = 3

while True:
    filepath = os.sep.join((os.getcwd(), 'talker.py'))
    cmd = 'python3 %s %s' % (filepath, TIMEOUT)
    talkpipe = Popen(shlex.split(cmd), shell=False,
        stdin=PIPE, stdout=PIPE, stderr=PIPE)

    stdout = talkpipe.stdout
    stderr = talkpipe.stderr
    stdin = talkpipe.stdin


    for line_out in iter(stdout.readline, b''):
        print(b'out>>> ' + line_out.rstrip())

        for line_err in iter(stderr.readline, b''):
            print(b'err-->' + line_err)

Mike Pennington
  • 41,899
  • 19
  • 136
  • 174
  • I have a question . I did the way u suggested. I will show the code above and i got the error , i guess because my server was already running when i tried to pass values through pipe , and it gave error that i didn't specify args or something like that . So the question is : everytime i pass value through pipe it executes the file to what it passed the value or not ? – Viktor Apr 30 '11 at 23:07
  • @Viktor, the description is inadequate to help without knowing the error, the way you called the number generation program with `subprocess`, and the way you attached `stdout` of that program to `stdin` in your socket server. – Mike Pennington Apr 30 '11 at 23:17
  • @Mike Maybe i just messed something with format , because it sends also stderr and stdout whtich are None,None , but as i see it is not pleased with my args[0] , which i have to specify when i run server , why i conclude that it kind of runs server.py and doesn't just pass stdin. File "myslowpoetry.py", line 36, in parse_args poetry_file = args[0] IndexError: list index out of range (None, None) – Viktor Apr 30 '11 at 23:24
  • @Mike Yep . I just checked , the error is exactly because it doesn't pass args[0] , which means that everytime i execute send_random_number.py through pipe , it executes the other file to which pipe is linked , which is completely wrong for my server because it needs to be run ones , and just passed values through PIPE. Shit ....=) – Viktor Apr 30 '11 at 23:42
  • @Viktor, you misunderstood what I was pointing to with the docs... I posted code that illustrates – Mike Pennington May 01 '11 at 02:32
  • @Mike . Thx. Worked like a charm ... I guess i just wrote something wrong the first time =). Thank you very much .. Help appreciated =) – Viktor May 01 '11 at 09:54
  • @Viktor, you're certainly welcome... actually I wrestled with it for a bit until I realized I needed to `flush()` to ensure that I got `stdout` through the PIPE. – Mike Pennington May 01 '11 at 09:58