Skip to content

Commit a52b729

Browse files
authored
gui: Handle GRASS_INFO_END in line in gui/wxpython/core/gcmd.py (OSGeo#4876)
* gui: Handle GRASS_INFO_END in line in gui/wxpython/core/gcmd.py * python: Add some typing to gui/wxpython/core/gcmd.py CommandThread * LiteralString is only available since Python 3.11, so use str instead * Simplify TYPE_CHECKING imports
1 parent 68a51f6 commit a52b729

File tree

1 file changed

+56
-41
lines changed

1 file changed

+56
-41
lines changed

gui/wxpython/core/gcmd.py

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,20 @@
2525
@author Martin Landa <landa.martin gmail.com>
2626
"""
2727

28+
from __future__ import annotations
29+
30+
import errno
31+
import locale
2832
import os
33+
import signal
34+
import subprocess
2935
import sys
3036
import time
31-
import errno
32-
import signal
3337
import traceback
34-
import locale
35-
import subprocess
3638
from threading import Thread
37-
import wx
39+
from typing import TYPE_CHECKING, TextIO
3840

41+
import wx
3942
from core.debug import Debug
4043
from core.globalvar import SCT_EXT
4144

@@ -44,12 +47,16 @@
4447

4548
is_mswindows = sys.platform == "win32"
4649
if is_mswindows:
50+
import msvcrt
51+
4752
from win32file import ReadFile, WriteFile
4853
from win32pipe import PeekNamedPipe
49-
import msvcrt
5054
else:
51-
import select
5255
import fcntl
56+
import select
57+
58+
if TYPE_CHECKING:
59+
from io import TextIOWrapper
5360

5461

5562
def DecodeString(string):
@@ -298,7 +305,7 @@ def _recv(self, which, maxsize):
298305
message = "Other end disconnected!"
299306

300307

301-
def recv_some(p, t=0.1, e=1, tr=5, stderr=0):
308+
def recv_some(p, t=0.1, e=1, tr=5, stderr=0) -> str: # TODO: use LiteralString on 3.11+
302309
tr = max(tr, 1)
303310
x = time.time() + t
304311
y = []
@@ -342,7 +349,7 @@ def __init__(
342349
stdin=None,
343350
verbose=None,
344351
wait=True,
345-
rerr=False,
352+
rerr: bool | None = False,
346353
stdout=None,
347354
stderr=None,
348355
):
@@ -482,7 +489,7 @@ def __ProcessStdErr(self):
482489
type = "WARNING"
483490
elif "GRASS_INFO_ERROR" in line: # error
484491
type = "ERROR"
485-
elif "GRASS_INFO_END": # end of message
492+
elif "GRASS_INFO_END" in line: # end of message
486493
msg.append((type, content))
487494
type = None
488495
content = ""
@@ -510,7 +517,14 @@ class CommandThread(Thread):
510517
"""Create separate thread for command. Used for commands launched
511518
on the background."""
512519

513-
def __init__(self, cmd, env=None, stdin=None, stdout=sys.stdout, stderr=sys.stderr):
520+
def __init__(
521+
self,
522+
cmd,
523+
env=None,
524+
stdin: TextIOWrapper | None = None,
525+
stdout: TextIO = sys.stdout,
526+
stderr: TextIO = sys.stderr,
527+
) -> None:
514528
"""
515529
:param cmd: command (given as list)
516530
:param env: environmental variables
@@ -522,11 +536,11 @@ def __init__(self, cmd, env=None, stdin=None, stdout=sys.stdout, stderr=sys.stde
522536

523537
self.cmd = cmd
524538
self.stdin = stdin
525-
self.stdout = stdout
526-
self.stderr = stderr
539+
self.stdout: TextIO = stdout
540+
self.stderr: TextIO = stderr
527541
self.env = env
528542

529-
self.module = None
543+
self.module: Popen | None = None
530544
self.error = ""
531545

532546
self._want_abort = False
@@ -584,7 +598,7 @@ def run(self):
584598
print(e, file=sys.stderr)
585599
return 1
586600

587-
if self.stdin: # read stdin if requested ...
601+
if self.stdin and self.module.stdin is not None: # read stdin if requested...
588602
self.module.stdin.write(self.stdin)
589603
self.module.stdin.close()
590604

@@ -593,34 +607,35 @@ def run(self):
593607

594608
def _redirect_stream(self):
595609
"""Redirect stream"""
596-
if self.stdout:
610+
if self.stdout and self.module is not None and self.module.stdout is not None:
597611
# make module stdout/stderr non-blocking
598612
out_fileno = self.module.stdout.fileno()
599613
if not is_mswindows:
600614
flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
601615
fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags | os.O_NONBLOCK)
602616

603-
if self.stderr:
617+
if self.stderr and self.module is not None and self.module.stderr is not None:
604618
# make module stdout/stderr non-blocking
605619
out_fileno = self.module.stderr.fileno()
606620
if not is_mswindows:
607621
flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
608622
fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags | os.O_NONBLOCK)
609623

