mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-10-03 17:50:11 +02:00
rough plugin system implementation
This commit is contained in:
parent
32f09603e1
commit
bf815251ec
@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# fdroidserver/__main__.py - part of the FDroid server tools
|
# fdroidserver/__main__.py - part of the FDroid server tools
|
||||||
|
# Copyright (C) 2020 Michael Pöhn <michael.poehn@fsfe.org>
|
||||||
# Copyright (C) 2010-2015, Ciaran Gultnieks, ciaran@ciarang.com
|
# Copyright (C) 2010-2015, Ciaran Gultnieks, ciaran@ciarang.com
|
||||||
# Copyright (C) 2013-2014 Daniel Marti <mvdan@mvdan.cc>
|
# Copyright (C) 2013-2014 Daniel Marti <mvdan@mvdan.cc>
|
||||||
#
|
#
|
||||||
@ -20,7 +21,9 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import locale
|
import locale
|
||||||
|
import pkgutil
|
||||||
import logging
|
import logging
|
||||||
|
import importlib
|
||||||
|
|
||||||
import fdroidserver.common
|
import fdroidserver.common
|
||||||
import fdroidserver.metadata
|
import fdroidserver.metadata
|
||||||
@ -54,25 +57,49 @@ commands = OrderedDict([
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def print_help():
|
def print_help(fdroid_modules=None):
|
||||||
print(_("usage: ") + _("fdroid [<command>] [-h|--help|--version|<args>]"))
|
print(_("usage: ") + _("fdroid [<command>] [-h|--help|--version|<args>]"))
|
||||||
print("")
|
print("")
|
||||||
print(_("Valid commands are:"))
|
print(_("Valid commands are:"))
|
||||||
for cmd, summary in commands.items():
|
for cmd, summary in commands.items():
|
||||||
print(" " + cmd + ' ' * (15 - len(cmd)) + summary)
|
print(" " + cmd + ' ' * (15 - len(cmd)) + summary)
|
||||||
|
if fdroid_modules:
|
||||||
|
print(_('commands from plugin modules:'))
|
||||||
|
for command in sorted(fdroid_modules.keys()):
|
||||||
|
print(' {:15}{}'.format(command, fdroid_modules[command]['summary']))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
def find_plugins():
|
||||||
|
fdroid_modules = [x[1] for x in pkgutil.iter_modules() if x[1].startswith('fdroid_')]
|
||||||
|
commands = {}
|
||||||
|
for module_name in fdroid_modules:
|
||||||
|
try:
|
||||||
|
command_name = module_name[7:]
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
if hasattr(module, 'fdroid_summary') and hasattr(module, 'main'):
|
||||||
|
commands[command_name] = {'summary': module.fdroid_summary,
|
||||||
|
'module': module}
|
||||||
|
except IOError:
|
||||||
|
# We need to keep module lookup fault tolerant because buggy
|
||||||
|
# modules must not prevent fdroidserver from functioning
|
||||||
|
# TODO: think about warning users or debug logs for notifying devs
|
||||||
|
pass
|
||||||
|
return commands
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
sys.path.append(os.getcwd())
|
||||||
|
fdroid_modules = find_plugins()
|
||||||
|
|
||||||
if len(sys.argv) <= 1:
|
if len(sys.argv) <= 1:
|
||||||
print_help()
|
print_help(fdroid_modules=fdroid_modules)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
command = sys.argv[1]
|
command = sys.argv[1]
|
||||||
if command not in commands:
|
if command not in commands and command not in fdroid_modules.keys():
|
||||||
if command in ('-h', '--help'):
|
if command in ('-h', '--help'):
|
||||||
print_help()
|
print_help(fdroid_modules=fdroid_modules)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif command == '--version':
|
elif command == '--version':
|
||||||
output = _('no version info found!')
|
output = _('no version info found!')
|
||||||
@ -103,7 +130,7 @@ def main():
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
print(_("Command '%s' not recognised.\n" % command))
|
print(_("Command '%s' not recognised.\n" % command))
|
||||||
print_help()
|
print_help(fdroid_modules=fdroid_modules)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
verbose = any(s in sys.argv for s in ['-v', '--verbose'])
|
verbose = any(s in sys.argv for s in ['-v', '--verbose'])
|
||||||
@ -133,7 +160,10 @@ def main():
|
|||||||
sys.argv[0] += ' ' + command
|
sys.argv[0] += ' ' + command
|
||||||
|
|
||||||
del sys.argv[1]
|
del sys.argv[1]
|
||||||
mod = __import__('fdroidserver.' + command, None, None, [command])
|
if command in commands.keys():
|
||||||
|
mod = __import__('fdroidserver.' + command, None, None, [command])
|
||||||
|
else:
|
||||||
|
mod = fdroid_modules[command]['module']
|
||||||
|
|
||||||
system_langcode, system_encoding = locale.getdefaultlocale()
|
system_langcode, system_encoding = locale.getdefaultlocale()
|
||||||
if system_encoding is None or system_encoding.lower() not in ('utf-8', 'utf8'):
|
if system_encoding is None or system_encoding.lower() not in ('utf-8', 'utf8'):
|
||||||
|
@ -4,8 +4,11 @@ import inspect
|
|||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import textwrap
|
||||||
import unittest
|
import unittest
|
||||||
|
import tempfile
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
from testcommon import TmpCwd, TmpPyPath
|
||||||
|
|
||||||
localmodule = os.path.realpath(
|
localmodule = os.path.realpath(
|
||||||
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
|
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
|
||||||
@ -17,7 +20,7 @@ from fdroidserver import common
|
|||||||
import fdroidserver.__main__
|
import fdroidserver.__main__
|
||||||
|
|
||||||
|
|
||||||
class FdroidTest(unittest.TestCase):
|
class MainTest(unittest.TestCase):
|
||||||
'''this tests fdroid.py'''
|
'''this tests fdroid.py'''
|
||||||
|
|
||||||
def test_commands(self):
|
def test_commands(self):
|
||||||
@ -49,18 +52,43 @@ class FdroidTest(unittest.TestCase):
|
|||||||
co = mock.Mock()
|
co = mock.Mock()
|
||||||
with mock.patch('sys.argv', ['', 'init', '-h']):
|
with mock.patch('sys.argv', ['', 'init', '-h']):
|
||||||
with mock.patch('fdroidserver.init.main', co):
|
with mock.patch('fdroidserver.init.main', co):
|
||||||
with mock.patch('sys.exit', lambda x: None):
|
with mock.patch('sys.exit') as exit_mock:
|
||||||
fdroidserver.__main__.main()
|
fdroidserver.__main__.main()
|
||||||
|
exit_mock.assert_called_once_with(0)
|
||||||
co.assert_called_once_with()
|
co.assert_called_once_with()
|
||||||
|
|
||||||
def test_call_deploy(self):
|
def test_call_deploy(self):
|
||||||
co = mock.Mock()
|
co = mock.Mock()
|
||||||
with mock.patch('sys.argv', ['', 'deploy', '-h']):
|
with mock.patch('sys.argv', ['', 'deploy', '-h']):
|
||||||
with mock.patch('fdroidserver.server.main', co):
|
with mock.patch('fdroidserver.server.main', co):
|
||||||
with mock.patch('sys.exit', lambda x: None):
|
with mock.patch('sys.exit') as exit_mock:
|
||||||
fdroidserver.__main__.main()
|
fdroidserver.__main__.main()
|
||||||
|
exit_mock.assert_called_once_with(0)
|
||||||
co.assert_called_once_with()
|
co.assert_called_once_with()
|
||||||
|
|
||||||
|
def test_find_plugins(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
|
||||||
|
with open('fdroid_testy.py', 'w') as f:
|
||||||
|
f.write(textwrap.dedent("""\
|
||||||
|
fdroid_summary = "ttt"
|
||||||
|
main = lambda: 'all good'"""))
|
||||||
|
with TmpPyPath(tmpdir):
|
||||||
|
plugins = fdroidserver.__main__.find_plugins()
|
||||||
|
self.assertIn('testy', plugins.keys())
|
||||||
|
self.assertEqual(plugins['testy']['summary'], 'ttt')
|
||||||
|
self.assertEqual(plugins['testy']['module'].main(), 'all good')
|
||||||
|
|
||||||
|
def test_main_plugin(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
|
||||||
|
with open('fdroid_testy.py', 'w') as f:
|
||||||
|
f.write(textwrap.dedent("""\
|
||||||
|
fdroid_summary = "ttt"
|
||||||
|
main = lambda: pritn('all good')"""))
|
||||||
|
with mock.patch('sys.argv', ['', 'testy']):
|
||||||
|
with mock.patch('sys.exit') as exit_mock:
|
||||||
|
fdroidserver.__main__.main()
|
||||||
|
exit_mock.assert_called_once_with(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
os.chdir(os.path.dirname(__file__))
|
os.chdir(os.path.dirname(__file__))
|
||||||
@ -71,5 +99,5 @@ if __name__ == "__main__":
|
|||||||
(common.options, args) = parser.parse_args(['--verbose'])
|
(common.options, args) = parser.parse_args(['--verbose'])
|
||||||
|
|
||||||
newSuite = unittest.TestSuite()
|
newSuite = unittest.TestSuite()
|
||||||
newSuite.addTest(unittest.makeSuite(FdroidTest))
|
newSuite.addTest(unittest.makeSuite(MainTest))
|
||||||
unittest.main(failfast=False)
|
unittest.main(failfast=False)
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
class TmpCwd():
|
class TmpCwd():
|
||||||
@ -32,3 +33,18 @@ class TmpCwd():
|
|||||||
|
|
||||||
def __exit__(self, a, b, c):
|
def __exit__(self, a, b, c):
|
||||||
os.chdir(self.orig_cwd)
|
os.chdir(self.orig_cwd)
|
||||||
|
|
||||||
|
|
||||||
|
class TmpPyPath():
|
||||||
|
"""Context-manager for temporarily changing the current working
|
||||||
|
directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, additional_path):
|
||||||
|
self.additional_path = additional_path
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
sys.path.append(self.additional_path)
|
||||||
|
|
||||||
|
def __exit__(self, a, b, c):
|
||||||
|
sys.path.remove(self.additional_path)
|
||||||
|
Loading…
Reference in New Issue
Block a user