aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generat3d/__init__.py28
-rw-r--r--generat3d/config.py56
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/test_config.py81
4 files changed, 165 insertions, 0 deletions
diff --git a/generat3d/__init__.py b/generat3d/__init__.py
new file mode 100644
index 0000000..917b0c7
--- /dev/null
+++ b/generat3d/__init__.py
@@ -0,0 +1,28 @@
+# generat3D - evaluate Daz DSON scenes and write .dbz files
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import sys
+
+__version__ = "0.1.0"
+
+
+class Error(Exception):
+ """Fatal problem. The CLI prints it and exits 1."""
+
+
+def warn(msg):
+ print(f"generat3d: warning: {msg}", file=sys.stderr)
diff --git a/generat3d/config.py b/generat3d/config.py
new file mode 100644
index 0000000..9ea7266
--- /dev/null
+++ b/generat3d/config.py
@@ -0,0 +1,56 @@
+# generat3D - evaluate Daz DSON scenes and write .dbz files
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import os
+import tomllib
+from pathlib import Path
+
+from . import Error
+
+KEYS = {"library_roots"}
+
+
+def config_path():
+ base = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser("~/.config")
+ return Path(base) / "generat3d" / "config.toml"
+
+
+def library_roots(cli_roots, path=None):
+ """Library roots from -L if given (replacing the config), else from the config file."""
+ path = Path(path) if path else config_path()
+ roots = list(cli_roots)
+ if not roots and path.exists():
+ try:
+ with open(path, "rb") as f:
+ data = tomllib.load(f)
+ except tomllib.TOMLDecodeError as e:
+ raise Error(f"{path}: {e}") from None
+ unknown = sorted(set(data) - KEYS)
+ if unknown:
+ raise Error(f"{path}: unknown keys: {', '.join(unknown)}")
+ roots = data.get("library_roots", [])
+ if not isinstance(roots, list) or not all(isinstance(r, str) for r in roots):
+ raise Error(f"{path}: library_roots must be a list of strings")
+ if not roots:
+ raise Error(f"no library roots: pass -L or set library_roots in {path}")
+ out = []
+ for r in roots:
+ p = Path(r).expanduser()
+ if not p.is_dir():
+ raise Error(f"library root not found: {p}")
+ out.append(p)
+ return out
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/test_config.py b/tests/test_config.py
new file mode 100644
index 0000000..d1ad0d2
--- /dev/null
+++ b/tests/test_config.py
@@ -0,0 +1,81 @@
+# generat3D - evaluate Daz DSON scenes and write .dbz files
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import os
+import tempfile
+import unittest
+from pathlib import Path
+
+from generat3d import Error
+from generat3d.config import config_path, library_roots
+
+
+class ConfigTest(unittest.TestCase):
+ def setUp(self):
+ self.tmp = tempfile.TemporaryDirectory()
+ self.dir = Path(self.tmp.name)
+ self.lib = self.dir / "lib"
+ self.lib.mkdir()
+ self.cfg = self.dir / "config.toml"
+
+ def tearDown(self):
+ self.tmp.cleanup()
+
+ def write(self, text):
+ self.cfg.write_text(text)
+
+ def test_cli_roots_win_over_config(self):
+ self.write('library_roots = ["/nonexistent"]\n')
+ self.assertEqual(library_roots([str(self.lib)], self.cfg), [self.lib])
+
+ def test_roots_from_config(self):
+ self.write(f'library_roots = ["{self.lib}"]\n')
+ self.assertEqual(library_roots([], self.cfg), [self.lib])
+
+ def test_no_roots_anywhere(self):
+ with self.assertRaisesRegex(Error, "no library roots"):
+ library_roots([], self.cfg)
+
+ def test_missing_root_is_fatal(self):
+ with self.assertRaisesRegex(Error, "library root not found"):
+ library_roots([str(self.dir / "nope")], self.cfg)
+
+ def test_unknown_key_is_fatal(self):
+ self.write(f'library_roots = ["{self.lib}"]\nlibary = 1\n')
+ with self.assertRaisesRegex(Error, "unknown keys: libary"):
+ library_roots([], self.cfg)
+
+ def test_bad_type_is_fatal(self):
+ self.write('library_roots = "/x"\n')
+ with self.assertRaisesRegex(Error, "list of strings"):
+ library_roots([], self.cfg)
+
+ def test_bad_toml_is_fatal(self):
+ self.write("library_roots = [\n")
+ with self.assertRaisesRegex(Error, "config.toml"):
+ library_roots([], self.cfg)
+
+ def test_xdg_config_home(self):
+ old = os.environ.get("XDG_CONFIG_HOME")
+ os.environ["XDG_CONFIG_HOME"] = "/tmp/xdg"
+ try:
+ self.assertEqual(config_path(), Path("/tmp/xdg/generat3d/config.toml"))
+ finally:
+ if old is None:
+ del os.environ["XDG_CONFIG_HOME"]
+ else:
+ os.environ["XDG_CONFIG_HOME"] = old