610624
# wait for the process to end, sucking in stuff until it does end
611-
while self.module.poll() is None:
612-
if self._want_abort: # abort running process
613-
self.module.terminate()
614-
self.aborted = True
615-
return
616-
if self.stdout:
617-
line = recv_some(self.module, e=0, stderr=0)
618-
self.stdout.write(line)
619-
if self.stderr:
620-
line = recv_some(self.module, e=0, stderr=1)
621-
self.stderr.write(line)
622-
if len(line) > 0:
623-
self.error = line
625+
if self.module is not None:
626+
while self.module.poll() is None:
627+
if self._want_abort: # abort running process
628+
self.module.terminate()
629+
self.aborted = True
630+
return
631+
if self.stdout:
632+
line = recv_some(self.module, e=0, stderr=0)
633+
self.stdout.write(line)
634+
if self.stderr:
635+
line = recv_some(self.module, e=0, stderr=1)
636+
self.stderr.write(line)
637+
if len(line) > 0:
638+
self.error = line
624639

625640
# get the last output
626641
if self.stdout:
@@ -632,12 +647,12 @@ def _redirect_stream(self):
632647
if len(line) > 0:
633648
self.error = line
634649

635-
def abort(self):
650+
def abort(self) -> None:
636651
"""Abort running process, used by main thread to signal an abort"""
637652
self._want_abort = True
638653

639654

640-
def _formatMsg(text):
655+
def _formatMsg(text: str) -> str:
641656
"""Format error messages for dialogs"""
642657
message = ""
643658
for line in text.splitlines():
@@ -660,14 +675,14 @@ def _formatMsg(text):
660675
def RunCommand(
661676
prog,
662677
flags="",
663-
overwrite=False,
664-
quiet=False,
665-
verbose=False,
678+
overwrite: bool = False,
679+
quiet: bool = False,
680+
verbose: bool = False,
666681
parent=None,
667-
read=False,
682+
read: bool = False,
668683
parse=None,
669-
stdin=None,
670-
getErrorMsg=False,
684+
stdin: TextIO | None = None,
685+
getErrorMsg: bool = False,
671686
env=None,
672687
**kwargs,
673688
):
@@ -717,7 +732,7 @@ def RunCommand(
717732

718733
ps = grass.start_command(prog, flags, overwrite, quiet, verbose, env=env, **kwargs)
719734

720-
if stdin:
735+
if stdin and ps.stdin:
721736
ps.stdin.write(encode(stdin))
722737
ps.stdin.close()
723738
ps.stdin = None
@@ -764,7 +779,7 @@ def RunCommand(
764779
return stdout, _formatMsg(stderr)
765780

766781

767-
def GetDefaultEncoding(forceUTF8=False):
782+
def GetDefaultEncoding(forceUTF8: bool = False) -> str:
768783
"""Get default system encoding
769784
770785
:param bool forceUTF8: force 'UTF-8' if encoding is not defined
@@ -786,4 +801,4 @@ def GetDefaultEncoding(forceUTF8=False):
786801
return enc
787802

788803

789-
_enc = GetDefaultEncoding() # define as global variable
804+
_enc: str = GetDefaultEncoding() # define as global variable

0 commit comments

Comments
 (0)