Search code examples
pythongitcurses

Python Curses Git Subprocess Output with unexpected spacing


I am observing a weird spacing issue when I call git within a python-curses screen. What am I doing in my minimal working example below that causes the spacing to be ajar and not flush with the side of the screen?

Minimal Working Example:

import curses, subprocess

class MyApp(object):

    def __init__(self, stdscreen):
        self.screen = stdscreen
        self.screen.addstr("Loading..." + '\n')
        self.screen.refresh()

        url = 'http://github.com/octocat/Hello-World/'

        process = subprocess.Popen(['git', 'clone', url], stdout=subprocess.PIPE)

        self.screen.addstr("Press any key to continue.")

        self.screen.getch()

if __name__ == '__main__':
    curses.wrapper(MyApp)

Output:

Loading...
Press any key to continue.Cloning into 'Hello-World'...
                                                       warning: redirecting to https://github.com/octocat/Hello-World/
              remote: Enumerating objects: 13, done.
                                                    remote: Total 13 (delta 0), reused 0 (delta 0), pack-reused 13
Unpacking objects: 100% (13/13), done.3)

Expected Output:

Loading...
Cloning into 'Hello-World'...
warning: redirecting to https://github.com/octocat/Hello-World/
remote: Enumerating objects: 13, done.
remote: Total 13 (delta 0), reused 0 (delta 0), pack-reused 13
Unpacking objects: 100% (13/13), done.3)
Press any key to continue.

Solution

  • git clone writes its informative messages to stderr, not stdout.

    From the documentation (emphasis added):

    --verbose

    Run verbosely. Does not affect the reporting of progress status to the standard error stream.


    (the code below is by Justapigeon)

    Updated MWE with this consideration:

    import curses, subprocess
    import os, re
    
    class MyApp(object):
    
        def __init__(self, stdscreen):
            self.screen = stdscreen
            self.screen.addstr("Loading..." + '\n')
            self.screen.refresh()
    
            url = 'http://github.com/octocat/Hello-World/'
    
            #process = subprocess.Popen(['git', 'clone', url], stdout=subprocess.PIPE)
    
            # additional code from: https://stackoverflow.com/a/39564562/6924364
    
            dloutput = subprocess.Popen(['git', 'clone', '--progress', url], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    
            fd = dloutput.stderr.fileno()
            gitmsg = [] 
            while True:
                lines = os.read(fd,1000).decode('utf-8')
                lines = re.split('\n|\r', lines)
                for l in lines:
                    self.screen.erase()
                    for g in gitmsg:
                        self.screen.addstr(g + '\n')
                    if l != '':
                        self.screen.addstr(str(l) + '\n')
                        self.screen.refresh()
                    if 'Cloning' in l:
                        gitmsg.append(l)
                    if 'done' in l:
                        gitmsg.append(l)
                if len(lines) == 1:
                    break
    
            self.screen.addstr("Press any key to continue.")
    
            self.screen.getch()
    
    if __name__ == '__main__':
        curses.wrapper(MyApp)