ref: move to pathlib

This commit is contained in:
Jordan ⌨️ 2023-11-13 21:22:59 +01:00
parent 8f59111ad7
commit e54f8f5964
7 changed files with 76 additions and 137 deletions

View file

@ -44,12 +44,11 @@ def setupLogger(verbose: bool):
projectRoot = model.Project.root() projectRoot = model.Project.root()
logFile = const.GLOBAL_LOG_FILE logFile = const.GLOBAL_LOG_FILE
if projectRoot is not None: if projectRoot is not None:
logFile = os.path.join(projectRoot, const.PROJECT_LOG_FILE) logfile = projectRoot / const.PROJECT_LOG_FILE
# create the directory if it doesn't exist # create the directory if it doesn't exist
logDir = os.path.dirname(logFile) if not logFile.parent.is_dir():
if not os.path.isdir(logDir): logFile.parent.mkdir(parents=True)
os.makedirs(logDir)
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,

View file

@ -49,7 +49,7 @@ def buildpath(target: model.Target, component: model.Component, path) -> Path:
def listSrc(component: model.Component) -> list[str]: def listSrc(component: model.Component) -> list[str]:
wildcards = set(chain(*map(lambda rule: rule.fileIn, rules.rules.values()))) wildcards = set(chain(*map(lambda rule: rule.fileIn, rules.rules.values())))
dirs = [component.dirname()] + list( dirs = [component.dirname()] + list(
map(lambda d: os.path.join(component.dirname(), d), component.subdirs) map(lambda d: component.parent / d, component.subdirs)
) )
return shell.find(dirs, list(wildcards), recusive=False) return shell.find(dirs, list(wildcards), recusive=False)
@ -195,9 +195,10 @@ def build(
components: Union[list[model.Component], model.Component, None] = None, components: Union[list[model.Component], model.Component, None] = None,
) -> list[Product]: ) -> list[Product]:
all = False all = False
shell.mkdir(target.builddir) target.builddir.mkdir(parents=True, exist_ok=True)
ninjaPath = os.path.join(target.builddir, "build.ninja") ninjaPath = target.builddir / "build.ninja"
with open(ninjaPath, "w") as f:
with ninjaPath.open("w") as f:
gen(f, target, registry) gen(f, target, registry)
if components is None: if components is None:

View file

@ -6,16 +6,16 @@ VERSION = (0, 6, 0, "dev")
VERSION_STR = ( VERSION_STR = (
f"{VERSION[0]}.{VERSION[1]}.{VERSION[2]}{'-' + VERSION[3] if VERSION[3] else ''}" f"{VERSION[0]}.{VERSION[1]}.{VERSION[2]}{'-' + VERSION[3] if VERSION[3] else ''}"
) )
ARGV0 = os.path.basename(sys.argv[0]) ARGV0 = Path(sys.argv[0])
PROJECT_CK_DIR = ".cutekit" PROJECT_CK_DIR = Path(".cutekit")
GLOBAL_CK_DIR = os.path.join(os.path.expanduser("~"), ".cutekit") GLOBAL_CK_DIR = Path.home() / ".cutekit"
BUILD_DIR = os.path.join(PROJECT_CK_DIR, "build") BUILD_DIR = PROJECT_CK_DIR / "build"
CACHE_DIR = os.path.join(PROJECT_CK_DIR, "cache") CACHE_DIR = PROJECT_CK_DIR / "cache"
EXTERN_DIR = os.path.join(PROJECT_CK_DIR, "extern") EXTERN_DIR = PROJECT_CK_DIR / "extern"
SRC_DIR = "src" SRC_DIR = Path("src")
META_DIR = "meta" META_DIR = Path("meta")
TARGETS_DIR = os.path.join(META_DIR, "targets") TARGETS_DIR = META_DIR / "targets"
DEFAULT_REPO_TEMPLATES = "cute-engineering/cutekit-templates" DEFAULT_REPO_TEMPLATES = "cute-engineering/cutekit-templates"
DESCRIPTION = "A build system and package manager for low-level software development" DESCRIPTION = "A build system and package manager for low-level software development"
PROJECT_LOG_FILE = os.path.join(PROJECT_CK_DIR, "cutekit.log") PROJECT_LOG_FILE = PROJECT_CK_DIR / "cutekit.log"
GLOBAL_LOG_FILE = os.path.join(os.path.expanduser("~"), ".cutekit", "cutekit.log") GLOBAL_LOG_FILE = GLOBAL_CK_DIR / "cutekit.log"

View file

