aboutsummaryrefslogtreecommitdiff
path: root/scripts/peafour.py
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2021-08-21 18:51:29 +0200
committerStefan Boberg <[email protected]>2021-08-21 18:51:29 +0200
commitee2d4c08b48c17605f36c00377beae0b0dc072e3 (patch)
tree19c8f1dfef4b2e19293327ee2a6d3a1321339397 /scripts/peafour.py
parentImproved crash reporting setup and removed old stubs (diff)
downloadzen-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.py187
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()