diff options
| author | Stefan Boberg <[email protected]> | 2021-08-21 18:51:29 +0200 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2021-08-21 18:51:29 +0200 |
| commit | ee2d4c08b48c17605f36c00377beae0b0dc072e3 (patch) | |
| tree | 19c8f1dfef4b2e19293327ee2a6d3a1321339397 /scripts/peafour.py | |
| parent | Improved crash reporting setup and removed old stubs (diff) | |
| download | zen-ee2d4c08b48c17605f36c00377beae0b0dc072e3.tar.xz zen-ee2d4c08b48c17605f36c00377beae0b0dc072e3.zip | |
Initial build deploy scripts. Still missing debug information upload step so should not be used
Diffstat (limited to 'scripts/peafour.py')
| -rw-r--r-- | scripts/peafour.py | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/scripts/peafour.py b/scripts/peafour.py new file mode 100644 index 000000000..c9f559a21 --- /dev/null +++ b/scripts/peafour.py @@ -0,0 +1,187 @@ +import types +import marshal +import threading +import subprocess as sp + +#------------------------------------------------------------------------------- +class _P4Result(object): + def __init__(self, result): + super().__setattr__("_result", result) + + def __str__(self): return str(self._result) + def __contains__(self, key): return (key in self._result) or (key + "0" in self._result) + def as_dict(self): return {k.decode():v.decode() for k,v in self._result.items()} + def __setattr__(self, key, value): self._result[key.encode()] = str(value).encode() + + def __getattr__(self, key): + if (key + "0").encode() in self._result: + def as_list(): + index = 0; + while (key + str(index)).encode() in self._result: + indexed_key = (key + str(index)).encode() + yield self._result[indexed_key].decode() + index += 1 + return as_list() + + ret = self._result.get(str.encode(key)) + if ret == None: raise AttributeError(key) + return ret.decode(errors="replace") + +#------------------------------------------------------------------------------- +class _P4Command(object): + @staticmethod + def _read_args(*args, **kwargs): + for k,v in kwargs.items(): + if isinstance(v, bool): + if v: + yield "-" + k + elif v != None: + yield f"-{k}={v}" if len(k) > 1 else f"-{k}{v}" + + for arg in (x for x in args if isinstance(x, str)): + yield arg + + def __init__(self, **options): + opt_iter = _P4Command._read_args(**options) + self._command = ["p4", "-Qutf8", "-G"] + self._command += (x for x in opt_iter) + + def start(self, command, *args, **kwargs): + self._stdin_args = [] + for arg in (x for x in args if not isinstance(x, str)): + if not hasattr(arg, "__iter__"): + raise TypeError("P4 arguments can be only strings or sequences") + self._stdin_args.append(arg) + + self._proc = None + + arg_iter = _P4Command._read_args(*args, **kwargs) + + if self._stdin_args: + self._command.append("-x-") + self._command.append(command) + self._command += (x for x in arg_iter) + + def __del__(self): self._close_proc() + def __str__(self): return " ".join(self._command) + def __iter__(self): yield from self._iter() + def __getattr__(self, name): return getattr(self.run(), name) + + def run(self, **kwargs): + return next(self._iter(**kwargs), None) + + def read(self, **kwargs): + yield from self._iter(**kwargs) + + def _close_proc(self): + if self._proc: + if self._proc.stdin: + self._proc.stdin.close() + self._proc.stdout.close() + self._proc = None + + def _iter(self, input_data=None, on_error=True, on_info=None, on_text=None): + stdin = None + if input_data != None: + if self._stdin_args: + raise _P4.Error("It is unsupported to have both generator-type arguments and input data") + + if isinstance(input_data, dict): + input_data = {str(k).encode():str(v).encode() for k,v in input_data.items()} + else: + raise _P4.Error("Unsupported input data type; " + type(input_data).__name__) + stdin = sp.PIPE + + if self._stdin_args: + stdin = sp.PIPE + + proc = sp.Popen(self._command, stdout=sp.PIPE, stdin=stdin) + self._proc = proc + + if stdin: + def stdin_thread_entry(): + try: + if input_data: + marshal.dump(input_data, proc.stdin, 0) + for args in self._stdin_args: + for arg in args: + arg = str(arg).encode() + b"\n" + proc.stdin.write(arg) + except (BrokenPipeError, OSError): + pass + finally: + try: proc.stdin.close() + except: pass + + stdin_thread = threading.Thread(target=stdin_thread_entry) + stdin_thread.start() + + while True: + try: result = marshal.load(proc.stdout) + except EOFError: break + + code = result[b"code"] + del result[b"code"] + + if code == b"error": + if isinstance(on_error, bool): + if on_error: + raise _P4.Error(result[b"data"].decode()[:-1]) + continue + + try: + on_error(_P4Result(result)) + except: + proc.terminate() + raise + continue + + if code == b"stat": + yield _P4Result(result) + continue + + if code == b"text" and on_text: + try: + data = result.get(b"data", b"") + on_text(data) + continue + except: + proc.terminate() + raise + + if code == b"info" and on_info: + try: + on_info(_P4Result(result)) + continue + except: + proc.terminate() + raise + + if stdin: + stdin_thread.join() + self._close_proc() + +#------------------------------------------------------------------------------- +class _P4(object): + class Error(Exception): + def __init__(self, msg): + super().__init__("Perforce: " + msg) + + def __init__(self, **options): + self._options = options + + def __getattr__(self, command): + if command == "Error": + return _P4.Error + + def inner(*args, **kwargs): + instance = _P4Command(**self._options) + instance.start(command, *args, **kwargs) + return instance + + return inner + + def __call__(self, **options): + return _P4(**options) + +P4 = _P4() |