Permalink
Browse files

Fixes and tests to supybot-plugin-create; modernize the plugin templa…

…te (#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
  • Loading branch information...
jlu5 authored and ProgVal committed Jun 20, 2018
1 parent 72c4801 commit 11d4015f71111fd4addc416ba2c9c1fa2b5cba1f
Showing with 122 additions and 18 deletions.
  1. +16 −18 scripts/supybot-plugin-create
  2. +106 −0 test/test_plugin_create.py
@@ -92,11 +92,8 @@ license = license.lstrip()
pluginTemplate = '''
%s
-import supybot.utils as utils
+from supybot import utils, plugins, ircutils, callbacks
from supybot.commands import *
-import supybot.plugins as plugins
-import supybot.ircutils as ircutils
-import supybot.callbacks as callbacks
try:
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('%s')
@@ -120,8 +117,7 @@ Class = %s
configTemplate = '''
%s
-import supybot.conf as conf
-import supybot.registry as registry
+from supybot import conf, registry
try:
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('%s')
@@ -157,8 +153,9 @@ __init__Template = '''
%s: %s
"""
+import sys
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
# in here if you're keeping the plugin in CVS or some similar system.
@@ -176,7 +173,10 @@ __url__ = ''
from . import config
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.
reload(config)
reload(plugin)
@@ -219,20 +219,15 @@ def main():
help='sets the name for the plugin.')
parser.add_option('-t', '--thread', action='store_true', dest='threaded',
help='makes the plugin threaded.')
- parser.add_option('', '--real-name', action='store', dest='realName',
- help='Determines what real name the copyright is '
+ parser.add_option('-a', '--author', '--real-name', action='store',
+ dest='realName', help='Determines who the copyright is '
'assigned to.')
- parser.add_option('', '--desc', action='store', dest='desc',
+ parser.add_option('-d', '--desc', action='store', dest='desc',
help='Short description of plugin.')
(options, args) = parser.parse_args()
if options.name:
name = options.name
- if options.threaded:
- threaded = True
- else:
- threaded = False
- if options.realName:
- realName = options.realName
+ threaded = options.threaded
else:
name = something('What should the name of the plugin be?')
if name.endswith('.py'):
@@ -255,8 +250,11 @@ def main():
print()
threaded = yn('Does your plugin need to be threaded?')
+ if options.realName:
+ realName = options.realName
+ else:
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?
""").strip())
View
@@ -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)

0 comments on commit 11d4015

Please sign in to comment.