Python-modernize munkilib/munkirepo and lay groundwork for loading additional repo plugins from an external directory

This commit is contained in:
Greg Neagle
2019-05-28 19:20:36 -07:00
parent f0e00aefba
commit 8e85d5c41d
5 changed files with 74 additions and 27 deletions
@@ -26,6 +26,7 @@ try:
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
# pylint: disable=invalid-name
NetFS = Attrdict()
# Can cheat and provide 'None' for the identifier, it'll just use
# frameworkPath instead
@@ -34,13 +35,16 @@ try:
'NetFS', frameworkIdentifier=None,
frameworkPath=objc.pathForFramework('NetFS.framework'),
globals=NetFS, scan_classes=False)
# pylint: enable=invalid-name
# https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/
# ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
# Fix NetFSMountURLSync signature
del NetFS['NetFSMountURLSync']
# pylint: disable=no-member
objc.loadBundleFunctions(
NetFS_bundle, NetFS, [('NetFSMountURLSync', 'i@@@@@@o^@')])
# pylint: enable=no-member
NETFSMOUNTURLSYNC_AVAILABLE = True
except (ImportError, KeyError):
NETFSMOUNTURLSYNC_AVAILABLE = False
@@ -141,6 +145,7 @@ def mount_share_url(share_url):
class FileRepo(Repo):
'''Handles local filesystem repo and repos mounted via filesharing'''
# pylint: disable=super-init-not-called
def __init__(self, baseurl):
'''Constructor'''
self.baseurl = baseurl
@@ -156,6 +161,7 @@ class FileRepo(Repo):
unicodeize(urllib.unquote(url_parts.path).lstrip('/')))
self.we_mounted_repo = False
self._connect()
# pylint: enable=super-init-not-called
def __del__(self):
'''Destructor -- unmount the fileshare if we mounted it'''
@@ -8,7 +8,7 @@ import pwd
import subprocess
import sys
from .FileRepo import FileRepo
from munkilib.munkirepo.FileRepo import FileRepo
# TODO: make this more easily customized
GITCMD = '/usr/bin/git'
@@ -19,16 +19,20 @@ DEBUG = False
CURL_CMD = '/usr/bin/curl'
class CurlError(Exception):
'''Error for curl operations'''
pass
class MWA2APIRepo(Repo):
'''Class for working with a repo accessible via the MWA2 API'''
# pylint: disable=super-init-not-called
def __init__(self, baseurl):
'''Constructor'''
self.baseurl = baseurl
self.authtoken = None
self._connect()
# pylint: enable=super-init-not-called
def _connect(self):
'''For a fileshare repo, we'd mount the share, prompting for
@@ -129,9 +133,9 @@ class MWA2APIRepo(Repo):
if kind in ['catalogs', 'manifests', 'pkgsinfo']:
# it's a list of dicts containing 'filename' key/values
return [item['filename'] for item in plist]
else:
# it's a list of filenames
return plist
# it's a list of filenames (pkgs, icons)
return plist
def get(self, resource_identifier):
'''Returns the content of item with given resource_identifier.
+45 -23
View File
@@ -5,27 +5,57 @@ import imp
import os
import sys
class RepoError(Exception):
'''Base exception for repo errors'''
pass
from ._baseclasses import RepoError, Repo
from .FileRepo import FileRepo
class Repo(object):
'''Abstract base class for repo'''
def __init__(self, url):
'''Override in subclasses'''
pass
def import_plugins(dirpath=None):
"""Imports plugins from dirpath or the directory this file is in"""
plugin_names = []
if not dirpath:
# get the directory this __init__.py file is in
dirpath = os.path.dirname(os.path.abspath(__file__))
# find all the .py files (minus __init__.py)
plugin_files = [
os.path.splitext(name)[0]
for name in os.listdir(dirpath)
if name.endswith(".py") and not name.startswith("_")
]
for name in plugin_files:
if name in globals():
# we already imported it
plugin_names.append(name)
continue
plugin_filename = os.path.join(dirpath, name + ".py")
try:
# attempt to import the module
_tmp = imp.load_source(name, plugin_filename)
# look for an attribute with the plugin name
plugin = getattr(_tmp, name)
# add the processor to munkirepo's namespace
globals()[name] = plugin
plugin_names.append(name)
except (ImportError, AttributeError) as err:
# if we aren't successful, print a warning
print(
"WARNING: %s: %s" % (plugin_filename, err), file=sys.stderr
)
return plugin_names
__all__ = import_plugins()
def plugin_named(name):
# Helper functions for munkirepo plugins
def plugin_named(some_name):
'''Returns a plugin object given a name'''
try:
module = globals()[name]
return getattr(module, name)
return globals()[some_name]
except (KeyError, AttributeError):
print((
"ERROR: %s repo plugin not found." % name), file=sys.stderr)
print("ERROR: %s repo plugin not found." % some_name, file=sys.stderr)
return None
@@ -35,12 +65,4 @@ def connect(repo_url, plugin_name):
if plugin:
return plugin(repo_url)
else:
raise RepoError('Could not find repo plugin named %s' % plugin_name)
# yes, having this at the end is weird. But it allows us to dynamically import
# additional modules from our directory
__all__ = [os.path.splitext(name)[0]
for name in os.listdir(os.path.dirname(os.path.abspath(__file__)))
if name.endswith('.py') and not name == '__init__.py']
from . import *
raise RepoError('Could not find repo plugin named: %s' % plugin_name)
@@ -0,0 +1,15 @@
# encoding: utf-8
"""Base classes for repo plugins"""
class RepoError(Exception):
'''Base exception for repo errors'''
pass
# pylint: disable=too-few-public-methods
class Repo(object):
'''Abstract base class for repo'''
def __init__(self, url):
'''Override in subclasses'''
pass
# pylint: enable=too-few-public-methods