feat: Add package structure for TheChart, implement entry points for console scripts and module execution

This commit is contained in:
William Valentin
2025-08-08 18:33:51 -07:00
parent 583f5d793a
commit bd598d63f9
6 changed files with 122 additions and 5 deletions
+4 -1
View File
@@ -8,6 +8,8 @@ make install
# Run the application # Run the application
make run make run
# Or use the package entry point
python -m thechart
# Run tests (consolidated test suite) # Run tests (consolidated test suite)
make test make test
@@ -96,8 +98,9 @@ python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r requirements.txt pip install -r requirements.txt
# Run the application # Run the application (any of the following)
python src/main.py python src/main.py
python -m thechart
``` ```
## 🧪 Testing ## 🧪 Testing
+15
View File
@@ -15,6 +15,9 @@ dependencies = [
"ttkthemes>=3.2.2", "ttkthemes>=3.2.2",
] ]
[project.scripts]
thechart = "thechart.__main__:main"
[dependency-groups] [dependency-groups]
dev = [ dev = [
"pre-commit>=4.2.0", "pre-commit>=4.2.0",
@@ -104,3 +107,15 @@ indent-style = "space" # Use spaces for indentation
[tool.ruff.lint.pycodestyle] [tool.ruff.lint.pycodestyle]
max-line-length = 88 max-line-length = 88
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
package-dir = { "" = "src" }
[tool.setuptools.packages.find]
where = ["src"]
include = ["thechart*"]
exclude = ["tests*"]
+11 -2
View File
@@ -1675,7 +1675,16 @@ Use Ctrl+S to save entries and Ctrl+Q to quit."""
logger.error(f"Error updating tree efficiently: {e}") logger.error(f"Error updating tree efficiently: {e}")
if __name__ == "__main__": def run() -> None:
"""Start the TheChart Tkinter application.
Provided to support `python -m thechart` and a console-script entry point
without changing import-time behavior (tests patch symbols on `main`).
"""
root: tk.Tk = tk.Tk() root: tk.Tk = tk.Tk()
app: MedTrackerApp = MedTrackerApp(root) _ = MedTrackerApp(root)
root.mainloop() root.mainloop()
if __name__ == "__main__":
run()
+26
View File
@@ -0,0 +1,26 @@
"""TheChart package.
This package provides the main application and components for the
TheChart (medication tracker) desktop app.
Notes
-----
- This package layer is introduced to follow Python packaging best
practices while keeping backward compatibility with existing
imports used in tests (e.g., ``src.*``). The original modules under
``src/`` remain available; this package enables ``python -m thechart``
and console-script usage.
"""
from __future__ import annotations
from importlib import metadata as _metadata
try: # Prefer installed package version if available
__version__ = _metadata.version("thechart")
except Exception: # Fallback in editable/dev mode
__version__ = "0.0.0.dev"
__all__ = [
"__version__",
]
+64
View File
@@ -0,0 +1,64 @@
"""Module entry-point for `python -m thechart` and console scripts.
This dynamically locates and runs the existing application start-up code
without imposing a hard packaging dependency on the development layout.
"""
from __future__ import annotations
import importlib
import os
import tkinter as tk
def _load_main_module():
"""Load the existing main module from common locations.
Tries in order:
- importlib.import_module("src.main")
- importlib.import_module("main")
- Load from file path next to this package (.. / main.py)
"""
try:
return importlib.import_module("src.main")
except Exception:
pass
try:
return importlib.import_module("main")
except Exception:
pass
# File-based fallback for src layout in editable/dev mode
try:
from importlib.machinery import SourceFileLoader
here = os.path.dirname(__file__)
candidate = os.path.abspath(os.path.join(here, os.pardir, "main.py"))
if os.path.exists(candidate):
return SourceFileLoader("main", candidate).load_module() # type: ignore[deprecated-import]
except Exception:
pass
raise ImportError("Could not locate application main module")
def main() -> None:
"""Start the TheChart application."""
mod = _load_main_module()
# Prefer a run() entry if available
try:
run_fn = mod.run # type: ignore[attr-defined]
except AttributeError:
run_fn = None # type: ignore[assignment]
if callable(run_fn): # type: ignore[truthy-function]
run_fn()
return
# Very old fallback: directly instantiate if run() is absent
root = tk.Tk()
app_cls = mod.MedTrackerApp # type: ignore[attr-defined]
_ = app_cls(root)
root.mainloop()
if __name__ == "__main__": # pragma: no cover - simple dispatcher
main()
Generated
+2 -2
View File
@@ -1,5 +1,5 @@
version = 1 version = 1
revision = 3 revision = 2
requires-python = ">=3.13" requires-python = ">=3.13"
[[package]] [[package]]
@@ -758,7 +758,7 @@ wheels = [
[[package]] [[package]]
name = "thechart" name = "thechart"
version = "1.14.9" version = "1.14.9"
source = { virtual = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "colorlog" }, { name = "colorlog" },
{ name = "dotenv" }, { name = "dotenv" },