diff --git a/README.md b/README.md
index d98d6cd..aca23e7 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,29 @@ options:
## ~~Build~~
+### Python 3.7, 3.8
+
+i dunno tox :(
+
+
+install Python, e.g.
+
+`sudo apt-get install python3.8`
+
+install pip
+
+```
+sudo apt install python3.8-distutils
+curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
+python3.8 get-pip.py
+```
+
+and your choice of venv
+
+```
+sudo apt install python3.8-venv
+```
+
### ~~pyinstaller~~
#### do this
diff --git a/setup.cfg b/setup.cfg
index fa01d0e..4bcf765 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,11 +1,11 @@
[metadata]
name = quickhost
-version = 0.0.2
+version = 1.0.0
[options]
-python_requires = >=3.10
+python_requires = >=3.7
install_requires =
- importlib-metadata; python_version>="3.10"
+ importlib-metadata; python_version>="3.7"
[options.entry_points]
console_scripts =
diff --git a/src/quickhost/Cli.py b/src/quickhost/Cli.py
index 49003e0..8237349 100644
--- a/src/quickhost/Cli.py
+++ b/src/quickhost/Cli.py
@@ -105,36 +105,35 @@ def cli(plugin_name: str) -> CliResponse:
args = vars(plugin_parser.parse_args(plugin_args))
action = args.pop(plugin_name)
- logger.debug(f"{action=}")
+ logger.debug("action={}".format(action))
app_class: AppBase = plugin.app
app_instance: AppBase = app_class(plugin.name) # pyright: ignore
- match action:
- case 'init':
- return app_instance.plugin_init(args) # pyright: ignore
- case 'make':
- return app_instance.create(args) # pyright: ignore
- case 'describe':
- return app_instance.describe(args) # pyright: ignore
- case 'destroy':
- return app_instance.destroy(args) # pyright: ignore
- case 'update':
- return app_instance.update(args) # pyright: ignore
- case 'list-all':
- return app_class.list_all() # pyright: ignore
- case 'destroy-all':
- return app_class.destroy_all() # pyright: ignore
- case 'destroy-plugin':
- return app_instance.plugin_destroy(args) # pyright: ignore
- case 'help':
- # @@@want: something for the Cobra-style commander users
- app_parser.print_help()
- return CliResponse('', 'For help on a specific action, use -h.', 1)
- case None:
- # unreachable
- app_parser.print_help()
- return CliResponse('', "Bug! action was 'None'", 1)
- case _:
- app_parser.print_help()
- return CliResponse('', f"Invalid action: '{action}'", 1)
+ if action == 'init':
+ return app_instance.plugin_init(args) # pyright: ignore
+ elif action == 'make':
+ return app_instance.create(args) # pyright: ignore
+ elif action == 'describe':
+ return app_instance.describe(args) # pyright: ignore
+ elif action == 'destroy':
+ return app_instance.destroy(args) # pyright: ignore
+ elif action == 'update':
+ return app_instance.update(args) # pyright: ignore
+ elif action == 'list-all':
+ return app_class.list_all() # pyright: ignore
+ elif action == 'destroy-all':
+ return app_class.destroy_all() # pyright: ignore
+ elif action == 'destroy-plugin':
+ return app_instance.plugin_destroy(args) # pyright: ignore
+ elif action == 'help':
+ # @@@want: something for the Cobra-style commander users
+ app_parser.print_help()
+ return CliResponse('', 'For help on a specific action, use -h.', 1)
+ elif action is None:
+ # unreachable
+ app_parser.print_help()
+ return CliResponse('', "Bug! action was 'None'", 1)
+ else:
+ app_parser.print_help()
+ return CliResponse('', f"Invalid action: '{action}'", 1)
diff --git a/src/quickhost/QuickhostPlugin.py b/src/quickhost/QuickhostPlugin.py
index 762d50f..d289313 100644
--- a/src/quickhost/QuickhostPlugin.py
+++ b/src/quickhost/QuickhostPlugin.py
@@ -14,8 +14,15 @@
# along with this program. If not, see .
import logging
-from importlib import metadata
import sys
+
+if sys.version_info.minor == 7:
+ import pkgutil
+else:
+ from importlib import metadata
+
+import typing as t
+
from collections import defaultdict
from dataclasses import dataclass
@@ -35,39 +42,36 @@ class Plugin:
class QHPlugin:
- """
- for python < 3.10
- https://bugs.python.org/issue44246
- https://github.com/python/importlib_metadata/pull/278/files#
- """
@classmethod
- def try_load_plugin(cls, plugin_name: str) -> Plugin:
- """
- returns a plugin
-
- This is meant to be used by plugins to provide a shortcut script,
- `quickhost-`, so we know ahead of time which plugin we want to try
- and load, and that it will definitely exist.
- """
- plugin_groups = metadata.entry_points().select(group='quickhost_plugin')
- app = plugin_groups.select(name='pve_app')[plugin_name + "_app"].load()()
- parser = plugin_groups.select(name='pve_parser')[plugin_name + "_parser"].load()()
- return Plugin(plugin_name, app, parser)
-
- @classmethod
- def load_all_plugins(cls) -> dict[str, Plugin]:
+ def load_all_plugins(cls) -> t.Dict[str, Plugin]:
"""returns a dictionary mapping installed plugin names to a Plugin object"""
plugins = defaultdict(dict)
- if sys.version_info.minor < 10:
+ if sys.version_info.minor == 7:
+ plugins = defaultdict(dict)
+ for p in pkgutil.walk_packages():
+ if p.name.startswith('quickhost_') and p.ispkg:
+ l = pkgutil.get_loader(p.name).load_module() # noqa: E741
+ # found in plugin's __init__.py
+ provider_name = p.name.split('_')[1]
+ app = l.load_plugin()
+ parser = l.get_parser()
+ plugins[provider_name] = Plugin(name=provider_name, app=app, parser=parser)
+
+ return dict(plugins)
+
+ elif sys.version_info.minor > 7 and sys.version_info.minor < 10:
plugin_parsers = metadata.entry_points()['quickhost_plugin']
else:
plugin_parsers = metadata.entry_points().select(group="quickhost_plugin")
# sift through plugins, organize by cloud provider and return
for p in plugin_parsers:
+ # see plugin's setup.py
+ # 'aws'
provider_name = p.name.split('_')[0]
+ # 'app' or 'parser'
plugin_type = p.name.split('_')[1]
if plugin_type == 'app':
plugins[provider_name]['app'] = p.load()()
@@ -75,7 +79,7 @@ def load_all_plugins(cls) -> dict[str, Plugin]:
plugins[provider_name]['parser'] = p.load()()
else:
logger.warning(f"Unknown plugin type '{plugin_type}'")
- plugins_list: dict[str, Plugin] = {
+ plugins_list: t.Dict[str, Plugin] = {
p: Plugin(p, plugins[p]['app'], plugins[p]['parser']) for p in plugins.keys() # noqa: E126
}
return dict(plugins_list)
diff --git a/src/quickhost/utilities.py b/src/quickhost/utilities.py
index b08f294..e4d5bfd 100644
--- a/src/quickhost/utilities.py
+++ b/src/quickhost/utilities.py
@@ -69,23 +69,21 @@ def __init__(self, color=False):
def format(self, record):
# orig_fmt = self._style._fmt
if self.colored_output:
- match record.levelno:
- case logging.DEBUG:
- self._style._fmt = QHLogFormatter.DebugFormatColor
- case logging.INFO:
- self._style._fmt = QHLogFormatter.InfoFormatColor
- case logging.WARNING:
- self._style._fmt = QHLogFormatter.WarningFormatColor
- case (logging.ERROR | logging.CRITICAL):
- self._style._fmt = QHLogFormatter.ErrorFormatColor
+ if record.levelno == logging.DEBUG:
+ self._style._fmt = QHLogFormatter.DebugFormatColor
+ elif record.levelno == logging.INFO:
+ self._style._fmt = QHLogFormatter.InfoFormatColor
+ elif record.levelno == logging.WARNING:
+ self._style._fmt = QHLogFormatter.WarningFormatColor
+ elif record.levelno == (logging.ERROR | logging.CRITICAL):
+ self._style._fmt = QHLogFormatter.ErrorFormatColor
else:
- match record.levelno:
- case logging.DEBUG:
- self._style._fmt = QHLogFormatter.DebugFormat
- case logging.INFO:
- self._style._fmt = QHLogFormatter.InfoFormat
- case logging.WARNING:
- self._style._fmt = QHLogFormatter.WarningFormat
- case (logging.ERROR | logging.CRITICAL):
- self._style._fmt = QHLogFormatter.ErrorFormat
+ if record.levelno == logging.DEBUG:
+ self._style._fmt = QHLogFormatter.DebugFormat
+ elif record.levelno == logging.INFO:
+ self._style._fmt = QHLogFormatter.InfoFormat
+ elif record.levelno == logging.WARNING:
+ self._style._fmt = QHLogFormatter.WarningFormat
+ elif record.levelno == (logging.ERROR | logging.CRITICAL):
+ self._style._fmt = QHLogFormatter.ErrorFormat
return super().format(record)
diff --git a/src/scripts/main.py b/src/scripts/main.py
index 82c9431..26507c9 100755
--- a/src/scripts/main.py
+++ b/src/scripts/main.py
@@ -48,15 +48,18 @@ def cli_main() -> CliResponse:
logger.debug("cli args={}".format(args))
if args['version']:
- from importlib.metadata import version
- return CliResponse(version('quickhost'), '', 0)
+ if sys.version_info.minor > 7:
+ from importlib.metadata import version
+ return CliResponse(version('quickhost'), '', 0)
+ else:
+ return CliResponse('', 'package info not available for Python {}.{}'.format(sys.version_info.major, sys.version_info.minor), 1)
if dict(plugins) == {}:
app_parser.print_help()
return CliResponse('', "No plugins are installed! Try pip install --user quickhost-aws", 1)
tgt_plugin = args.pop('plugin')
- logger.debug(f"{tgt_plugin=}")
+ logger.debug(f"{tgt_plugin}")
if tgt_plugin is None:
app_parser.print_help()
return CliResponse('', f"Provide a plugin {[k for k in plugins.keys()]}", 1)
@@ -64,35 +67,34 @@ def cli_main() -> CliResponse:
app_name = None # @@@
if 'app_name' in args.keys(): # @@@
app_name = args.pop("app_name") # @@@
- logger.debug(f"{app_name=}")
+ logger.debug("app_name={}".format(app_name))
action = args.pop(tgt_plugin)
- logger.debug(f"{action=}")
+ logger.debug("action={}".format(action))
app_class: AppBase = plugins[tgt_plugin].app
app_instance: Type[AppBase] = app_class(app_name) # pyright: ignore
- match action:
- case 'init':
- return app_instance.plugin_init(args) # pyright: ignore
- case 'make':
- return app_instance.create(args) # pyright: ignore
- case 'describe':
- return app_instance.describe(args) # pyright: ignore
- case 'destroy':
- return app_instance.destroy(args) # pyright: ignore
- case 'update':
- return app_instance.update(args) # pyright: ignore
- case 'list-all':
- return app_class.list_all() # pyright: ignore
- case 'destroy-all':
- return app_class.destroy_all() # pyright: ignore
- case 'destroy-plugin':
- return app_instance.plugin_destroy(args) # pyright: ignore
- case None:
- app_parser.print_help()
- return CliResponse('', f"No action provided (try quickhost {tgt_plugin} -h)", 1)
- case _:
- app_parser.print_help()
- return CliResponse('', f"Invalid action: '{action}'", 1)
+ if action == 'init':
+ return app_instance.plugin_init(args) # pyright: ignore
+ elif action == 'make':
+ return app_instance.create(args) # pyright: ignore
+ elif action == 'describe':
+ return app_instance.describe(args) # pyright: ignore
+ elif action == 'destroy':
+ return app_instance.destroy(args) # pyright: ignore
+ elif action == 'update':
+ return app_instance.update(args) # pyright: ignore
+ elif action == 'list-all':
+ return app_class.list_all() # pyright: ignore
+ elif action == 'destroy-all':
+ return app_class.destroy_all() # pyright: ignore
+ elif action == 'destroy-plugin':
+ return app_instance.plugin_destroy(args) # pyright: ignore
+ elif action is None:
+ app_parser.print_help()
+ return CliResponse('', f"No action provided (try quickhost {tgt_plugin} -h)", 1)
+ else:
+ app_parser.print_help()
+ return CliResponse('', f"Invalid action: '{action}'", 1)
fd1, fd2, rc = cli_main()
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index 13044d2..1fb13e9 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -12,7 +12,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import pytest
from quickhost import QHPlugin
from quickhost_null import NullApp, NullParser