utils.py (2377B)
1 import os 2 import subprocess 3 import sys 4 from typing import Any, Callable, Generic, Optional, Text, TypeVar 5 T = TypeVar("T") 6 7 8 def rel_path_to_url(rel_path: Text, url_base: Text = "/") -> Text: 9 assert not os.path.isabs(rel_path), rel_path 10 if url_base[0] != "/": 11 url_base = "/" + url_base 12 if url_base[-1] != "/": 13 url_base += "/" 14 return url_base + rel_path.replace(os.sep, "/") 15 16 17 def from_os_path(path: Text) -> Text: 18 assert os.path.sep == "/" or sys.platform == "win32" 19 if "/" == os.path.sep: 20 rv = path 21 else: 22 rv = path.replace(os.path.sep, "/") 23 if "\\" in rv: 24 raise ValueError("path contains \\ when separator is %s" % os.path.sep) 25 return rv 26 27 28 def to_os_path(path: Text) -> Text: 29 assert os.path.sep == "/" or sys.platform == "win32" 30 if "\\" in path: 31 raise ValueError("normalised path contains \\") 32 if "/" == os.path.sep: 33 return path 34 return path.replace("/", os.path.sep) 35 36 37 def git(path: Text) -> Optional[Callable[..., Text]]: 38 def gitfunc(cmd: Text, *args: Text) -> Text: 39 full_cmd = ["git", cmd] + list(args) 40 try: 41 return subprocess.check_output(full_cmd, cwd=path, stderr=subprocess.STDOUT).decode('utf8') 42 except Exception as e: 43 if sys.platform == "win32" and isinstance(e, WindowsError): 44 full_cmd[0] = "git.bat" 45 return subprocess.check_output(full_cmd, cwd=path, stderr=subprocess.STDOUT).decode('utf8') 46 else: 47 raise 48 49 try: 50 # this needs to be a command that fails if we aren't in a git repo 51 gitfunc("rev-parse", "--show-toplevel") 52 except (subprocess.CalledProcessError, OSError): 53 return None 54 else: 55 return gitfunc 56 57 58 class cached_property(Generic[T]): 59 def __init__(self, func: Callable[[Any], T]) -> None: 60 self.func = func 61 self.__doc__ = getattr(func, "__doc__") 62 self.name = func.__name__ 63 64 def __get__(self, obj: Any, cls: Optional[type] = None) -> T: 65 if obj is None: 66 return self # type: ignore 67 68 # we can unconditionally assign as next time this won't be called 69 assert self.name not in obj.__dict__ 70 rv = obj.__dict__[self.name] = self.func(obj) 71 obj.__dict__.setdefault("__cached_properties__", set()).add(self.name) 72 return rv