Asked  7 Months ago    Answers:  5   Viewed   59 times

I'm using python's ftplib to write a small FTP client, but some of the functions in the package don't return string output, but print to stdout. I want to redirect stdout to an object which I'll be able to read the output from.

I know stdout can be redirected into any regular file with:

stdout = open("file", "a")

But I prefer a method that doesn't uses the local drive.

I'm looking for something like the BufferedReader in Java that can be used to wrap a buffer into a stream.

 Answers

50
from cStringIO import StringIO # Python3 use: from io import StringIO
import sys

old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()

# blah blah lots of code ...

sys.stdout = old_stdout

# examine mystdout.getvalue()
Tuesday, June 1, 2021
 
TheLovelySausage
answered 7 Months ago
66

Thanks for everyone's answers.

I actually ended up solving the problem without revealing the path to the audio file to the user.

Because this is for an online voice messaging application, it was important to protect the path to the messages in order to protect the user's privacy.

The situation ended up being further complicated by the fact that the messages are stored on a remote server... found this out right after I posted this question.

So all in all the process was:

1) Curl the MP3 file from the remote server

2) Set some headers to force the download of the data, which you then echo to stdout

Because the data is being put on standard out and the headers are set to force a download, the effect for jPlayer is the same as if it had requested a file at /some/dir/message.mp3, except that the request goes to /some/dir/playmessage.php - thus the path is never revealed to the user.

EDIT** - Assume that I have access control, validation running before the snippit below, otherwise exposing the path to the script below is no different than just exposing the path to the mp3. (see comments between me and Lloyd)

Here's the code that ended up getting the job done:

$ch = curl_init($remoteFile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$data = curl_exec($ch);
curl_close($ch);
if ($data === false) {
  echo 'CURL Failed';
  exit;
}

//Get file size
if (preg_match('/Content-Length: (d+)/', $data, $matches)) {
  $contentLength = (int)$matches[1];
}

//force user to download file contents
header('Content-Transfer-Encoding: binary');
header('Content-Type: audio/mpeg');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Content-Length: ' . $contentLength);
ob_clean();
flush();
echo $data;
exit;
?>

Assume that $remoteFile is a path to the mp3 file that I want to play on the server where we are storing messages.

I found this resource on reading a remote file using PHP especially helpful. Here's some more information on modifying http headers.

NOTE I am essentially downloading the file twice, which slows the process down for the user. There is probably a better way to do this. :) If you kept the file locally, what I had initially in my question actually works (using readfile() to output the raw mp3 data).

Saturday, May 29, 2021
 
MassiveAttack
answered 7 Months ago
92

Use the same C runtime that CPython 3.x uses (e.g. msvcr100.dll for 3.3). Also include a call to fflush(NULL) before and after redirecting stdout. For good measure, redirect the Windows StandardOutput handle, in case a program uses the Windows API directly.

This can get complicated if the DLL uses a different C runtime, which has its own set of POSIX file descriptors. That said, it should be OK if it gets loaded after you've redirected Windows StandardOutput.

Edit:

I've modified the example to run in Python 3.5+. VC++ 14's new "Universal CRT" makes it much more difficult to use C standard I/O via ctypes.

import os
import sys
import ctypes, ctypes.util

kernel32 = ctypes.WinDLL('kernel32')

STD_OUTPUT_HANDLE = -11

if sys.version_info < (3, 5):
    libc = ctypes.CDLL(ctypes.util.find_library('c'))
else:
    if hasattr(sys, 'gettotalrefcount'): # debug build
        libc = ctypes.CDLL('ucrtbased')
    else:
        libc = ctypes.CDLL('api-ms-win-crt-stdio-l1-1-0')

    # VC 14.0 doesn't implement printf dynamically, just
    # __stdio_common_vfprintf. This take a va_array arglist,
    # which I won't implement, so I escape format specificiers.

    class _FILE(ctypes.Structure):
        """opaque C FILE type"""

    libc.__acrt_iob_func.restype = ctypes.POINTER(_FILE)    

    def _vprintf(format, arglist_ignored):
        options = ctypes.c_longlong(0) # no legacy behavior
        stdout = libc.__acrt_iob_func(1)
        format = format.replace(b'%%', b'')
        format = format.replace(b'%', b'%%')
        format = format.replace(b'', b'%%')
        arglist = locale = None        
        return libc.__stdio_common_vfprintf(
            options, stdout, format, locale, arglist)

    def _printf(format, *args):
        return _vprintf(format, args)

    libc.vprintf = _vprintf
    libc.printf = _printf
