From 4dbb8d742510867838cc00215ac6ee7de3f57f1d Mon Sep 17 00:00:00 2001 From: "Arjun G. Menon" Date: Sat, 3 Aug 2024 02:13:29 -0400 Subject: [PATCH] Implement --ignore, refactor Fs a little bit, etc. --- alteza/engine.py | 38 +++++++++++++++---------------- alteza/fs.py | 59 +++++++++++++++++++++++++++--------------------- build_test.sh | 2 +- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/alteza/engine.py b/alteza/engine.py index 6357768..0d1154a 100644 --- a/alteza/engine.py +++ b/alteza/engine.py @@ -23,6 +23,7 @@ AltezaException, Md, NonMd, + FsCrawlResult, Fs, Fore, Style, @@ -42,7 +43,7 @@ class Args(Tap): # pyre-ignore[13] class Content: - def __init__(self, args: Args, fs: Fs) -> None: + def __init__(self, args: Args, fs: FsCrawlResult) -> None: self.inTemplate: bool = False self.templateCache: Dict[str, str] = {} self.seenTemplateLinks: Set[FileNode] = set() @@ -328,11 +329,9 @@ def enterDir(newDir: str) -> Generator[None, None, None]: class Engine: - # This class is just here to organize a bunch of related functions together. - # This class should never be instantiated, and most functions not called directly. # Engine.generate(...) is called to write the output of a processed Content object. - # Similarly, Engine.run(args) is used to invoke Alteza overall. - + # Engine.makeSite() is called to perform a full site generation. + # Engine.run() is used to invoke Alteza overall. def __init__(self, args: Args) -> None: self.args: Args = args # Just copying & renaming a few args: @@ -341,7 +340,7 @@ def __init__(self, args: Args) -> None: self.outputDir: str = args.output # Other instance variables: self.shouldExit: bool = False - self.ignoreAbsPaths: List[str] = self.checkIgnorePaths(args) + self.setIgnoreAbsPaths(args) @staticmethod def generateMdContents(md: Md) -> None: @@ -432,15 +431,15 @@ def resetOutputDir(self) -> None: def processContent(self) -> Content: with enterDir(self.contentDir): print("Analyzing content directory...") - fs = Fs() - print(fs.nameRegistry) - content = Content(self.args, fs) + fsCrawlResult = Fs.crawl() + print(fsCrawlResult.nameRegistry) + content = Content(self.args, fsCrawlResult) print("Processing...\n") content.process() print("\nSuccessfully completed processing.\n") print("File Tree:") - print(fs.rootDir.displayDir()) + print(fsCrawlResult.rootDir.displayDir()) return content @@ -458,6 +457,15 @@ def makeSite(self) -> None: # pylint: disable=consider-using-f-string print("\nSite generation complete. Time elapsed: %.2f ms" % elapsedMilliseconds) + @staticmethod + def setIgnoreAbsPaths(args: Args) -> None: + Fs.ignoreAbsPaths = [] + for somePath in args.ignore: + if os.path.exists(somePath): + Fs.ignoreAbsPaths.append(os.path.abspath(somePath)) + else: + raise AltezaException(f"Path to ignore `{somePath}` does not exist.") + class WatchdogEventHandler(FileSystemEventHandler): def __init__(self) -> None: self.timeOfMostRecentEvent: Optional[int] = None @@ -504,16 +512,6 @@ def signalHandler(sig: int, frame: Optional[types.FrameType]) -> None: observer.stop() observer.join() - @staticmethod - def checkIgnorePaths(args: Args) -> List[str]: - ignoreAbsPaths = [] - for somePath in args.ignore: - if os.path.exists(somePath): - ignoreAbsPaths.append(os.path.abspath(somePath)) - else: - raise AltezaException(f"Path to ignore `{somePath}` does not exist.") - return ignoreAbsPaths - def run(self) -> None: self.makeSite() diff --git a/alteza/fs.py b/alteza/fs.py index 0cc1b7b..7b4c89b 100644 --- a/alteza/fs.py +++ b/alteza/fs.py @@ -2,6 +2,7 @@ import os import re from collections import defaultdict +from dataclasses import dataclass from datetime import date, datetime from subprocess import check_output, CalledProcessError, STDOUT from typing import ( @@ -55,13 +56,24 @@ def setNodeAsPublic(self) -> None: self.shouldPublish = True def makePublic(self) -> None: - Fs.runOnFsNodeAndAscendantNodes(self, lambda fsNode: fsNode.setNodeAsPublic()) + self.runOnFsNodeAndAscendantNodes(self, lambda fsNode: fsNode.setNodeAsPublic()) def isParentGitRepo(self) -> bool: if self.parent is None: return DirNode.isPwdGitRepo() return self.parent.isInGitRepo + @staticmethod + def runOnFsNodeAndAscendantNodes( + startingNode: "FsNode", fn: Callable[["FsNode"], None] + ) -> None: + def walk(node: FsNode) -> None: + fn(node) + if node.parent is not None: + walk(node.parent) + + walk(startingNode) + class FileNode(FsNode): @staticmethod @@ -153,8 +165,8 @@ def __init__( self, parent: Optional["DirNode"], dirPath: str, - # shouldIgnore(name: str, isDir: bool) -> bool - shouldIgnore: Callable[[str, bool], bool], + # shouldIgnore(name: str, parentPath: str, isDir: bool) -> bool + shouldIgnore: Callable[[str, str, bool], bool], ) -> None: _, subDirNames, fileNames = next(os.walk(dirPath)) dirPath = "" if dirPath == os.curdir else dirPath @@ -163,12 +175,12 @@ def __init__( self.files: List[FileNode] = [ FileNode.construct(self, dirPath, fileName) for fileName in fileNames - if not shouldIgnore(fileName, False) + if not shouldIgnore(fileName, self.fullPath, False) ] self.subDirs: List[DirNode] = [ DirNode(self, os.path.join(dirPath, subDirName), shouldIgnore) for subDirName in subDirNames - if not shouldIgnore(subDirName, True) + if not shouldIgnore(subDirName, self.fullPath, True) ] def getRectifiedName(self) -> str: @@ -409,31 +421,27 @@ def __repr__(self) -> str: ) +@dataclass +class FsCrawlResult: + rootDir: DirNode + nameRegistry: NameRegistry + + class Fs: configFileName: str = "__config__.py" + ignoreAbsPaths: List[str] = [] @staticmethod def readfile(file_path: str) -> str: with open(file_path, "r", encoding="utf-8") as someFile: return someFile.read() - @staticmethod - def runOnFsNodeAndAscendantNodes( - startingNode: FsNode, fn: Callable[[FsNode], None] - ) -> None: - def walk(node: FsNode) -> None: - fn(node) - if node.parent is not None: - walk(node.parent) - - walk(startingNode) - @staticmethod def isHidden(name: str) -> bool: return name.startswith(".") @staticmethod - def defaultShouldIgnore(name: str, isDir: bool) -> bool: + def defaultShouldIgnore(name: str, parentPath: str, isDir: bool) -> bool: # pylint: disable=unused-argument if Fs.isHidden(name): return True @@ -444,6 +452,10 @@ def defaultShouldIgnore(name: str, isDir: bool) -> bool: return True if name != Fs.configFileName and fileExt == ".py": return True + fullPath = os.path.abspath(os.path.join(parentPath, name)) + for ignoreAbsPath in Fs.ignoreAbsPaths: + if fullPath in ignoreAbsPath: + return True return False @staticmethod @@ -454,10 +466,10 @@ def defaultSkipForRegistry(name: str) -> bool: @staticmethod def crawl( - # Signature -- shouldIgnore(name: str, isDir: bool) -> bool - shouldIgnore: Callable[[str, bool], bool] = defaultShouldIgnore, + # Signature -- shouldIgnore(name: str, parentPath: str, isDir: bool) -> bool + shouldIgnore: Callable[[str, str, bool], bool] = defaultShouldIgnore, skipForRegistry: Callable[[str], bool] = defaultSkipForRegistry, - ) -> Tuple[DirNode, NameRegistry]: + ) -> FsCrawlResult: """ Crawl the current directory. Construct & return an FsNode tree and NameRegistry. """ @@ -466,9 +478,4 @@ def crawl( rootDir: DirNode = DirNode(None, dirPath, shouldIgnore) nameRegistry = NameRegistry(rootDir, skipForRegistry) - return rootDir, nameRegistry - - def __init__(self) -> None: - rootDir, nameRegistry = Fs.crawl() - self.rootDir: DirNode = rootDir - self.nameRegistry: NameRegistry = nameRegistry + return FsCrawlResult(rootDir, nameRegistry) diff --git a/build_test.sh b/build_test.sh index dd32d43..efadfe1 100755 --- a/build_test.sh +++ b/build_test.sh @@ -1 +1 @@ -python -m alteza --content test_content --output test_output --clear_output_dir --watch +python -m alteza --content test_content --output test_output --clear_output_dir --watch --ignore test_content/dirA