7.2: directory configs can live outside their directories

DirConfDir keeps every directory's config (and history file) in one folder,
and [DirConfs] names a file for single directories, like SaraTag's Lexicons
and Records. Both are optional; without them configs stay inside the
directories as before. Clone now copies straight to the new name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
sarahandClaude Opus 5.5 committed 2026-10-08 18:09:49 +02:00
1 parent aef32f6f3b
commit 4389a1c76e
5 files changed
+97 -53

No files matched your search

+80 -46
View File
@@ -6,7 +6,7 @@ import shutil
import time
import toml
# Version = "7.1"
# Version = "7.2"
ConfPath = "./GlobalConf.toml"
@@ -189,10 +189,69 @@ def Parse(
return String
def ConfFolder(PathText):
# Relative paths in GlobalConf.toml (DirConfDir, [DirConfs]) count from GlobalConf.toml's own folder.
PathText = Parse(String = PathText)
if os.path.isabs(PathText):
return os.path.normpath(PathText)
return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(ConfPath)), PathText))
def FlatName(Path):
# A directory's name for files kept outside it: its path below RootDir (or its whole path), "/" turned into "_".
# E.G: "%RootDir%/Pictures/Cats" becomes "Pictures_Cats".
Root = Parse(String = ConfDirs.get("RootDir", "")).rstrip("/")
Path = Path.rstrip("/")
if Root and Path.startswith(Root + "/"):
Relative = Path[len(Root) + 1:]
else:
Relative = Path.strip("/") or os.path.basename(Path)
return Relative.replace("/", "_")
def CentralConf(Path, Kind):
# Since 7.2, a directory's config can live outside it, like SaraTag's Lexicons:
# [DirConfs] names a file per directory ("dir" = "file", or "dir" = { In = "file", Out = "file" }),
# and DirConfDir holds the configs of every directory [DirConfs] doesn't name. None means inside the directory.
for Key, Value in (Conf.get("DirConfs") or {}).items():
if Parse(String = Key).rstrip("/") == Path.rstrip("/"):
if isinstance(Value, dict):
Value = Value.get(Kind)
if Value:
return ConfFolder(Value)
if ConfDirs.get("DirConfDir"):
NameKey = "InDirConfName" if Kind == "In" else "OutDirConfName"
Name = Parse(String = ConfNames[NameKey], Parent = FlatName(Path)).lstrip(".")
return f"{ConfFolder(ConfDirs['DirConfDir'])}/{Name}.toml"
return None
def DirConfPath(Path, Kind):
# The config of an input ("In") or output ("Out") directory.
Central = CentralConf(Path, Kind)
if Central:
return Central
NameKey = "InDirConfName" if Kind == "In" else "OutDirConfName"
return f"{Path}/{Parse(String = ConfNames[NameKey], Path = ConfPath, Parent = os.path.basename(Path))}.toml"
def HistoryPath(Path):
# The history file of a directory that sorts into itself. It goes next to the directory's config
# when that's kept outside the directory.
Central = CentralConf(Path, "In") or CentralConf(Path, "Out")
if Central:
Name = Parse(String = ConfNames["FileHistoryName"], Parent = FlatName(Path)).lstrip(".")
return f"{os.path.dirname(Central)}/{Name}.toml"
return f"{Path}/{Parse(String = ConfNames['FileHistoryName'], Parent = os.path.basename(Path))}.toml"
def Clone(Source, Destination, NewName, Delete = False):
if (os.path.dirname(Source) == Destination):
FileHistoryPath = f"{Destination}/{Parse(ConfNames["FileHistoryName"], Parent = os.path.basename(Destination))}.toml"
print("FileHistoryPath:",FileHistoryPath)
FileHistoryPath = HistoryPath(Destination)
if not os.path.exists(FileHistoryPath):
with open(FileHistoryPath, "w", encoding = "utf-8") as HistoryFile:
@@ -208,10 +267,9 @@ def Clone(Source, Destination, NewName, Delete = False):
return
Dir(Destination, Output = False, CopyConf = False)
shutil.copy2(Source, Destination)
BaseName = os.path.basename(Source)
shutil.move(os.path.join(Destination, BaseName), os.path.join(Destination, NewName))
# Straight to the new name: copying under the old name first could land on a file that's already
# there (E.G. a template, when DirConfDir is GlobalConf.toml's own folder).
shutil.copy2(Source, os.path.join(Destination, NewName))
if Delete:
os.remove(Source)
@@ -222,39 +280,16 @@ def Dir(Path, Output = True, CopyConf = "OutputDirConf.toml"):
os.makedirs(Path, exist_ok=True)
if CopyConf:
if "Out" in CopyConf:
NewDirConf = f"{Parse(String = ConfNames['OutDirConfName'], Path = ConfPath, Parent = ParentName)}.toml"
for Kind in ("Out", "In"):
if Kind not in CopyConf:
continue
if Conf["OverwriteDirConf"] == 1 or not os.path.exists(os.path.join(f"{Path}/{NewDirConf}")):
Clone(CopyConf, Path, NewDirConf)
UpdateConf(
f"{Path}/{Parse(String = ConfNames['OutDirConfName'], Path = ConfPath, Parent = ParentName)}.toml",
"ParentDir",
ParentName,
)
DirConf = DirConfPath(Path, Kind)
UpdateConf(
f"{Path}/{Parse(String = ConfNames['OutDirConfName'], Path = ConfPath, Parent = ParentName)}.toml",
"Title",
f"{ParentName} Config",
)
if "In" in CopyConf:
NewDirConf = f"{Parse(String = ConfNames['InDirConfName'], Path = ConfPath, Parent = ParentName)}.toml"
if Conf["OverwriteDirConf"] == 1 or not os.path.exists(os.path.join(f"{Path}/{NewDirConf}")):
Clone(CopyConf, Path, NewDirConf)
UpdateConf(
f"{Path}/{Parse(String = ConfNames['InDirConfName'], Path = ConfPath, Parent = ParentName)}.toml",
"ParentDir",
ParentName,
)
UpdateConf(
f"{Path}/{Parse(String = ConfNames['InDirConfName'], Path = ConfPath, Parent = ParentName)}.toml",
"Title",
f"{ParentName} Config",
)
if Conf["OverwriteDirConf"] == 1 or not os.path.exists(DirConf):
Clone(CopyConf, os.path.dirname(DirConf), os.path.basename(DirConf))
UpdateConf(DirConf, "ParentDir", ParentName)
UpdateConf(DirConf, "Title", f"{ParentName} Config")
if Output:
ParsedPath = Parse(String = Path)
@@ -299,8 +334,7 @@ def DecideNewPath(FilePath):
ParentName = os.path.basename(Output)
OutDirConfName = Parse(String = ConfNames["OutDirConfName"], Path = ConfPath, Parent = ParentName)
OutDirConfPath = f"{Output}/{OutDirConfName}.toml"
OutDirConfPath = DirConfPath(Output, "Out")
ValidInputDirs = GetConf("ValidInputDirs", OutDirConfPath) or []
InputDirValid = False
@@ -429,7 +463,7 @@ def Sort(FilePath):
NewDirPath = os.path.dirname(NewPath)
NewName = os.path.basename(NewPath)
if (GetConf("DeleteOrg", f"{NewDirPath}/{Parse(String = ConfNames['OutDirConfName'], Parent = (os.path.basename(NewDirPath)))}.toml") == 1):
if (GetConf("DeleteOrg", DirConfPath(NewDirPath, "Out")) == 1):
Delete = True
else:
@@ -437,7 +471,7 @@ def Sort(FilePath):
try:
Clone(FilePath, NewDirPath, NewName, Delete)
UpdateConf(f"{NewDirPath}/{Parse(String = ConfNames['OutDirConfName'], Parent = os.path.basename(NewDirPath))}.toml", "LastFile", NewName)
UpdateConf(DirConfPath(NewDirPath, "Out"), "LastFile", NewName)
TextOutput = Parse(String = ConfLog["Sorted"], VarCall = f"{FilePath} to {NewDirPath}/{NewName}")
LogWrite(TextOutput)
@@ -470,7 +504,7 @@ def Init():
Output = Parse(String = Output, Parent = os.path.basename(os.path.dirname(Output)))
Dir(Output)
OutDirConfPath = f"{Output}/{Parse(String = ConfNames["OutDirConfName"], Parent = os.path.basename(Output))}.toml"
OutDirConfPath = DirConfPath(Output, "Out")
ConfData = toml.load(OutDirConfPath)
for Item in ConfData.get("Files", []):
@@ -509,12 +543,12 @@ def Main():
if not os.path.exists(InputPath):
continue
InputDirConfPath = f"{InputPath}/{Parse(ConfNames["InDirConfName"], Parent = os.path.basename(InputPath))}.toml"
InputDirConfPath = DirConfPath(InputPath, "In")
InputDirConf = toml.load(InputDirConfPath)
for Output in ConfDirs["OutputDir"]:
OutputPath = Parse(Output)
OutDirConfPath = f"{OutputPath}/{Parse(ConfNames["OutDirConfName"], Parent = os.path.basename(OutputPath))}.toml"
OutDirConfPath = DirConfPath(OutputPath, "Out")
if len(GetConf("Unzip", OutDirConfPath)) >= 1:
ZipTypes = GetConf("Unzip", OutDirConfPath)
@@ -541,7 +575,7 @@ def Main():
if (OutputPath == InputPath):
RecurseDir = True
FileHistoryPath = f"{InputPath}/{Parse(ConfNames["FileHistoryName"], Parent = os.path.basename(InputPath))}.toml"
FileHistoryPath = HistoryPath(InputPath)
if not os.path.exists(FileHistoryPath):
with open(FileHistoryPath, "w", encoding = "utf-8") as HistoryFile: