tsmetallic 1.0.0__tar.gz
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.
- tsmetallic-1.0.0/.vscode/PythonImportHelper-v2-Completion.json +330 -0
- tsmetallic-1.0.0/LICENSE +21 -0
- tsmetallic-1.0.0/Makefile +98 -0
- tsmetallic-1.0.0/PKG-INFO +82 -0
- tsmetallic-1.0.0/README.md +72 -0
- tsmetallic-1.0.0/pyproject.toml +14 -0
- tsmetallic-1.0.0/requirements.txt +20 -0
- tsmetallic-1.0.0/tests/test_tsm_config.py +8 -0
- tsmetallic-1.0.0/tests/test_tsm_logging.py +5 -0
- tsmetallic-1.0.0/tsmetallic/__init__.py +2 -0
- tsmetallic-1.0.0/tsmetallic/config/__init__.py +29 -0
- tsmetallic-1.0.0/tsmetallic/loader/__init__.py +52 -0
- tsmetallic-1.0.0/tsmetallic/logging/__init__.py +75 -0
- tsmetallic-1.0.0/tsmetallic/register/__init__.py +1 -0
- tsmetallic-1.0.0/tsmetallic/utils/__init__.py +0 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"label": "Subnet",
|
|
4
|
+
"importPath": "ipamanager.subnet",
|
|
5
|
+
"description": "ipamanager.subnet",
|
|
6
|
+
"isExtraImport": true,
|
|
7
|
+
"detail": "ipamanager.subnet",
|
|
8
|
+
"documentation": {}
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"label": "get_term_size",
|
|
12
|
+
"importPath": "ipamanager.utils",
|
|
13
|
+
"description": "ipamanager.utils",
|
|
14
|
+
"isExtraImport": true,
|
|
15
|
+
"detail": "ipamanager.utils",
|
|
16
|
+
"documentation": {}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"label": "err_and_exit",
|
|
20
|
+
"importPath": "ipamanager.utils",
|
|
21
|
+
"description": "ipamanager.utils",
|
|
22
|
+
"isExtraImport": true,
|
|
23
|
+
"detail": "ipamanager.utils",
|
|
24
|
+
"documentation": {}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"label": "show_help",
|
|
28
|
+
"importPath": "ipamanager.utils",
|
|
29
|
+
"description": "ipamanager.utils",
|
|
30
|
+
"isExtraImport": true,
|
|
31
|
+
"detail": "ipamanager.utils",
|
|
32
|
+
"documentation": {}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"label": "err_and_exit",
|
|
36
|
+
"importPath": "ipamanager.utils",
|
|
37
|
+
"description": "ipamanager.utils",
|
|
38
|
+
"isExtraImport": true,
|
|
39
|
+
"detail": "ipamanager.utils",
|
|
40
|
+
"documentation": {}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"label": "sys",
|
|
44
|
+
"kind": 6,
|
|
45
|
+
"isExtraImport": true,
|
|
46
|
+
"importPath": "sys",
|
|
47
|
+
"description": "sys",
|
|
48
|
+
"detail": "sys",
|
|
49
|
+
"documentation": {}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"label": "json",
|
|
53
|
+
"kind": 6,
|
|
54
|
+
"isExtraImport": true,
|
|
55
|
+
"importPath": "json",
|
|
56
|
+
"description": "json",
|
|
57
|
+
"detail": "json",
|
|
58
|
+
"documentation": {}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"label": "Console",
|
|
62
|
+
"importPath": "rich.console",
|
|
63
|
+
"description": "rich.console",
|
|
64
|
+
"isExtraImport": true,
|
|
65
|
+
"detail": "rich.console",
|
|
66
|
+
"documentation": {}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"label": "Console",
|
|
70
|
+
"importPath": "rich.console",
|
|
71
|
+
"description": "rich.console",
|
|
72
|
+
"isExtraImport": true,
|
|
73
|
+
"detail": "rich.console",
|
|
74
|
+
"documentation": {}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"label": "settings",
|
|
78
|
+
"importPath": "ipamanager.config",
|
|
79
|
+
"description": "ipamanager.config",
|
|
80
|
+
"isExtraImport": true,
|
|
81
|
+
"detail": "ipamanager.config",
|
|
82
|
+
"documentation": {}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"label": "IpamDb",
|
|
86
|
+
"importPath": "ipamanager.db",
|
|
87
|
+
"description": "ipamanager.db",
|
|
88
|
+
"isExtraImport": true,
|
|
89
|
+
"detail": "ipamanager.db",
|
|
90
|
+
"documentation": {}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"label": "Report",
|
|
94
|
+
"importPath": "ipamanager.reports",
|
|
95
|
+
"description": "ipamanager.reports",
|
|
96
|
+
"isExtraImport": true,
|
|
97
|
+
"detail": "ipamanager.reports",
|
|
98
|
+
"documentation": {}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"label": "ReportRecord",
|
|
102
|
+
"importPath": "ipamanager.reports",
|
|
103
|
+
"description": "ipamanager.reports",
|
|
104
|
+
"isExtraImport": true,
|
|
105
|
+
"detail": "ipamanager.reports",
|
|
106
|
+
"documentation": {}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"label": "os",
|
|
110
|
+
"kind": 6,
|
|
111
|
+
"isExtraImport": true,
|
|
112
|
+
"importPath": "os",
|
|
113
|
+
"description": "os",
|
|
114
|
+
"detail": "os",
|
|
115
|
+
"documentation": {}
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"label": "Path",
|
|
119
|
+
"importPath": "pathlib",
|
|
120
|
+
"description": "pathlib",
|
|
121
|
+
"isExtraImport": true,
|
|
122
|
+
"detail": "pathlib",
|
|
123
|
+
"documentation": {}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"label": "sqlite3",
|
|
127
|
+
"kind": 6,
|
|
128
|
+
"isExtraImport": true,
|
|
129
|
+
"importPath": "sqlite3",
|
|
130
|
+
"description": "sqlite3",
|
|
131
|
+
"detail": "sqlite3",
|
|
132
|
+
"documentation": {}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"label": "time",
|
|
136
|
+
"kind": 6,
|
|
137
|
+
"isExtraImport": true,
|
|
138
|
+
"importPath": "time",
|
|
139
|
+
"description": "time",
|
|
140
|
+
"detail": "time",
|
|
141
|
+
"documentation": {}
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"label": "Table",
|
|
145
|
+
"importPath": "rich.table",
|
|
146
|
+
"description": "rich.table",
|
|
147
|
+
"isExtraImport": true,
|
|
148
|
+
"detail": "rich.table",
|
|
149
|
+
"documentation": {}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"label": "Panel",
|
|
153
|
+
"importPath": "rich.panel",
|
|
154
|
+
"description": "rich.panel",
|
|
155
|
+
"isExtraImport": true,
|
|
156
|
+
"detail": "rich.panel",
|
|
157
|
+
"documentation": {}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"label": "tsm.semantic_colors",
|
|
161
|
+
"kind": 6,
|
|
162
|
+
"isExtraImport": true,
|
|
163
|
+
"importPath": "tsm.semantic_colors",
|
|
164
|
+
"description": "tsm.semantic_colors",
|
|
165
|
+
"detail": "tsm.semantic_colors",
|
|
166
|
+
"documentation": {}
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"label": "AddressPool",
|
|
170
|
+
"kind": 6,
|
|
171
|
+
"importPath": "src.tsm.ipamanager.address_pool",
|
|
172
|
+
"description": "src.tsm.ipamanager.address_pool",
|
|
173
|
+
"peekOfCode": "class AddressPool:\n \"\"\"A contiguous range of IP addresses intended to provide logical\n boundaries for automated allocation and reporting.\n \"\"\"\n def list_records(self):\n \"\"\"List detail records of members of the pool.\n Returns:\n list: A list of tuples, each a detail record of a member of the\n pool.\n \"\"\"",
|
|
174
|
+
"detail": "src.tsm.ipamanager.address_pool",
|
|
175
|
+
"documentation": {}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"label": "cli_parser",
|
|
179
|
+
"kind": 2,
|
|
180
|
+
"importPath": "src.tsm.ipamanager.cli",
|
|
181
|
+
"description": "src.tsm.ipamanager.cli",
|
|
182
|
+
"peekOfCode": "def cli_parser(args=sys.argv):\n \"\"\"Handles unpacking of sys.argv.\n Args:\n args (list): List of arguments received by the module at runtime.\n Defaults to sys.argv.\n Returns:\n tuple: a subcommand, which selects the function to be called by the\n CLI, and a list of parameters to be made available to that function.\n \"\"\"\n if len(args) == 0:",
|
|
183
|
+
"detail": "src.tsm.ipamanager.cli",
|
|
184
|
+
"documentation": {}
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"label": "cli",
|
|
188
|
+
"kind": 2,
|
|
189
|
+
"importPath": "src.tsm.ipamanager.cli",
|
|
190
|
+
"description": "src.tsm.ipamanager.cli",
|
|
191
|
+
"peekOfCode": "def cli():\n \"\"\"Calls functions based on the arguments used to run the application.\"\"\"\n subcmd, params = cli_parser()\n dbpath = config[\"database\"][\"path\"]\n dbfields = config[\"database\"][\"fields\"]\n db = IpamDb(str(dbpath))\n match subcmd:\n case \"available\":\n print(db.is_unassigned(params[0]))\n case \"setup\":",
|
|
192
|
+
"detail": "src.tsm.ipamanager.cli",
|
|
193
|
+
"documentation": {}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"label": "dump_config",
|
|
197
|
+
"kind": 2,
|
|
198
|
+
"importPath": "src.tsm.ipamanager.config",
|
|
199
|
+
"description": "src.tsm.ipamanager.config",
|
|
200
|
+
"peekOfCode": "def dump_config():\n print(json.dumps(settings))",
|
|
201
|
+
"detail": "src.tsm.ipamanager.config",
|
|
202
|
+
"documentation": {}
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"label": "config_file",
|
|
206
|
+
"kind": 5,
|
|
207
|
+
"importPath": "src.tsm.ipamanager.config",
|
|
208
|
+
"description": "src.tsm.ipamanager.config",
|
|
209
|
+
"peekOfCode": "config_file = Path(os.environ.get(\"XDG_CONFIG_HOME\")) / \"ipam/settings.conf\"\nif not config_file.exists():\n err_and_exit(f\"Missing Config - {config_file}\")\nwith open(config_file, \"r\", encoding=\"utf-8\") as f:\n settings = json.load(f)\ndef dump_config():\n print(json.dumps(settings))",
|
|
210
|
+
"detail": "src.tsm.ipamanager.config",
|
|
211
|
+
"documentation": {}
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"label": "Database",
|
|
215
|
+
"kind": 6,
|
|
216
|
+
"importPath": "src.tsm.ipamanager.db",
|
|
217
|
+
"description": "src.tsm.ipamanager.db",
|
|
218
|
+
"peekOfCode": "class Database:\n \"\"\"Handles database operations for SQLite3.\n Provides methods for connecting to the database, executing SQL queries,\n and fetching results. This class is intended to be used as a base class\n for specific database functionality.\n Attributes:\n db_name (str): The name of the SQLite3 database file.\n conn (sqlite3.Connection): The database connection object.\n \"\"\"\n def __init__(self, db_name):",
|
|
219
|
+
"detail": "src.tsm.ipamanager.db",
|
|
220
|
+
"documentation": {}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"label": "IpamDb",
|
|
224
|
+
"kind": 6,
|
|
225
|
+
"importPath": "src.tsm.ipamanager.db",
|
|
226
|
+
"description": "src.tsm.ipamanager.db",
|
|
227
|
+
"peekOfCode": "class IpamDb(Database):\n \"\"\"Specialized database class for managing IP address data.\n This class extends the Database class and provides additional functionality\n for managing IP address records, such as assigning, unassigning, and\n retrieving IP addresses.\n Attributes:\n main_table (dict): A dictionary containing the structure of the IP\n address table.\n \"\"\"\n def __init__(self, db_name):",
|
|
228
|
+
"detail": "src.tsm.ipamanager.db",
|
|
229
|
+
"documentation": {}
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"label": "ReportRecord",
|
|
233
|
+
"kind": 6,
|
|
234
|
+
"importPath": "src.tsm.ipamanager.reports",
|
|
235
|
+
"description": "src.tsm.ipamanager.reports",
|
|
236
|
+
"peekOfCode": "class ReportRecord:\n def __init__(self, addr_id, address, host, pool):\n hstyle_dimmed = \"grey23\"\n hstyle_bright = \"sky_blue3\"\n pstyle = {\n \"none\": \"color(238)\",\n \"oleander\": \"color(88)\",\n \"dahlia\": \"color(91)\",\n \"hemlock\": \"color(98)\",\n \"dhcp\": \"color(141)\"",
|
|
237
|
+
"detail": "src.tsm.ipamanager.reports",
|
|
238
|
+
"documentation": {}
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"label": "Report",
|
|
242
|
+
"kind": 6,
|
|
243
|
+
"importPath": "src.tsm.ipamanager.reports",
|
|
244
|
+
"description": "src.tsm.ipamanager.reports",
|
|
245
|
+
"peekOfCode": "class Report:\n def __init__(self, name, fields, records):\n self.console = Console()\n self.name = name\n self.fields = fields\n self.records = records\n def display_table(self):\n table = Table(title=self.name,\n padding=(0, 4),\n expand=False,",
|
|
246
|
+
"detail": "src.tsm.ipamanager.reports",
|
|
247
|
+
"documentation": {}
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"label": "read_file",
|
|
251
|
+
"kind": 2,
|
|
252
|
+
"importPath": "src.tsm.ipamanager.utils",
|
|
253
|
+
"description": "src.tsm.ipamanager.utils",
|
|
254
|
+
"peekOfCode": "def read_file(filepath):\n \"\"\"Reads and returns the content of a file.\"\"\"\n try:\n with open(filepath, 'r') as file:\n return file.read()\n except FileNotFoundError:\n return None\ndef write_file(filepath, content):\n \"\"\"Writes content to a file.\"\"\"\n try:",
|
|
255
|
+
"detail": "src.tsm.ipamanager.utils",
|
|
256
|
+
"documentation": {}
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"label": "write_file",
|
|
260
|
+
"kind": 2,
|
|
261
|
+
"importPath": "src.tsm.ipamanager.utils",
|
|
262
|
+
"description": "src.tsm.ipamanager.utils",
|
|
263
|
+
"peekOfCode": "def write_file(filepath, content):\n \"\"\"Writes content to a file.\"\"\"\n try:\n with open(filepath, 'w') as file:\n file.write(content)\n return True\n except Exception as e:\n print(f\"Error writing to file: {e}\")\n return False\ndef create_directory(dirpath):",
|
|
264
|
+
"detail": "src.tsm.ipamanager.utils",
|
|
265
|
+
"documentation": {}
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"label": "create_directory",
|
|
269
|
+
"kind": 2,
|
|
270
|
+
"importPath": "src.tsm.ipamanager.utils",
|
|
271
|
+
"description": "src.tsm.ipamanager.utils",
|
|
272
|
+
"peekOfCode": "def create_directory(dirpath):\n \"\"\"Creates a directory if it doesn't exist.\"\"\"\n if not os.path.exists(dirpath):\n os.makedirs(dirpath)\n return True\n return False\ndef load_json(filepath):\n \"\"\"Loads JSON data from a file.\"\"\"\n try:\n with open(filepath, 'r') as file:",
|
|
273
|
+
"detail": "src.tsm.ipamanager.utils",
|
|
274
|
+
"documentation": {}
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"label": "load_json",
|
|
278
|
+
"kind": 2,
|
|
279
|
+
"importPath": "src.tsm.ipamanager.utils",
|
|
280
|
+
"description": "src.tsm.ipamanager.utils",
|
|
281
|
+
"peekOfCode": "def load_json(filepath):\n \"\"\"Loads JSON data from a file.\"\"\"\n try:\n with open(filepath, 'r') as file:\n return json.load(file)\n except (FileNotFoundError, json.JSONDecodeError):\n return None\ndef save_json(filepath, data, indent=4):\n \"\"\"Saves data to a JSON file.\"\"\"\n try:",
|
|
282
|
+
"detail": "src.tsm.ipamanager.utils",
|
|
283
|
+
"documentation": {}
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"label": "save_json",
|
|
287
|
+
"kind": 2,
|
|
288
|
+
"importPath": "src.tsm.ipamanager.utils",
|
|
289
|
+
"description": "src.tsm.ipamanager.utils",
|
|
290
|
+
"peekOfCode": "def save_json(filepath, data, indent=4):\n \"\"\"Saves data to a JSON file.\"\"\"\n try:\n with open(filepath, 'w') as file:\n json.dump(data, file, indent=indent)\n return True\n except Exception as e:\n print(f\"Error saving JSON to file: {e}\")\n return False\ndef get_term_size():",
|
|
291
|
+
"detail": "src.tsm.ipamanager.utils",
|
|
292
|
+
"documentation": {}
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"label": "get_term_size",
|
|
296
|
+
"kind": 2,
|
|
297
|
+
"importPath": "src.tsm.ipamanager.utils",
|
|
298
|
+
"description": "src.tsm.ipamanager.utils",
|
|
299
|
+
"peekOfCode": "def get_term_size():\n \"\"\"Return terminal dimensions (width, height)\"\"\"\n try:\n terminal_size = os.get_terminal_size()\n width = terminal_size.columns\n height = terminal_size.lines\n return width, height\n except Exception as e:\n print(f\"Error getting response from terminal: {e}\")\n return False",
|
|
300
|
+
"detail": "src.tsm.ipamanager.utils",
|
|
301
|
+
"documentation": {}
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"label": "err_and_exit",
|
|
305
|
+
"kind": 2,
|
|
306
|
+
"importPath": "src.tsm.ipamanager.utils",
|
|
307
|
+
"description": "src.tsm.ipamanager.utils",
|
|
308
|
+
"peekOfCode": "def err_and_exit(message):\n \"\"\"Print error message and exit.\n Args:\n message (string): Info to be displayed before exiting. Usually\n describes the error or reason for the exit.\n \"\"\"\n sys.exit(print(f\"\\001\\033[31m\\002Error:\\001\\033[m\\002 {message}\"))\ndef show_help():\n \"\"\"Display a help message with a list of CLI subcommands.\"\"\"\n print(\"\\nUsage: ipm SUBCOMMAND [ARG[S]]\\n\")",
|
|
309
|
+
"detail": "src.tsm.ipamanager.utils",
|
|
310
|
+
"documentation": {}
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"label": "show_help",
|
|
314
|
+
"kind": 2,
|
|
315
|
+
"importPath": "src.tsm.ipamanager.utils",
|
|
316
|
+
"description": "src.tsm.ipamanager.utils",
|
|
317
|
+
"peekOfCode": "def show_help():\n \"\"\"Display a help message with a list of CLI subcommands.\"\"\"\n print(\"\\nUsage: ipm SUBCOMMAND [ARG[S]]\\n\")\n print(\"Subcommand:\")\n print(\" setup - Initialize database (no assignments)\")\n print(\" assign - Assign ADDRESS to HOST\")\n print(\" next - Assign next available IP in POOL to HOST\")\n print(\" show - Print details (host, pool) for ADDRESS\")\n print(\" list - Show all IPs, host assignments and pools\")\n print(\" help - Display this message\")",
|
|
318
|
+
"detail": "src.tsm.ipamanager.utils",
|
|
319
|
+
"documentation": {}
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"label": "scolor",
|
|
323
|
+
"kind": 5,
|
|
324
|
+
"importPath": "app",
|
|
325
|
+
"description": "app",
|
|
326
|
+
"peekOfCode": "scolor = sc.SemanticColor(\"external\")\nprint(scolor)",
|
|
327
|
+
"detail": "app",
|
|
328
|
+
"documentation": {}
|
|
329
|
+
}
|
|
330
|
+
]
|
tsmetallic-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 tsharley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
.DELETE_ON_ERROR:
|
|
2
|
+
.ONESHELL:
|
|
3
|
+
.SHELLFLAGS := -eu -o pipefail -c
|
|
4
|
+
MAKEFLAGS += --warn-undefined-variables
|
|
5
|
+
MAKEFLAGS += --no-builtin-rules
|
|
6
|
+
SHELL := bash
|
|
7
|
+
|
|
8
|
+
.PHONY: help tree init run view clean debug build install config
|
|
9
|
+
|
|
10
|
+
.DEFAULT_GOAL := help
|
|
11
|
+
|
|
12
|
+
VENV = .venv
|
|
13
|
+
ACTIVATE = $(VENV)/bin/activate
|
|
14
|
+
PIP = $(VENV)/bin/pip
|
|
15
|
+
PYTHON = $(VENV)/bin/python
|
|
16
|
+
BREWPYTHON = /opt/homebrew/bin/python3
|
|
17
|
+
PYGITIGNORE = $(HOME)/Repos/gitignore/Python.gitignore
|
|
18
|
+
GIT_ORG = ts
|
|
19
|
+
GIT_REPO = $$(basename $$(pwd))
|
|
20
|
+
GIT_URL = http://git.tsmetallic.com/$(GIT_ORG)/$(GIT_REPO)
|
|
21
|
+
PACKAGE = tsm
|
|
22
|
+
PROJECT = tsm
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
help:
|
|
26
|
+
@echo -e ""
|
|
27
|
+
@echo -e " Help - Available Make Commands"
|
|
28
|
+
@echo -e " ====================================================================="
|
|
29
|
+
@echo -e ""
|
|
30
|
+
@echo -e " Setup Commands:"
|
|
31
|
+
@echo -e ""
|
|
32
|
+
@echo -e " make init : Initialize version control and link remote"
|
|
33
|
+
@echo -e " make install : Install pkg/module in local environment"
|
|
34
|
+
@echo -e ""
|
|
35
|
+
@echo -e " Dev Commands:"
|
|
36
|
+
@echo -e ""
|
|
37
|
+
@echo -e " make tree : Show project's file system excluding caches"
|
|
38
|
+
@echo -e " make view : Display a snapshot of each database table"
|
|
39
|
+
@echo -e " make config : Display runtime configuration"
|
|
40
|
+
@echo -e " make debug : Toggle the configuration option for debug mode"
|
|
41
|
+
@echo -e ""
|
|
42
|
+
@echo -e " Test Commands:"
|
|
43
|
+
@echo -e ""
|
|
44
|
+
@echo -e " make test : Execute full test suite"
|
|
45
|
+
@echo -e " make utest : Execute unit tests"
|
|
46
|
+
@echo -e " make itest : Execute integration tests"
|
|
47
|
+
@echo -e ""
|
|
48
|
+
@echo -e " Build Commands:"
|
|
49
|
+
@echo -e ""
|
|
50
|
+
@echo -e " make build : Compile project to wheel distribution"
|
|
51
|
+
@echo -e " make clean : Remove cache files and build artifacts"
|
|
52
|
+
@echo -e ""
|
|
53
|
+
|
|
54
|
+
tree:
|
|
55
|
+
@tree -I "*.egg-info|__pycache__"
|
|
56
|
+
|
|
57
|
+
config: config.json
|
|
58
|
+
@cat config.json | jq
|
|
59
|
+
|
|
60
|
+
debug:
|
|
61
|
+
@/Users/tjsharley/Projects/tsm/.venv/bin/tsm-tgl
|
|
62
|
+
|
|
63
|
+
view:
|
|
64
|
+
@source $(ACTIVATE) && $(PYTHON) src/heroes/view.py database.db team
|
|
65
|
+
@source $(ACTIVATE) && $(PYTHON) src/heroes/view.py database.db hero
|
|
66
|
+
|
|
67
|
+
build: $(ACTIVATE)
|
|
68
|
+
@source $(ACTIVATE) && $(PYTHON) -m build
|
|
69
|
+
|
|
70
|
+
install: $(ACTIVATE)
|
|
71
|
+
@source $(ACTIVATE) && $(PYTHON) -m pip install -e .
|
|
72
|
+
|
|
73
|
+
run: $(ACTIVATE) $(EPSCRIPT)
|
|
74
|
+
@source $(ACTIVATE) && $(EPSCRIPT)
|
|
75
|
+
|
|
76
|
+
$(ACTIVATE): requirements.txt
|
|
77
|
+
@$(BREWPYTHON) -m venv $(VENV)
|
|
78
|
+
@$(PYTHON) -m pip install --upgrade pip
|
|
79
|
+
@$(PIP) install -r requirements.txt
|
|
80
|
+
|
|
81
|
+
clean:
|
|
82
|
+
@rm -rf src/$(PKGNAME)/__pycache__
|
|
83
|
+
@rm -rf proc/out/*
|
|
84
|
+
|
|
85
|
+
requirements.txt:
|
|
86
|
+
@pip freeze --exclude-editable >> 'requirements.txt'
|
|
87
|
+
|
|
88
|
+
init: .git
|
|
89
|
+
|
|
90
|
+
.git: .gitignore
|
|
91
|
+
@git init .
|
|
92
|
+
@git add .
|
|
93
|
+
@git commit -a -m 'Initial commit'
|
|
94
|
+
@git remote add origin $(GIT_URL)
|
|
95
|
+
@git push -u origin main
|
|
96
|
+
|
|
97
|
+
.gitignore:
|
|
98
|
+
@cp $(PYGITIGNORE) .gitignore
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tsmetallic
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A collection of reusable Python utilities and personal libraries.
|
|
5
|
+
Author-email: tsmetallic <ts-metallic@proton.me>
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
|
|
10
|
+
# tsmetallic
|
|
11
|
+
> A lightweight collection of reusable Python utilities for building robust applications.
|
|
12
|
+
`tsmetallic` is a modular utility library that provides the infrastructure code commonly needed across Python projects. It brings together reusable components for logging, configuration management, data loading, registries, dispatchers, and other application building blocks into a single, consistent package.
|
|
13
|
+
Rather than being a framework, `tsmetallic` is designed to complement your existing architecture by providing small, focused utilities that solve common problems with minimal dependencies.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
```bash
|
|
17
|
+
python -m venv .venv
|
|
18
|
+
source .venv/bin/activate
|
|
19
|
+
pip install tsmetallic
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Why tsmetallic?
|
|
23
|
+
Most Python applications eventually grow a collection of shared infrastructure:
|
|
24
|
+
* Logging configuration
|
|
25
|
+
* Configuration loading
|
|
26
|
+
* File and data loaders
|
|
27
|
+
* Registry and dispatcher patterns
|
|
28
|
+
* General-purpose helper utilities
|
|
29
|
+
Instead of rewriting these components for every project, `tsm` packages them into reusable modules with a consistent API.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
* **Modular** — Import only the functionality you need.
|
|
33
|
+
* **Lightweight** — Designed to stay out of your way.
|
|
34
|
+
* **Reusable** — Components intended for use across many projects.
|
|
35
|
+
* **Practical** — Focused on solving common application problems.
|
|
36
|
+
* **Well-typed** — Modern Python with type annotations where appropriate.
|
|
37
|
+
|
|
38
|
+
## Included Modules
|
|
39
|
+
Although the library continues to evolve, it currently includes utilities such as:
|
|
40
|
+
|
|
41
|
+
| Module | Purpose |
|
|
42
|
+
| --------------------- | ------------------------------------ |
|
|
43
|
+
| `tsmetallic.logging` | Logging configuration and helpers |
|
|
44
|
+
| `tsmetallic.config` | Configuration loading and management |
|
|
45
|
+
| `tsmetallic.loaders` | Common data and file loaders |
|
|
46
|
+
| `tsmetallic.registry` | Generic registry implementations |
|
|
47
|
+
| `tsmetallic.utils` | General-purpose utility functions |
|
|
48
|
+
|
|
49
|
+
Additional modules are added as reusable functionality emerges from real-world projects.
|
|
50
|
+
|
|
51
|
+
## Example
|
|
52
|
+
```python
|
|
53
|
+
import tsmetallic.logging
|
|
54
|
+
|
|
55
|
+
logger = tsmetallic.logging.setup_logging()
|
|
56
|
+
logger.info('Logging initialized')
|
|
57
|
+
```
|
|
58
|
+
Because each module is independent, you can adopt only the pieces that fit your application.
|
|
59
|
+
|
|
60
|
+
## Design Principles
|
|
61
|
+
`tsmetallic` is built around a few simple ideas:
|
|
62
|
+
* Prefer composition over frameworks.
|
|
63
|
+
* Keep abstractions small and focused.
|
|
64
|
+
* Minimize dependencies.
|
|
65
|
+
* Write code that is easy to understand and reuse.
|
|
66
|
+
* Favor explicit behavior over hidden magic.
|
|
67
|
+
|
|
68
|
+
## Compatibility
|
|
69
|
+
* Python >= 3.12
|
|
70
|
+
* macOS
|
|
71
|
+
* Linux
|
|
72
|
+
* Windows?
|
|
73
|
+
|
|
74
|
+
## Documentation
|
|
75
|
+
Each module includes docstrings and examples. As the package grows, additional documentation and API references will be added.
|
|
76
|
+
|
|
77
|
+
## Contributing
|
|
78
|
+
Issues, bug reports, and suggestions are welcome. While `tsm` primarily serves as a personal utility library, improvements that make the package more generally useful are appreciated.
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
Released under the terms of the project's license. See `LICENSE` for details.
|
|
82
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# tsmetallic
|
|
2
|
+
> A lightweight collection of reusable Python utilities for building robust applications.
|
|
3
|
+
`tsmetallic` is a modular utility library that provides the infrastructure code commonly needed across Python projects. It brings together reusable components for logging, configuration management, data loading, registries, dispatchers, and other application building blocks into a single, consistent package.
|
|
4
|
+
Rather than being a framework, `tsmetallic` is designed to complement your existing architecture by providing small, focused utilities that solve common problems with minimal dependencies.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
```bash
|
|
8
|
+
python -m venv .venv
|
|
9
|
+
source .venv/bin/activate
|
|
10
|
+
pip install tsmetallic
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Why tsmetallic?
|
|
14
|
+
Most Python applications eventually grow a collection of shared infrastructure:
|
|
15
|
+
* Logging configuration
|
|
16
|
+
* Configuration loading
|
|
17
|
+
* File and data loaders
|
|
18
|
+
* Registry and dispatcher patterns
|
|
19
|
+
* General-purpose helper utilities
|
|
20
|
+
Instead of rewriting these components for every project, `tsm` packages them into reusable modules with a consistent API.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
* **Modular** — Import only the functionality you need.
|
|
24
|
+
* **Lightweight** — Designed to stay out of your way.
|
|
25
|
+
* **Reusable** — Components intended for use across many projects.
|
|
26
|
+
* **Practical** — Focused on solving common application problems.
|
|
27
|
+
* **Well-typed** — Modern Python with type annotations where appropriate.
|
|
28
|
+
|
|
29
|
+
## Included Modules
|
|
30
|
+
Although the library continues to evolve, it currently includes utilities such as:
|
|
31
|
+
|
|
32
|
+
| Module | Purpose |
|
|
33
|
+
| --------------------- | ------------------------------------ |
|
|
34
|
+
| `tsmetallic.logging` | Logging configuration and helpers |
|
|
35
|
+
| `tsmetallic.config` | Configuration loading and management |
|
|
36
|
+
| `tsmetallic.loaders` | Common data and file loaders |
|
|
37
|
+
| `tsmetallic.registry` | Generic registry implementations |
|
|
38
|
+
| `tsmetallic.utils` | General-purpose utility functions |
|
|
39
|
+
|
|
40
|
+
Additional modules are added as reusable functionality emerges from real-world projects.
|
|
41
|
+
|
|
42
|
+
## Example
|
|
43
|
+
```python
|
|
44
|
+
import tsmetallic.logging
|
|
45
|
+
|
|
46
|
+
logger = tsmetallic.logging.setup_logging()
|
|
47
|
+
logger.info('Logging initialized')
|
|
48
|
+
```
|
|
49
|
+
Because each module is independent, you can adopt only the pieces that fit your application.
|
|
50
|
+
|
|
51
|
+
## Design Principles
|
|
52
|
+
`tsmetallic` is built around a few simple ideas:
|
|
53
|
+
* Prefer composition over frameworks.
|
|
54
|
+
* Keep abstractions small and focused.
|
|
55
|
+
* Minimize dependencies.
|
|
56
|
+
* Write code that is easy to understand and reuse.
|
|
57
|
+
* Favor explicit behavior over hidden magic.
|
|
58
|
+
|
|
59
|
+
## Compatibility
|
|
60
|
+
* Python >= 3.12
|
|
61
|
+
* macOS
|
|
62
|
+
* Linux
|
|
63
|
+
* Windows?
|
|
64
|
+
|
|
65
|
+
## Documentation
|
|
66
|
+
Each module includes docstrings and examples. As the package grows, additional documentation and API references will be added.
|
|
67
|
+
|
|
68
|
+
## Contributing
|
|
69
|
+
Issues, bug reports, and suggestions are welcome. While `tsm` primarily serves as a personal utility library, improvements that make the package more generally useful are appreciated.
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
Released under the terms of the project's license. See `LICENSE` for details.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["flit_core >=3.11,<4"]
|
|
3
|
+
build-backend = "flit_core.buildapi"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tsmetallic"
|
|
7
|
+
authors = [{name = "tsmetallic", email = "ts-metallic@proton.me"}]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICENSE"]
|
|
11
|
+
dynamic = ["version", "description"]
|
|
12
|
+
|
|
13
|
+
# [tool.flit.packages.find]
|
|
14
|
+
# where = ["src"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
attrs==26.1.0
|
|
2
|
+
build==1.3.0
|
|
3
|
+
cyclopts==4.21.1
|
|
4
|
+
docstring_parser==0.18.0
|
|
5
|
+
iniconfig==2.3.0
|
|
6
|
+
markdown-it-py==4.0.0
|
|
7
|
+
mdurl==0.1.2
|
|
8
|
+
packaging==25.0
|
|
9
|
+
pluggy==1.6.0
|
|
10
|
+
prompt_toolkit==3.0.52
|
|
11
|
+
Pygments==2.19.2
|
|
12
|
+
pyproject_hooks==1.2.0
|
|
13
|
+
pytest==9.1.1
|
|
14
|
+
python-json-logger==4.1.0
|
|
15
|
+
PyYAML==6.0.3
|
|
16
|
+
questionary==2.1.1
|
|
17
|
+
rich==14.1.0
|
|
18
|
+
rich-rst==2.1.0
|
|
19
|
+
setuptools==80.9.0
|
|
20
|
+
wcwidth==0.2.14
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any
|
|
3
|
+
import tsm.loader
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Config:
|
|
7
|
+
|
|
8
|
+
paths = ['.', './config']
|
|
9
|
+
names = ['config', 'settings']
|
|
10
|
+
extensions = ['json', 'toml', 'yaml', 'yml']
|
|
11
|
+
|
|
12
|
+
def __init__(self, path: Path | None = None) -> None:
|
|
13
|
+
if path is not None and Path(path).exists():
|
|
14
|
+
self.path = Path(path)
|
|
15
|
+
else:
|
|
16
|
+
for path in self.paths:
|
|
17
|
+
for name in self.names:
|
|
18
|
+
for extension in self.extensions:
|
|
19
|
+
if not (Path(path) / f'{name}.{extension}').exists():
|
|
20
|
+
continue
|
|
21
|
+
else:
|
|
22
|
+
self.path = Path(path) / f'{name}.{extension}'
|
|
23
|
+
|
|
24
|
+
def load(self) -> dict[str, Any]:
|
|
25
|
+
print(f'loader_path: {self.path}')
|
|
26
|
+
filetype = self.path.suffix.replace('.', '')
|
|
27
|
+
loader = tsm.loader.DataLoader.get_loader(filetype)
|
|
28
|
+
print(f'loader.load({self.path})')
|
|
29
|
+
loader.load(self.path)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class DataLoader:
|
|
2
|
+
_registry = {}
|
|
3
|
+
|
|
4
|
+
def __init_subclass__(cls, fmt=None, **kwargs):
|
|
5
|
+
super().__init_subclass__(**kwargs)
|
|
6
|
+
if fmt:
|
|
7
|
+
DataLoader._registry[fmt] = cls
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def get_loader(cls, fmt):
|
|
11
|
+
if fmt not in cls._registry:
|
|
12
|
+
raise ValueError(f"No loader for {fmt!r}. "
|
|
13
|
+
f"Available: {list(cls._registry)}")
|
|
14
|
+
return cls._registry[fmt]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CSVLoader(DataLoader, fmt="csv"):
|
|
18
|
+
|
|
19
|
+
def load(self, path):
|
|
20
|
+
import csv
|
|
21
|
+
return f"Loading CSV from {path}"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class JSONLoader(DataLoader, fmt="json"):
|
|
25
|
+
|
|
26
|
+
def load(self, path):
|
|
27
|
+
import json
|
|
28
|
+
with open(path, 'r', encoding='utf-8') as file:
|
|
29
|
+
data = json.load(file)
|
|
30
|
+
return data
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class YAMLLoader(DataLoader, fmt="yaml"):
|
|
34
|
+
|
|
35
|
+
def load(self, path):
|
|
36
|
+
import yaml
|
|
37
|
+
with open(path, 'rb') as file:
|
|
38
|
+
data = yaml.safe_load(file)
|
|
39
|
+
return data
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TOMLLoader(DataLoader, fmt="toml"):
|
|
43
|
+
|
|
44
|
+
def load(self, path):
|
|
45
|
+
import tomllib
|
|
46
|
+
with open(path, 'rb') as file:
|
|
47
|
+
data = tomllib.load(file)
|
|
48
|
+
return data
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# loader = DataLoader.get_loader("parquet")
|
|
52
|
+
# print(loader.load("sales.parquet"))
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import logging.handlers
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LogLevel(Enum):
|
|
7
|
+
NOTSET = 0
|
|
8
|
+
DEBUG = 10
|
|
9
|
+
INFO = 20
|
|
10
|
+
WARN = 30
|
|
11
|
+
ERROR = 40
|
|
12
|
+
CRITICAL = 50
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def setup_logging(
|
|
16
|
+
*,
|
|
17
|
+
name: str = __name__,
|
|
18
|
+
console: bool = True,
|
|
19
|
+
path: str | None = None,
|
|
20
|
+
level: LogLevel = 20,
|
|
21
|
+
):
|
|
22
|
+
"""Provides a logger with optional FileHandler and StreamHandler.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
name: str
|
|
27
|
+
The new logger's name (default: __name__)
|
|
28
|
+
console: bool
|
|
29
|
+
Whether or not to activate the console stream logger
|
|
30
|
+
path: str | None = None
|
|
31
|
+
Path to a file for persistent logging if needed
|
|
32
|
+
level: LogLevel
|
|
33
|
+
The minimum level threshold for messages to appear in your logs
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
Logger
|
|
38
|
+
A configured logging.Logger instance
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
handlers = []
|
|
42
|
+
|
|
43
|
+
# Initialize a logger
|
|
44
|
+
logger = logging.getLogger(name)
|
|
45
|
+
logger.setLevel(level)
|
|
46
|
+
|
|
47
|
+
# 1. Formatter (applied to all handlers)
|
|
48
|
+
fmt = "%(asctime)s [%(levelname)s "
|
|
49
|
+
fmt += "%(name)s(line:%(lineno)d)]: %(message)s"
|
|
50
|
+
formatter = logging.Formatter(fmt=fmt, datefmt="%Y%m%d.%H:%M:%S")
|
|
51
|
+
|
|
52
|
+
# 2. File Handler (for persistent storage)
|
|
53
|
+
if path is not None:
|
|
54
|
+
file_handler = logging.handlers.RotatingFileHandler(
|
|
55
|
+
path,
|
|
56
|
+
maxBytes=1024 * 1024,
|
|
57
|
+
backupCount=5,
|
|
58
|
+
)
|
|
59
|
+
file_handler.setLevel(level)
|
|
60
|
+
file_handler.setFormatter(formatter)
|
|
61
|
+
handlers.append(file_handler)
|
|
62
|
+
|
|
63
|
+
# 3. Console Handler (for real-time feedback)
|
|
64
|
+
if console:
|
|
65
|
+
console_handler = logging.StreamHandler()
|
|
66
|
+
console_handler.setLevel(level)
|
|
67
|
+
console_handler.setFormatter(formatter)
|
|
68
|
+
handlers.append(console_handler)
|
|
69
|
+
|
|
70
|
+
# 4. Add handlers (if they are not already present)
|
|
71
|
+
if not logger.handlers:
|
|
72
|
+
for handler in handlers:
|
|
73
|
+
logger.addHandler(handler)
|
|
74
|
+
|
|
75
|
+
return logger
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
File without changes
|