Skip to content

Commit

Permalink
remove need for configuration file
Browse files Browse the repository at this point in the history
  • Loading branch information
mazborowski committed Jun 26, 2024
1 parent 9ae5c0b commit 0fceb71
Show file tree
Hide file tree
Showing 14 changed files with 365 additions and 169 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# quickhost

Make a publically accessible host, quickly.
##



## Usage

Expand Down
18 changes: 18 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,21 @@
requires = ['setuptools']
build-backend = "setuptools.build_meta"

[project]
name = "quickhost"
version = "0.0.1"
authors = [
{ name="zeebrow", email="[email protected]" },
]
description = "Start a publically available webhost, quickly."
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]

[project.urls]
Homepage = "https://github.com/zeebrow/quickhost"
Issues = "https://github.com/zeebrow/quickhost/issues"
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = quickhost
version = 1.0.1
version = 0.1.1

[options]
python_requires = >=3.7
Expand Down
43 changes: 0 additions & 43 deletions setup.py

This file was deleted.

139 changes: 92 additions & 47 deletions src/quickhost/QuickhostPlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import sys

if sys.version_info.minor == 7:
import pkgutil
import importlib_metadata as metadata
from importlib_metadata import version
else:
from importlib import metadata
from importlib.metadata import version

import typing as t

Expand All @@ -30,60 +32,103 @@

logger = logging.getLogger(__name__)


PluginName = str


class NoPluginFoundError(Exception):
pass


@dataclass
class Plugin:
"""
Container object for plugin-loading functions
"""
name: PluginName
package_name: str
version: str
app: AppBase
parser: ParserBase


class QHPlugin:
@classmethod
def load_all_plugins(cls) -> t.Dict[PluginName, Plugin]:
"""returns a dictionary mapping installed plugin names to a Plugin object"""
plugins = defaultdict(dict)