def do_print(label):
    print("%s: python print" % label)
    s = ("%s: libc _writen" % label).encode('ascii')
    libc._write(1, s, len(s))
    s = ("%s: libc printfn" % label).encode('ascii')
    libc.printf(s)
    libc.fflush(None) # flush all C streams

if __name__ == '__main__':
    # save POSIX stdout and Windows StandardOutput
    fd_stdout = os.dup(1)
    hStandardOutput = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)

    do_print("begin")

    # redirect POSIX and Windows
    with open("TEST.TXT", "w") as test:
        os.dup2(test.fileno(), 1)
        kernel32.SetStdHandle(STD_OUTPUT_HANDLE, libc._get_osfhandle(1))

    do_print("redirected")

    # restore POSIX and Windows
    os.dup2(fd_stdout, 1)
    kernel32.SetStdHandle(STD_OUTPUT_HANDLE, hStandardOutput)

    do_print("end")
Sunday, July 4, 2021
 
Blur
answered 5 Months ago
41

You can set a stream and a textwriter directly from c#:

engine.Runtime.IO.SetOutput(stream, txtWriter);
engine.Runtime.IO.SetErrorOutput(stream, txtWriter);

To redirect the output for example you could override TextWriter class with a new one writing on your textbox.


e.g.

in my application I did an override of StreamWriter class that rises events when something is written on the stream (here just a part of the code):

public class MyEvtArgs<T> : EventArgs
{
    public T Value
    {
        get;
        private set;
    }
    public MyEvtArgs(T value)
    {
        this.Value = value;
    }
}


public class EventRaisingStreamWriter : StreamWriter
{
    #region Event
    public event EventHandler<MyEvtArgs<string>> StringWritten;
    #endregion

    #region CTOR
    public EventRaisingStreamWriter(Stream s):base(s)
    { }
    #endregion

    #region Private Methods
    private void LaunchEvent(string txtWritten)
    {
        if (StringWritten != null)
        {
            StringWritten(this, new MyEvtArgs<string>(txtWritten));
        }
    }
    #endregion


    #region Overrides

    public override void Write(string value)
    {
        base.Write(value);
        LaunchEvent(value);
    }
    public override void Write(bool value)
    {
        base.Write(value);
        LaunchEvent(value.ToString());
    }
    // here override all writing methods...

    #endregion
}

Then in your application you should just do something like:

    MemoryStream ms = new MemoryStream();

    EventRaisingStreamWriter outputWr = new EventRaisingStreamWriter(ms);
    outputWr.StringWritten += new EventHandler<MyEvtArgs<string>>(sWr_StringWritten);

    var engine = Python.CreateEngine();
    engine.Runtime.IO.SetOutput(ms, outputWr);
    engine.CreateScriptSourceFromString("print 'hello world!'").Execute();


    void sWr_StringWritten(object sender, MyEvtArgs<string> e)
    {
        textBox1.Text += e.Value;
    }
Monday, July 19, 2021
 
pwaring
answered 5 Months ago
12

Step 1: Firstly go to wp-content/themes/ folder. And then rename your theme folder to whatever you want.

Step 2: Open your theme folder and open style.css file. In top part of style.css you will see theme name. Rename it and save changes.

Step 3: Go to Wp-admin/appearance/themes and activate your theme under new name.

If you are using child/parent theme and you also rename parent theme folder&name, so after Step 3 you should additionally change parent theme path (template field) in child theme’s style.css.

note : renaming your theme will stop its automatic updates, you should do it manually in the future.

Sunday, September 5, 2021
 
hidden_4003
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share