Fixes and tests to supybot-plugin-create; modernize the plugin template (#1340)

* supybot-plugin-create: compactify import statements in the template

* supybot-plugin-create: prefer importlib over imp on Python >= 3.4

The imp module is deprecated as of Python 3.4[1], with importlib being the successor. However, importlib is only available in Python 2.7+ and 3.1+, so we should still use a fallback.

[1]: https://docs.python.org/3.6/library/imp.html

* test: add test cases for supybot-plugin-create

* -plugin-create: fix errors when only a subset of args are given

* -plugin-create: rename --real-name to --author/-a

These days, working under a pseudonym or alias is not unheard of, so putting emphasis on real names feels somewhat out of place.

* -plugin-create: add -d as an alias to --desc for consistency
This commit is contained in:
James Lu 2018-06-20 08:18:46 -07:00 committed by Valentin Lorentz
parent 72c4801bb9
commit 11d4015f71
2 changed files with 122 additions and 18 deletions

View File

@ -92,11 +92,8 @@ license = license.lstrip()
pluginTemplate = ''' pluginTemplate = '''
%s %s
import supybot.utils as utils from supybot import utils, plugins, ircutils, callbacks
from supybot.commands import * from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
try: try:
from supybot.i18n import PluginInternationalization from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('%s') _ = PluginInternationalization('%s')
@ -120,8 +117,7 @@ Class = %s
configTemplate = ''' configTemplate = '''
%s %s
import supybot.conf as conf from supybot import conf, registry
import supybot.registry as registry
try: try:
from supybot.i18n import PluginInternationalization from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('%s') _ = PluginInternationalization('%s')
@ -157,8 +153,9 @@ __init__Template = '''
%s: %s %s: %s
""" """
import sys
import supybot import supybot
import supybot.world as world from supybot import world
# Use this for the version of this plugin. You may wish to put a CVS keyword # Use this for the version of this plugin. You may wish to put a CVS keyword
# in here if you're keeping the plugin in CVS or some similar system. # in here if you're keeping the plugin in CVS or some similar system.
@ -176,7 +173,10 @@ __url__ = ''
from . import config from . import config
from . import plugin from . import plugin
from imp import reload if sys.version_info >= (3, 4):
from importlib import reload
else:
from imp import reload
# In case we're being reloaded. # In case we're being reloaded.
reload(config) reload(config)
reload(plugin) reload(plugin)
@ -219,20 +219,15 @@ def main():
help='sets the name for the plugin.') help='sets the name for the plugin.')
parser.add_option('-t', '--thread', action='store_true', dest='threaded', parser.add_option('-t', '--thread', action='store_true', dest='threaded',
help='makes the plugin threaded.') help='makes the plugin threaded.')
parser.add_option('', '--real-name', action='store', dest='realName', parser.add_option('-a', '--author', '--real-name', action='store',
help='Determines what real name the copyright is ' dest='realName', help='Determines who the copyright is '
'assigned to.') 'assigned to.')
parser.add_option('', '--desc', action='store', dest='desc', parser.add_option('-d', '--desc', action='store', dest='desc',
help='Short description of plugin.') help='Short description of plugin.')
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
if options.name: if options.name:
name = options.name name = options.name
if options.threaded: threaded = options.threaded
threaded = True
else:
threaded = False
if options.realName:
realName = options.realName
else: else:
name = something('What should the name of the plugin be?') name = something('What should the name of the plugin be?')
if name.endswith('.py'): if name.endswith('.py'):
@ -255,8 +250,11 @@ def main():
print() print()
threaded = yn('Does your plugin need to be threaded?') threaded = yn('Does your plugin need to be threaded?')
if options.realName:
realName = options.realName
else:
realName = something(textwrap.dedent(""" realName = something(textwrap.dedent("""
What is your real name, so I can fill in the copyright and license What is your name, so I can fill in the copyright and license
appropriately? appropriately?
""").strip()) """).strip())

106
test/test_plugin_create.py Normal file
View File

@ -0,0 +1,106 @@
###
# Copyright (c) 2018, James Lu <james@overdrivenetworks.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot
from supybot.test import *
import subprocess
import unittest
import os
import shutil
import compileall
TEST_PLUGIN_NAME = "TestPlugin"
class PluginCreateTestCase(SupyTestCase):
@staticmethod
def _communicate(proc, text):
outs, errs = proc.communicate(input=text)
supybot.log.info("testPluginCreate: supybot-plugin-create outs:")
for line in outs.splitlines():
supybot.log.info(" %s", line.decode())
supybot.log.info("testPluginCreate: supybot-plugin-create errs:")
for line in errs.splitlines():
supybot.log.info(" %s", line.decode())
def _makeplugin(self):
proc = subprocess.Popen(['supybot-plugin-create'], stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# In order: plugin name, threaded?, author, use Supybot license?, description
cmdinput = TEST_PLUGIN_NAME.encode() + b"""
n
Test Case Runner
y
Dummy test plugin
"""
self._communicate(proc, cmdinput)
def testPluginCreate(self):
tmpdir = conf.supybot.directories.data.tmp()
curdir = os.getcwd()
try:
os.chdir(tmpdir)
if TEST_PLUGIN_NAME in os.listdir('.'):
supybot.log.info("testPluginCreate: Removing old TestPlugin directory")
shutil.rmtree(TEST_PLUGIN_NAME)
self._makeplugin()
self.assertIn(TEST_PLUGIN_NAME, os.listdir('.'))
# Make sure that out generated plugin is valid
compileall.compile_dir(TEST_PLUGIN_NAME)
finally:
os.chdir(curdir)
class PluginCreateNoninteractiveTestCase(PluginCreateTestCase):
def _makeplugin(self):
with open(os.devnull, 'w') as devnull: # Compat with Python < 3.3
retcode = subprocess.call(['supybot-plugin-create', '-n', TEST_PLUGIN_NAME,
'--author=skynet', '--desc=Some description'],
stdin=devnull)
self.assertFalse(retcode) # Check that the return code is 0
class PluginCreatePartialArgsTestCase(PluginCreateTestCase):
def _makeplugin(self):
# We passed in a subset of args, so the script should only prompt for the
# ones not given
proc = subprocess.Popen(['supybot-plugin-create', '-n', TEST_PLUGIN_NAME],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# In order: threaded?, author, use Supybot license?, description
cmdinput = TEST_PLUGIN_NAME.encode() + b"""
Test Case Runner
y
Dummy test plugin
"""
self._communicate(proc, cmdinput)