if sys.version_info.minor == 7:
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]
package_name = 'quickhost_' + provider_name
version = "undefined"
app = l.load_plugin()
parser = l.get_parser()
plugins[provider_name] = Plugin(name=provider_name, package_name=package_name, version=version, 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:
provider_name = p.name.split('_')[0]
package_name = 'quickhost_' + provider_name
version = metadata.version("quickhost_{}".format(provider_name))
# 'app' or 'parser'
plugin_type = p.name.split('_')[1]
if plugin_type == 'app':
plugins[provider_name]['app'] = p.load()()
elif plugin_type == 'parser':
plugins[provider_name]['parser'] = p.load()()
else:
logger.warning(f"Unknown plugin type '{plugin_type}'")
plugins_list: t.Dict[str, Plugin] = {
p: Plugin(name=p, package_name=package_name, version=version, app=plugins[p]['app'], parser=plugins[p]['parser']) for p in plugins.keys() # noqa: E126
}
return dict(plugins_list)
def get_plugin(plugin_name: PluginName) -> Plugin:
"""
Get a quickhost plugin that adheres to entrypoint naming conventions:
:raises NoPluginFoundError: The pip package 'quickhost-<plugin_name>' is not installed
:return: Plugin containing AppBase, Parser, and metadata
:rtype: quickhost.QuickhostPlugin.Plugin
[options.entry_points]
quickhost_plugin =
<plugin_name>_app = quickhost_<plugin_name>:get_app
<plugin_name>_parser = quickhost_<plugin_name>:get_parser
"""
qh_plugins = metadata.entry_points(group='quickhost_plugin')
try:
v = version(f'quickhost_{plugin_name}')
app = qh_plugins[f'{plugin_name}_app'].load()()
parser = qh_plugins[f'{plugin_name}_parser'].load()()
except Exception as e:
print(e)
print(e)
print(e)
print(e)
raise NoPluginFoundError(f"No plugin found for '{plugin_name}' -- try running pip install quickhost-{plugin_name}")

return Plugin(
name=plugin_name,
package_name=f'quickhost_{plugin_name}',
version=v,
app=app,
parser=parser,
)


def get_plugin_app_getter(plugin_name: PluginName) -> t.Callable[..., AppBase]:
"""
Get the loader a :class:`quickhost.quickhost_app_base.AppBase` subclass.
:param name: Name of the plugin
:type name: str
:raises NoPluginFoundError: The pip package 'quickhost-<plugin_name>' is not installed
:return: function returning the plugin's app class
:rtype: quickhost.QuickhostPlugin.Plugin
"""
try:
qh_plugins = metadata.entry_points(group='quickhost_plugin')[f'{plugin_name}_app']
return qh_plugins.load()
except:
raise NoPluginFoundError(f"No plugin found for '{plugin_name}' -- try running pip install quickhost-{plugin_name}")


def get_plugin_parser_getter(plugin_name: PluginName) -> t.Callable[..., ParserBase]:
"""
Get the loader a :class:`quickhost.quickhost_app_base.ParserBase` subclass.
:param name: Name of the plugin
:type name: str
:raises NoPluginFoundError: The pip package 'quickhost-<plugin_name>' is not installed
:return: function returning the plugin's parser class
:rtype: Callable[..., ParserBase]
"""
try:
qh_plugins = metadata.entry_points(group='quickhost_plugin')[f'{plugin_name}_parser']
return qh_plugins.load()
except:
raise NoPluginFoundError(f"No plugin found for '{plugin_name}' -- try running pip install quickhost-{plugin_name}")


def fetch_all_plugins() -> t.Dict[PluginName, Plugin]:
"""
Eagerly loads all quickhost plugins.
Returns a dictionary mapping installed plugin names to a Plugin object
:return: dict mapping plugin names to their :class:`quickhost.QuickhostPlugin.Plugin`.
:rtype: Dict[str, Plugin]
"""
plugins = {}
plugin_providers = set([entrypoint.name.split('_')[0] for entrypoint in metadata.entry_points(group='quickhost_plugin')])
logger.debug("Found %d provider plugins.", len(plugin_providers))
for p in plugin_providers:
plugins[p] = get_plugin(p)
return plugins
4 changes: 2 additions & 2 deletions src/quickhost/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

from .quickhost_app_base import AppBase, ParserBase, AppConfigFileParser
from .quickhost_app_base import AppBase, ParserBase
from .constants import APP_CONST, QHExit
from .utilities import get_my_public_ip, scrub_datetime, QHLogFormatter
from .temp_data_collector import store_test_data
from .QuickhostPlugin import QHPlugin, Plugin
from .QuickhostPlugin import Plugin
from .Cli import CliResponse
29 changes: 0 additions & 29 deletions src/quickhost/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import sys
import os


def get_config_dir() -> str:
"""matches setup.py"""
if sys.platform == 'linux':
return os.path.join(os.path.expanduser("~"), ".config", "quickhost") # XDG
elif sys.platform in ['win32', 'cygwin']:
return os.path.join(os.path.expanduser("~")) # ???
elif sys.platform == 'darwin':
return os.path.join(os.path.expanduser("~"), ".config", "quickhost") # ...???
else:
raise Exception("Unsupported platform '{}'".format(sys.platform))


def get_config_filepath() -> str:
"""so lazy..."""
if sys.platform == 'linux':
return os.path.join(get_config_dir(), "quickhost.conf")
elif sys.platform in ['win32', 'cygwin']:
return os.path.join(get_config_dir(), ".quickhost.conf")
elif sys.platform == 'darwin':
return os.path.join(get_config_dir(), "quickhost.conf")
else:
raise Exception("Unsupported platform '{}'".format(sys.platform))


class APP_CONST:
DEFAULT_CONFIG_FILEPATH = get_config_filepath()
DEFAULT_CONFIG_DIR = get_config_dir()
DEFAULT_APP_NAME = 'quickhost'
DEFAULT_OPEN_PORTS = ['22']
DEFAULT_VPC_CIDR = '172.16.0.0/16'
Expand Down
15 changes: 2 additions & 13 deletions src/quickhost/quickhost_app_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,19 @@

import argparse
from abc import abstractmethod, abstractclassmethod
import configparser
import logging

from .constants import APP_CONST as C

logger = logging.getLogger(__name__)


class AppConfigFileParser(configparser.ConfigParser):
def __init__(self):
super().__init__(allow_no_value=True)


class ParserBase():
"""
A plugin's __init__.py must implement a function named get_parser() which return the implementation of this class.
"""
@abstractmethod
def __init__(self, config_file=C.DEFAULT_CONFIG_FILEPATH):
def __init__(self):
...

@abstractmethod
Expand Down Expand Up @@ -70,12 +64,7 @@ class AppBase():
Using the `app_name` as a key, caching may be implemented.
"""
@abstractmethod
def __init__(self, app_name: str, config_file=C.DEFAULT_CONFIG_FILEPATH):
...

@abstractmethod
def load_default_config(self):
"""get possible config from file"""
def __init__(self, app_name: str):
...

@abstractmethod
Expand Down
7 changes: 6 additions & 1 deletion src/quickhost/temp_data_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
MAX_FILE_SIZE_BYTES = 10_000_000 # ~10MB


def store_test_data(resource: str, action: str, response_data: dict):
def store_test_data(resource: str, action: str, response_data: dict, disable=True):
if disable:
return
global TOTAL_RUNTIME
t_start = perf_counter()
cwd = Path(os.getcwd())
Expand Down Expand Up @@ -54,6 +56,9 @@ def store_test_data(resource: str, action: str, response_data: dict):
if fp.stat().st_size >= MAX_FILE_SIZE_BYTES:
logger.debug(f"{fp.stem} Max filesize reached")
with fp.open("r") as g:
print(fp.absolute())
print(fp.absolute())
print(fp.absolute())
j = json.load(g)
j[action].append(response_data)
with fp.open("w") as h:
Expand Down
Loading

0 comments on commit 0fceb71

Please sign in to comment.