luxforge 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- foundry/__init__.py +0 -0
- foundry/colours/__init__.py +25 -0
- foundry/colours/colours.py +68 -0
- foundry/files/__init__.py +25 -0
- foundry/files/files.py +108 -0
- foundry/logger/__init__.py +27 -0
- foundry/logger/logger.py +459 -0
- foundry/menu/__init__.py +27 -0
- foundry/menu/keyhandler.py +180 -0
- foundry/menu/main_menu.py +25 -0
- foundry/menu/menu.py +698 -0
- foundry/postgres/__init__.py +8 -0
- foundry/postgres/client.py +436 -0
- foundry/postgres/db.py +125 -0
- foundry/postgres/managed.py +21 -0
- foundry/postgres/orchestrator.py +67 -0
- foundry/postgres/role.py +110 -0
- foundry/postgres/table.py +84 -0
- foundry/postgres/user.py +93 -0
- foundry/utils/passwords.py +97 -0
- luxforge-0.1.2.dist-info/METADATA +38 -0
- luxforge-0.1.2.dist-info/RECORD +24 -0
- luxforge-0.1.2.dist-info/WHEEL +5 -0
- luxforge-0.1.2.dist-info/top_level.txt +1 -0
foundry/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .colours import Colours
|
|
2
|
+
|
|
3
|
+
# Often modified metadata
|
|
4
|
+
__version__ = "1.0.0"
|
|
5
|
+
__modified__ = "2025-10-16"
|
|
6
|
+
|
|
7
|
+
__all__ = ["Colours"]
|
|
8
|
+
|
|
9
|
+
# Metadata
|
|
10
|
+
__author__ = "LuxForge"
|
|
11
|
+
__maintainer__ = "LuxForge"
|
|
12
|
+
__email__ = "lab@luxforge.dev"
|
|
13
|
+
__license__ = "MIT"
|
|
14
|
+
__status__ = "Development"
|
|
15
|
+
__copyright__ = "© 2025 LuxForge"
|
|
16
|
+
__credits__ = ["LuxForge"]
|
|
17
|
+
__description__ = "Colour utility module for Foundry tools, supporting ANSI styling, bold emphasis, and semantic log highlighting."
|
|
18
|
+
__created__ = "2025-10-16"
|
|
19
|
+
__module__ = "foundry.colours"
|
|
20
|
+
__tags__ = ["colour", "ansi", "styling", "logging", "foundry"]
|
|
21
|
+
__interface__ = "console"
|
|
22
|
+
__features__ = ["ANSI colour codes", "bold text", "semantic level mapping"]
|
|
23
|
+
__dependencies__ = ["os", "sys"]
|
|
24
|
+
__compatibility__ = ["Python 3.8+", "Foundry VTT 0.8+"]
|
|
25
|
+
__repository__ = "https://github.com/LuxForge/LuxForge-Foundry"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# !/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Colour definitions for terminal output
|
|
4
|
+
# Author: Luxforge
|
|
5
|
+
|
|
6
|
+
class Colours:
|
|
7
|
+
GRAY = "\033[90m"
|
|
8
|
+
RED = "\033[91m"
|
|
9
|
+
GREEN = "\033[92m"
|
|
10
|
+
YELLOW = "\033[93m"
|
|
11
|
+
ORANGE = "\033[38;5;208m"
|
|
12
|
+
BLUE = "\033[94m"
|
|
13
|
+
CYAN = "\033[96m"
|
|
14
|
+
MAGENTA = "\033[95m"
|
|
15
|
+
CYBERPURPLE = "\033[38;5;201m" # Neon pink
|
|
16
|
+
DEEP_MAGENTA = "\033[38;5;165m" # Deep magenta
|
|
17
|
+
BUBBLEGUM = "\033[38;5;213m" # Bubblegum
|
|
18
|
+
RASPBERRY = "\033[38;5;200m" # Raspberry
|
|
19
|
+
RESET = "\033[0m"
|
|
20
|
+
BOLD = "\033[1m"
|
|
21
|
+
UNDERLINE = "\033[4m"
|
|
22
|
+
REVERSE = "\033[7m"
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def colour_text( text, colour: str = None, bold: bool = False, underline: bool = False, reverse: bool = False):
|
|
26
|
+
# cast colour to upper to match class attributes
|
|
27
|
+
if colour:
|
|
28
|
+
colour = colour.upper()
|
|
29
|
+
else:
|
|
30
|
+
colour = "RESET"
|
|
31
|
+
if not hasattr(Colours, colour) or colour == "RESET":
|
|
32
|
+
colour = Colours.RESET
|
|
33
|
+
else:
|
|
34
|
+
colour = getattr(Colours, colour)
|
|
35
|
+
styles = [colour]
|
|
36
|
+
if bold:
|
|
37
|
+
styles.append(Colours.BOLD)
|
|
38
|
+
if underline:
|
|
39
|
+
styles.append(Colours.UNDERLINE)
|
|
40
|
+
if reverse:
|
|
41
|
+
styles.append(Colours.REVERSE)
|
|
42
|
+
return f"{''.join(styles)}{text}{Colours.RESET}"
|
|
43
|
+
@staticmethod
|
|
44
|
+
def test_all():
|
|
45
|
+
for colour in ["GRAY", "RED", "GREEN", "YELLOW", "ORANGE", "BLUE", "CYAN", "MAGENTA"]:
|
|
46
|
+
print(Colours.colour_text(f"This is {colour.lower()} text", colour))
|
|
47
|
+
print(Colours.colour_text("This is bold text", bold=True))
|
|
48
|
+
print(Colours.colour_text("This is underlined text", underline=True))
|
|
49
|
+
print(Colours.colour_text("This is reversed text", reverse=True))
|
|
50
|
+
print(Colours.colour_text("This is normal text"))
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def style(colour: str = None, bold=False, underline=False, reverse=False):
|
|
54
|
+
colour = colour.upper() if colour else "RESET"
|
|
55
|
+
if not hasattr(Colours, colour) or colour == "RESET":
|
|
56
|
+
colour_code = Colours.RESET
|
|
57
|
+
else:
|
|
58
|
+
colour_code = getattr(Colours, colour)
|
|
59
|
+
styles = [colour_code]
|
|
60
|
+
if bold: styles.append(Colours.BOLD)
|
|
61
|
+
if underline: styles.append(Colours.UNDERLINE)
|
|
62
|
+
if reverse: styles.append(Colours.REVERSE)
|
|
63
|
+
return ''.join(styles)
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
Colours.test_all()
|
|
67
|
+
for code in [201, 200, 213, 165]:
|
|
68
|
+
print(f"\033[38;5;{code}mCyberpunk Purple {code}\033[0m")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .files import find_all_files, read_file, write_file
|
|
2
|
+
|
|
3
|
+
# Often modified metadata
|
|
4
|
+
__version__ = "1.0.0"
|
|
5
|
+
__modified__ = "2025-10-16"
|
|
6
|
+
|
|
7
|
+
__all__ = ["find_all_files", "read_file", "write_file"]
|
|
8
|
+
|
|
9
|
+
# Metadata
|
|
10
|
+
__author__ = "LuxForge"
|
|
11
|
+
__maintainer__ = "LuxForge"
|
|
12
|
+
__email__ = "lab@luxforge.dev"
|
|
13
|
+
__license__ = "MIT"
|
|
14
|
+
__status__ = "Development"
|
|
15
|
+
__copyright__ = "© 2025 LuxForge"
|
|
16
|
+
__credits__ = ["LuxForge"]
|
|
17
|
+
__description__ = "Modular file I/O handler for Foundry tools, supporting read, write, append, export, and structured archival operations."
|
|
18
|
+
__created__ = "2025-10-16"
|
|
19
|
+
__module__ = "foundry.files"
|
|
20
|
+
__tags__ = ["file", "io", "read", "write", "append", "export", "foundry"]
|
|
21
|
+
__interface__ = "filesystem,stream"
|
|
22
|
+
__features__ = ["read", "write", "append", "export", "structured archival"]
|
|
23
|
+
__dependencies__ = ["os", "pathlib", "datetime", "json", "csv"]
|
|
24
|
+
__compatibility__ = ["Python 3.8+", "Foundry VTT 0.8+"]
|
|
25
|
+
__repository__ = "https://github.com/LuxForge/LuxForge-Foundry"
|
foundry/files/files.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# files.py
|
|
4
|
+
# Author: Luxforge
|
|
5
|
+
# File and directory utilities
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from foundry.logger.logger import logger
|
|
11
|
+
from typing import List
|
|
12
|
+
|
|
13
|
+
def write_file(filepath, data, retries=5, timeout=2, encoding="utf-8"):
|
|
14
|
+
"""
|
|
15
|
+
Write data to a file with retry logic and timeout between attempts.
|
|
16
|
+
|
|
17
|
+
ARGS:
|
|
18
|
+
filepath (str or Path): Destination file path
|
|
19
|
+
data (str): Data to write
|
|
20
|
+
retries (int): Number of retry attempts
|
|
21
|
+
timeout (int or float): Seconds to wait between retries
|
|
22
|
+
encoding (str): File encoding (default: utf-8)
|
|
23
|
+
|
|
24
|
+
RETURNS:
|
|
25
|
+
bool: True if write succeeded, False otherwise
|
|
26
|
+
"""
|
|
27
|
+
# VALIDATE INPUTS
|
|
28
|
+
if retries == 0:
|
|
29
|
+
logger.warning(f"Retries set to 0, no further attempts will be made to write the file: {filepath}")
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
# Ensure filepath is a Path object
|
|
33
|
+
filepath = Path(filepath)
|
|
34
|
+
|
|
35
|
+
# Ensure the parent directory exists
|
|
36
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
# Recursively attempt to write the file
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
with open(filepath, "w", encoding=encoding) as f:
|
|
42
|
+
f.write(data)
|
|
43
|
+
except Exception as e:
|
|
44
|
+
|
|
45
|
+
# Log the error and retry
|
|
46
|
+
logger.error(f"Failed to write to {filepath}. Retries pending:{retries}. Error: {e}")
|
|
47
|
+
time.sleep(timeout)
|
|
48
|
+
|
|
49
|
+
return write_file(filepath, data, retries - 1, timeout, encoding)
|
|
50
|
+
logger.info(f"Successfully wrote to {filepath}")
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
def read_file(filepath: str | Path, encoding: str="utf-8", retries: int=5, timeout: int=2) -> str | None:
|
|
54
|
+
"""
|
|
55
|
+
Read data from a file.
|
|
56
|
+
|
|
57
|
+
ARGS:
|
|
58
|
+
filepath (str or Path): Source file path
|
|
59
|
+
encoding (str): File encoding (default: utf-8)
|
|
60
|
+
retries (int): Number of retry attempts (default: 5)
|
|
61
|
+
timeout (int or float): Seconds to wait between retries (default: 2)
|
|
62
|
+
RETURNS:
|
|
63
|
+
str: File contents, or None if read failed
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
# VALIDATE INPUTS
|
|
67
|
+
|
|
68
|
+
if retries == 0:
|
|
69
|
+
logger.warning(f"Out of retries, no further attempts will be made to read the file: {filepath}")
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
# Ensure filepath is a Path object
|
|
73
|
+
filepath = Path(filepath)
|
|
74
|
+
if not filepath.exists():
|
|
75
|
+
logger.error(f"File does not exist: {filepath}")
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
# Recursively attempt to read the file
|
|
79
|
+
try:
|
|
80
|
+
with open(filepath, "r", encoding=encoding) as f:
|
|
81
|
+
return f.read()
|
|
82
|
+
except Exception as e:
|
|
83
|
+
|
|
84
|
+
logger.error(f"Failed to read from {filepath}. Error: {e}")
|
|
85
|
+
time.sleep(timeout)
|
|
86
|
+
return read_file(filepath, encoding, retries - 1, timeout)
|
|
87
|
+
|
|
88
|
+
logger.info(f"Successfully read from {filepath}")
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
def find_all_files(directory: str | Path, pattern: str="*") -> List[Path]:
|
|
92
|
+
"""
|
|
93
|
+
Recursively find all files in a directory matching a pattern.
|
|
94
|
+
|
|
95
|
+
ARGS:
|
|
96
|
+
directory (str or Path): Root directory to search
|
|
97
|
+
pattern (str): Glob pattern to match files (default: "*")
|
|
98
|
+
|
|
99
|
+
RETURNS:
|
|
100
|
+
list of Path: List of matching file paths
|
|
101
|
+
"""
|
|
102
|
+
directory = Path(directory)
|
|
103
|
+
if not directory.exists() or not directory.is_dir():
|
|
104
|
+
logger.error(f"Directory does not exist or is not a directory: {directory}")
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
logger.info(f"Searching for files in {directory} matching pattern '{pattern}'")
|
|
108
|
+
return [p.resolve() for p in directory.rglob(pattern)]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .logger import BoundLogger, Logger, format_utc, logger, utc_now
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# Often modified metadata
|
|
5
|
+
__version__ = "1.0.0"
|
|
6
|
+
__modified__ = "2025-10-16"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__ = ["BoundLogger", "Logger", "format_utc", "logger", "utc_now"]
|
|
10
|
+
|
|
11
|
+
# Metadata
|
|
12
|
+
__author__ = "LuxForge"
|
|
13
|
+
__maintainer__ = "LuxForge"
|
|
14
|
+
__email__ = "lab@luxforge.dev"
|
|
15
|
+
__license__ = "MIT"
|
|
16
|
+
__status__ = "Development"
|
|
17
|
+
__copyright__ = "© 2025 LuxForge"
|
|
18
|
+
__credits__ = ["LuxForge"]
|
|
19
|
+
__description__ = "Audit-grade logging module for Foundry tools, supporting timestamped output to console, file, and (eventually) database and API endpoints."
|
|
20
|
+
__created__ = "2025-10-16"
|
|
21
|
+
__module__ = "foundry.logger"
|
|
22
|
+
__tags__ = ["logging", "audit", "rasputin", "foundry", "timestamp"]
|
|
23
|
+
__interface__ = "console,file,api"
|
|
24
|
+
__features__ = ["timestamped output", "file rotation", "milestone tagging"]
|
|
25
|
+
__dependencies__ = ["os", "logging", "logging.handlers", "datetime", "threading"]
|
|
26
|
+
__compatibility__ = ["Python 3.8+", "Foundry VTT 0.8+"]
|
|
27
|
+
__repository__ = "https://github.com/LuxForge/LuxForge-Foundry"
|