@ -80,7 +80,7 @@ def view(
for req in component.provides: for req in component.provides:
g.edge(req, component.id, arrowhead="none", color="#aaaaaa") g.edge(req, component.id, arrowhead="none", color="#aaaaaa")
g.view(filename=os.path.join(target.builddir, "graph.gv")) g.view(filename=str(target.builddir / "graph.gv"))
@cli.command("g", "graph", "Show the dependency graph") @cli.command("g", "graph", "Show the dependency graph")

View file

@ -32,7 +32,7 @@ class Kind(Enum):
class Manifest(DataClassJsonMixin): class Manifest(DataClassJsonMixin):
id: str id: str
type: Kind = field(default=Kind.UNKNOWN) type: Kind = field(default=Kind.UNKNOWN)
path: str = field(default="") path: Path = field(default=Path())
@staticmethod @staticmethod
def parse(path: Path, data: dict[str, Any]) -> "Manifest": def parse(path: Path, data: dict[str, Any]) -> "Manifest":
@ -43,7 +43,7 @@ class Manifest(DataClassJsonMixin):
kind = Kind(data["type"]) kind = Kind(data["type"])
del data["$schema"] del data["$schema"]
obj = KINDS[kind].from_dict(data) obj = KINDS[kind].from_dict(data)
obj.path = str(path) obj.path = path
return obj return obj
@staticmethod @staticmethod
@ -53,14 +53,14 @@ class Manifest(DataClassJsonMixin):
""" """
return Manifest.parse(path, jexpr.evalRead(path)) return Manifest.parse(path, jexpr.evalRead(path))
def dirname(self) -> str: def dirname(self) -> Path:
""" """
Return the directory of the manifest Return the directory of the manifest
""" """
return os.path.dirname(self.path) return self.path.parent
def subpath(self, path) -> Path: def subpath(self, path) -> Path:
return Path(self.dirname()) / path return self.dirname() / path
def ensureType(self, t: Type[utils.T]) -> utils.T: def ensureType(self, t: Type[utils.T]) -> utils.T:
""" """
@ -90,19 +90,19 @@ class Project(Manifest):
extern: dict[str, Extern] = field(default_factory=dict) extern: dict[str, Extern] = field(default_factory=dict)
@property @property
def externDirs(self) -> list[str]: def externDirs(self) -> list[Path]:
res = map(lambda e: os.path.join(const.EXTERN_DIR, e), self.extern.keys()) res = map(lambda e: const.EXTERN_DIR / e, self.extern.keys())
return list(res) return list(res)
@staticmethod @staticmethod
def root() -> Optional[str]: def root() -> Optional[Path]:
""" """
Find the root of the project by looking for a project.json Find the root of the project by looking for a project.json
""" """
cwd = Path.cwd() cwd = Path.cwd()
while str(cwd) != cwd.root: while str(cwd) != cwd.root:
if (cwd / "project.json").is_file(): if (cwd / "project.json").is_file():
return str(cwd) return cwd
cwd = cwd.parent cwd = cwd.parent
return None return None
@ -119,11 +119,11 @@ class Project(Manifest):
os.chdir(path) os.chdir(path)
@staticmethod @staticmethod
def at(path: str) -> Optional["Project"]: def at(path: str | Path) -> Optional["Project"]:
path = os.path.join(path, "project.json") path = Path(path) / "project.json"
if not os.path.exists(path): if not path.exists():
return None return None
return Manifest.load(Path(path)).ensureType(Project) return Manifest.load(path).ensureType(Project)
@staticmethod @staticmethod
def ensure() -> "Project": def ensure() -> "Project":
@ -133,16 +133,16 @@ class Project(Manifest):
"No project.json found in this directory or any parent directory" "No project.json found in this directory or any parent directory"
) )
os.chdir(root) os.chdir(root)
return Manifest.load(Path(os.path.join(root, "project.json"))).ensureType( return Manifest.load(Path(root / "project.json")).ensureType(
Project Project
) )
@staticmethod @staticmethod
def fetchs(extern: dict[str, Extern]): def fetchs(extern: dict[str | Path, Extern]):
for extSpec, ext in extern.items(): for extSpec, ext in extern.items():
extPath = os.path.join(const.EXTERN_DIR, extSpec) extPath = const.EXTERN_DIR / extSpec
if os.path.exists(extPath): if extPath.exists():
print(f"Skipping {extSpec}, already installed") print(f"Skipping {extSpec}, already installed")
continue continue
@ -184,7 +184,7 @@ def initCmd(args: cli.Args):
list = args.consumeOpt("list") list = args.consumeOpt("list")
template = args.consumeArg() template = args.consumeArg()
name = args.consumeArg() name = Path(args.consumeArg())
_logger.info("Fetching registry...") _logger.info("Fetching registry...")
r = requests.get(f"https://raw.githubusercontent.com/{repo}/main/registry.json") r = requests.get(f"https://raw.githubusercontent.com/{repo}/main/registry.json")
@ -214,7 +214,7 @@ def initCmd(args: cli.Args):
_logger.info(f"No name was provided, defaulting to {template}") _logger.info(f"No name was provided, defaulting to {template}")
name = template name = template
if os.path.exists(name): if name.exists():
raise RuntimeError(f"Directory {name} already exists") raise RuntimeError(f"Directory {name} already exists")
print(f"Creating project {name} from template {template}...") print(f"Creating project {name} from template {template}...")
@ -255,8 +255,8 @@ class Target(Manifest):
return utils.hash((self.props, [v.to_dict() for k, v in self.tools.items()])) return utils.hash((self.props, [v.to_dict() for k, v in self.tools.items()]))
@property @property
def builddir(self) -> str: def builddir(self) -> Path:
return os.path.join(const.BUILD_DIR, f"{self.id}-{self.hashid[:8]}") return const.BUILD_DIR / f"{self.id}-{self.hashid[:8]}"
@staticmethod @staticmethod
def use(args: cli.Args) -> "Target": def use(args: cli.Args) -> "Target":
@ -518,15 +518,15 @@ class Registry(DataClassJsonMixin):
# Lookup and load all extern projects # Lookup and load all extern projects
for externDir in project.externDirs: for externDir in project.externDirs:
projectPath = os.path.join(externDir, "project.json") projectPath = externDir / "project.json"
manifestPath = os.path.join(externDir, "manifest.json") manifestPath = externDir / "manifest.json"
if os.path.exists(projectPath): if projectPath.exists():
registry._append(Manifest.load(Path(projectPath)).ensureType(Project)) registry._append(Manifest.load(projectPath).ensureType(Project))
elif os.path.exists(manifestPath): elif manifestPath.exists():
# For simple library allow to have a manifest.json instead of a project.json # For simple library allow to have a manifest.json instead of a project.json
registry._append( registry._append(
Manifest.load(Path(manifestPath)).ensureType(Component) Manifest.load(manifestPath).ensureType(Component)
) )
else: else:
_logger.warn( _logger.warn(
@ -535,22 +535,22 @@ class Registry(DataClassJsonMixin):
# Load all manifests from projects # Load all manifests from projects
for project in list(registry.iter(Project)): for project in list(registry.iter(Project)):
targetDir = os.path.join(project.dirname(), const.TARGETS_DIR) targetDir = project.parent / const.TARGETS_DIR
targetFiles = shell.find(targetDir, ["*.json"]) targetFiles = targetDir.glob("*.json")
for targetFile in targetFiles: for targetFile in targetFiles:
registry._append(Manifest.load(Path(targetFile)).ensureType(Target)) registry._append(Manifest.load(Path(targetFile)).ensureType(Target))
componentDir = os.path.join(project.dirname(), const.SRC_DIR) componentDir = project.parent / const.COMPONENTS_DIR
rootComponent = os.path.join(project.dirname(), "manifest.json") rootComponent = project.parent / "manifest.json"
componentFiles = shell.find(componentDir, ["manifest.json"]) componentFiles = list(componentDir.glob("manifest.json"))
if os.path.exists(rootComponent): if rootComponent.exists():
componentFiles += [rootComponent] componentFiles += [rootComponent]
for componentFile in componentFiles: for componentFile in componentFiles:
registry._append( registry._append(
Manifest.load(Path(componentFile)).ensureType(Component) Manifest.load(componentFile).ensureType(Component)
) )
# Resolve all dependencies for all targets # Resolve all dependencies for all targets

View file

@ -30,17 +30,14 @@ def loadAll():
return return
project = model.Project.at(root) project = model.Project.at(root)
paths = list( paths = list(map(lambda e: const.EXTERN_DIR / e, project.extern.keys())) + ["."]
map(lambda e: os.path.join(const.EXTERN_DIR, e), project.extern.keys())
) + ["."]
for dirname in paths: for dirname in paths:
pluginDir = os.path.join(root, dirname, const.META_DIR, "plugins") pluginDir = root / dirname / const.META_DIR / "plugins"
for files in shell.readdir(pluginDir): for script in pluginDir.glob("*.py"):
if files.endswith(".py"): plugin = load(script)
plugin = load(os.path.join(pluginDir, files))
if plugin: if plugin:
_logger.info(f"Loaded plugin {plugin.name}") _logger.info(f"Loaded plugin {plugin.name}")
plugin.init() plugin.init()

View file

@ -12,6 +12,7 @@ import logging
import tempfile import tempfile
from pathlib import Path
from typing import Optional from typing import Optional
from . import const from . import const
@ -49,81 +50,30 @@ def sha256sum(path: str) -> str:
return hashlib.sha256(f.read()).hexdigest() return hashlib.sha256(f.read()).hexdigest()
def find( def rmrf(path: Path) -> bool:
path: str | list[str], wildcards: list[str] = [], recusive: bool = True
) -> list[str]:
_logger.info(f"Looking for files in {path} matching {wildcards}")
result: list[str] = []
if isinstance(path, list):
for p in path:
result += find(p, wildcards, recusive)
return result
if not os.path.isdir(path):
return []
if recusive:
for root, _, files in os.walk(path):
for f in files:
if len(wildcards) == 0:
result.append(os.path.join(root, f))
else:
for wildcard in wildcards:
if fnmatch.fnmatch(f, wildcard):
result.append(os.path.join(root, f))
break
else:
for f in os.listdir(path):
if len(wildcards) == 0:
result.append(os.path.join(path, f))
else:
for wildcard in wildcards:
if fnmatch.fnmatch(f, wildcard):
result.append(os.path.join(path, f))
break
return result
def mkdir(path: str) -> str:
_logger.info(f"Creating directory {path}")
try:
os.makedirs(path)
except OSError as exc:
if not (exc.errno == errno.EEXIST and os.path.isdir(path)):
raise
return path
def rmrf(path: str) -> bool:
_logger.info(f"Removing directory {path}") _logger.info(f"Removing directory {path}")
if not os.path.exists(path): if not path.exists():
return False return False
shutil.rmtree(path, ignore_errors=True) shutil.rmtree(path, ignore_errors=True)
return True return True
def wget(url: str, path: Optional[str] = None) -> str: def wget(url: str, path: Optional[Path] = None) -> Path:
import requests import requests
if path is None: if path is None:
path = os.path.join( path = const.CACHE_DIR / hashlib.sha256(url.encode("utf-8")).hexdigest()
const.CACHE_DIR, hashlib.sha256(url.encode("utf-8")).hexdigest()
)
if os.path.exists(path): if path.exists():
return path return path
_logger.info(f"Downloading {url} to {path}") _logger.info(f"Downloading {url} to {path}")
r = requests.get(url, stream=True) r = requests.get(url, stream=True)
r.raise_for_status() r.raise_for_status()
mkdir(os.path.dirname(path)) path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "wb") as f: with path.open("wb") as f:
for chunk in r.iter_content(chunk_size=8192): for chunk in r.iter_content(chunk_size=8192):
if chunk: if chunk:
f.write(chunk) f.write(chunk)
@ -179,36 +129,28 @@ def popen(*args: str) -> str:
return proc.stdout.decode("utf-8") return proc.stdout.decode("utf-8")
def readdir(path: str) -> list[str]: def cp(src: Path, dst: Path):
_logger.info(f"Reading directory {path}")
try:
return os.listdir(path)
except FileNotFoundError:
return []
def cp(src: str, dst: str):
_logger.info(f"Copying {src} to {dst}") _logger.info(f"Copying {src} to {dst}")
shutil.copy(src, dst) shutil.copy(src, dst)
def mv(src: str, dst: str): def mv(src: Path, dst: Path):
_logger.info(f"Moving {src} to {dst}") _logger.info(f"Moving {src} to {dst}")
shutil.move(src, dst) shutil.move(src, dst)
def cpTree(src: str, dst: str): def cpTree(src: Path, dst: Path):
_logger.info(f"Copying {src} to {dst}") _logger.info(f"Copying {src} to {dst}")
shutil.copytree(src, dst, dirs_exist_ok=True) shutil.copytree(src, dst, dirs_exist_ok=True)
def cloneDir(url: str, path: str, dest: str) -> str: def cloneDir(url: str, path: Path, dest: Path) -> Path:
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
mkdir(tmp) tmp = Path(tmp)
tmp.mkdir(parents=True, exist_ok=True)
exec( exec(
*["git", "clone", "-n", "--depth=1", "--filter=tree:0", url, tmp, "-q"], *["git", "clone", "-n", "--depth=1", "--filter=tree:0", url, tmp, "-q"],
quiet=True, quiet=True,
@ -218,7 +160,7 @@ def cloneDir(url: str, path: str, dest: str) -> str:
quiet=True, quiet=True,
) )
exec(*["git", "-C", tmp, "checkout", "-q", "--no-progress"], quiet=True) exec(*["git", "-C", tmp, "checkout", "-q", "--no-progress"], quiet=True)
mv(os.path.join(tmp, path), dest) mv(tmp / path, dest)
return dest return dest