88
99import argparse
1010import importlib .util
11+ import pathlib
1112import pkgutil
1213import sys
14+ import types
1315
1416import argcomplete
1517
1618import infuse_iot .tools
1719from infuse_iot .commands import InfuseCommand
20+ from infuse_iot .credentials import get_custom_tool_path
1821from infuse_iot .version import __version__
1922
2023
@@ -38,24 +41,42 @@ def run(self, argv):
3841 tool = self .args .tool_class (self .args )
3942 tool .run ()
4043
44+ def _load_from_module (self , parent_parser : argparse ._SubParsersAction , module : types .ModuleType ):
45+ tool_cls : InfuseCommand = module .SubCommand
46+ parser = parent_parser .add_parser (
47+ tool_cls .NAME ,
48+ help = tool_cls .HELP ,
49+ description = tool_cls .DESCRIPTION ,
50+ formatter_class = argparse .RawDescriptionHelpFormatter ,
51+ )
52+ parser .set_defaults (tool_class = tool_cls )
53+ tool_cls .add_parser (parser )
54+
4155 def _load_tools (self , parser : argparse .ArgumentParser ):
4256 tools_parser = parser .add_subparsers (title = "commands" , metavar = "<command>" , required = True )
4357
44- # Iterate over tools
58+ # Iterate over local tools
4559 for _ , name , _ in pkgutil .walk_packages (infuse_iot .tools .__path__ ):
4660 full_name = f"{ infuse_iot .tools .__name__ } .{ name } "
4761 module = importlib .import_module (full_name )
48-
49- # Add tool to parser
50- tool_cls : InfuseCommand = module .SubCommand
51- parser = tools_parser .add_parser (
52- tool_cls .NAME ,
53- help = tool_cls .HELP ,
54- description = tool_cls .DESCRIPTION ,
55- formatter_class = argparse .RawDescriptionHelpFormatter ,
56- )
57- parser .set_defaults (tool_class = tool_cls )
58- tool_cls .add_parser (parser )
62+ self ._load_from_module (tools_parser , module )
63+
64+ # Load custom tools, if configured
65+ if extension_tools := get_custom_tool_path ():
66+ extension_path = pathlib .Path (extension_tools )
67+ for _ , name , _ in pkgutil .walk_packages ([extension_tools ]):
68+ full_name = f"{ infuse_iot .tools .__name__ } .{ name } "
69+ full_path = str (extension_path / f"{ name } .py" )
70+ spec = importlib .util .spec_from_file_location (full_name , full_path )
71+ if spec is None or spec .loader is None :
72+ continue
73+ module = importlib .util .module_from_spec (spec )
74+ try :
75+ spec .loader .exec_module (module )
76+ except Exception as _ :
77+ continue
78+ if hasattr (module , "SubCommand" ):
79+ self ._load_from_module (tools_parser , module )
5980
6081
6182def main (argv = None ):
0 commit comments