From ff5ead703326900f1dea4aaabae95196bb221281 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 7 Sep 2015 12:07:02 +0200 Subject: [PATCH] initial invoke tasks.py script tasks: build, test, coverage, clean_git mrbob: py_file, plugin --- bobtemplates/plugin/.mrbob.ini | 36 ++++ .../plugin/did/plugins/+plugin.name+.py.bob | 67 +++++++ bobtemplates/py_file/+file.name+.py.bob | 23 +++ bobtemplates/py_file/.mrbob.ini | 15 ++ docs/contribute.rst | 25 +++ tasks.py | 178 ++++++++++++++++++ 6 files changed, 344 insertions(+) create mode 100644 bobtemplates/plugin/.mrbob.ini create mode 100644 bobtemplates/plugin/did/plugins/+plugin.name+.py.bob create mode 100644 bobtemplates/py_file/+file.name+.py.bob create mode 100644 bobtemplates/py_file/.mrbob.ini create mode 100644 tasks.py diff --git a/bobtemplates/plugin/.mrbob.ini b/bobtemplates/plugin/.mrbob.ini new file mode 100644 index 00000000..64657a85 --- /dev/null +++ b/bobtemplates/plugin/.mrbob.ini @@ -0,0 +1,36 @@ +[questions] + +plugin.name.question = Name of the plugin (one word; lower-case) +plugin.name.required = True +plugin.name.help = Should be something like 'rh_bugzilla'. + +plugin.app.question = Name of the App (CamelCase) +plugin.app.required = True +plugin.app.help = Should be something like 'RedHatBugzilla'. + +plugin.target.question = Name of the object acted on (CamelCase) +plugin.target.required = True +plugin.target.help = Should be something like 'Bug' + +plugin.verb.question = Activity verb (one word; CamelCase; past-tense) +plugin.verb.required = True +plugin.verb.help = Should be something like 'saved' or 'filed' or 'sent' + +plugin.sort_order.question = What sort order to set for this plugin? +plugin.sort_order.required = False +plugin.sort_order.help = Should be an integer above (default: 500) +plugin.sort_order.default = 500 +plugin.sort_order.post_ask_question = mrbob.hooks:to_integer + +author.name.question = What is your full name? +author.name.required = True + +author.email.question = What is your email address? +author.email.required = True +#author.email.post_ask_question = bobtemplates.hooks:set_name_email + + +[template] + +post_render = bobtemplates.hooks:post_render +pre_render = bobtemplates.hooks:pre_render diff --git a/bobtemplates/plugin/did/plugins/+plugin.name+.py.bob b/bobtemplates/plugin/did/plugins/+plugin.name+.py.bob new file mode 100644 index 00000000..5d048908 --- /dev/null +++ b/bobtemplates/plugin/did/plugins/+plugin.name+.py.bob @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# coding: utf-8 +# Author: "{{{author.name}}}" <{{{author.email}}}> + +from __future__ import unicode_literals, absolute_import + +""" +{{{plugin.app}}} stats + +Config example:: + + [{{{plugin.name}}}] + type = {{{plugin.name}}} + # more config options +""" + +from did.base import Stats, StatsGroup +from did.utils import Config, item + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# {{{plugin.app}}} Investigator +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class {{{plugin.app}}}{{{plugin.target}}}(object): + """ {{{plugin.app}}} {{{plugin.target}}} investigator """ + + def __init__(self, **kwargs): + """ Initialize {{{plugin.target}}} """ + self.record = 'Testing ONE TWO THREE' + + def __unicode__(self): + """ Summary for displaying """ + return "{0}".format(self.record) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# {{{plugin.app}}} Stats +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class {{{plugin.target}}}{{{plugin.verb}}}(Stats): + """ {{{plugin.app}}} changes """ + def __init__(self, option='cards', name=None, parent=None): + super({{{plugin.target}}}{{{plugin.verb}}}, self).__init__(option, name, parent) + + def fetch(self): + if self.option == 'cards': + self.stats.append('CARD #X: DONE') + else: + self.stats.append('UNKNOWN: UNKNOWN') + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# {{{plugin.app}}} Stats Group +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class {{{plugin.app}}}Stats(StatsGroup): + """ {{{plugin.app}}} aggregated stats """ + + # Default order + order = {{{plugin.sort_order}}} + + def __init__(self, option, name=None, parent=None): + super({{{plugin.app}}}Stats, self).__init__(option, name, parent) + self.stats.append( + {{{plugin.target}}}{{{plugin.verb}}}(parent=self) + ) diff --git a/bobtemplates/py_file/+file.name+.py.bob b/bobtemplates/py_file/+file.name+.py.bob new file mode 100644 index 00000000..f1cabafa --- /dev/null +++ b/bobtemplates/py_file/+file.name+.py.bob @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# coding: utf-8 +# Author: "{{{author.name}}}" <{{{author.email}}}> + +# Notes: +# NOQA silences flake8 error checking for the given line + +''' +Default Python File Doc API +--------------------------- + +''' + +''' +Python 3 compatability +---------------------- +src: (http://python-future.org) +''' +from __future__ import absolute_import, division, print_function + + +# Default did logger (colorized) +from did.utils import log diff --git a/bobtemplates/py_file/.mrbob.ini b/bobtemplates/py_file/.mrbob.ini new file mode 100644 index 00000000..4eed17b0 --- /dev/null +++ b/bobtemplates/py_file/.mrbob.ini @@ -0,0 +1,15 @@ +[questions] + +file.name.question = File name? +file.name.required = True + +author.name.question = Your full name? +author.name.required = True + +author.email.question = Your email address? +author.email.required = True +# if we use ANY default answers parsed from an ~/.mrbob.ini file for example +# this doesn't seem to be called and thus the template breaks when trying +# to render because the variable which this hook would set if called isn't +# actually set since the hook isn't called (i'm guessing...). +#author.email.post_ask_question = bobtemplates.hooks:set_name_email diff --git a/docs/contribute.rst b/docs/contribute.rst index ec6fe1c5..75af36e1 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -38,3 +38,28 @@ or pip:: # sudo required if not in a virtualenv pip install pytest coveralls + + +Invoke +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use `invoke` command to run certain built-in project +commands:: + + pip install invoke + invoke --list + invoke --help coverage + invoke coverage + +MrBob +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +With or without `invoke` you can also use `mrbob` to easily create +templates to help you get started contributing. Demo using `invoke`:: + + pip install mrbob + invoke bob_did_plugin + +`mrbob` should have asked you a few questions before creating a +new basic Stats plugin for you in `did/plugins/`. Check `git status` +to see the new files it created as a result. diff --git a/tasks.py b/tasks.py new file mode 100644 index 00000000..330bed7c --- /dev/null +++ b/tasks.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python +# coding: utf-8 +# Author: "Chris Ward" + +from __future__ import absolute_import, unicode_literals + +import os + +from invoke import task, run + +try: + from did.utils import log, LOG_INFO +except Exception as e: + import logging + LOG_INFO = logging.INFO + log = logging.getLogger() + +# set log level to INFO +log.setLevel(LOG_INFO) + + +@task +def build(sdist=False, rpm=False): + ''' + Build Packages + -------------- + + Supports + + * sdist + * rpm [fedora] + + ''' + log.info("Running build command!") + if sdist: + run("python setup.py sdist") + if rpm: + run("make rpm") + return + + +@task +def pytest(): + ''' + Run pytest + ---------- + ''' + log.info("Running `{}` command!".format(__name__)) + run("py.test tests/") + return + + +@task +def coverage(report=True, coveralls=False, append=True): + ''' + Run Coverage Test [pytest] + -------------------------- + + Supports + * coverage [reporting, coveralls, append] + * coveralls + + Do not support (yet) + * coverage [xml] + ''' + log.info("Running `{}` command!".format(__name__)) + opts = {} + # save to new timestamped file in ~/.coverage ? + _cmd = "coverage run {run_opts}--source=did -m py.test tests" + + opts['run_opts'] = '--append ' if append else '' + cmd = _cmd.format(**opts) + + log.info(" ... Options: {}".format(opts)) + run(cmd) + if coveralls: + run("coveralls") + if report: + run("coverage report") + return + + +@task +def docs(html=False): + ''' + Build Documentation + ------------------- + + ''' + log.info("Running Documentation command!") + _cmd = "cd docs;" + if html: + cmd = "{0} make html".format(_cmd) + else: + cmd = "{0} make html".format(_cmd) + run(cmd) + return + + +@task +def clean_git(force=False, options='Xd'): + ''' + Clean Your Repo + --------------- + + ''' + log.info("Running Git Repository Clean command") + force = 'f' if force else 'n' + cmd = "git clean" + if options: + _options = ' -{}{}'.format(options, force) + cmd += _options + log.info(" ... Options: {}".format(_options)) + run(cmd) + + +_bob_py_file_help = { + 'name': "Name of the python file", + 'answers': "path to mrbob.ini with [variables] defined", + # FIXME: Add remaining bob_py_file_help strings +} + + +# FIXME: make 'bob' stuff reusable? redefining code here +# and in the other bob func below... not good. +@task(help=_bob_py_file_help) +def bob_py_file(path='./', answers='~/.mrbob.ini', overwrite=False): + ''' + MrBob: New .py File + ------------------- + + Note: MrBob asks for filename rather than pass it here to the task + to make it more easily accessible from the template + + ''' + log.info("MrBob is building a .py file") + + template = 'bobtemplates/py_file' + + path = path or './' + answers = answers or os.path.expanduser('~/.mrbob.ini') + + cmd = 'mrbob {} -O {}'.format(template, path) + if answers: + cmd += ' -c {}'.format(answers) + + log.info(" ... Defaults: {}".format(answers)) + run(cmd, pty=True) + + +@task +def bob_did_plugin(path=None, answers=None, overwrite=False): + ''' + MrBob: New did Plugin + --------------------- + + Create all the files needed to start writing a new did plugin. + + Note: MrBob asks for filename rather than pass it here to the task + to make it more easily accessible from the template + + ''' + log.info("MrBob is building a did plugin template") + + template = 'bobtemplates/plugin' + + path = path or './did/plugins' + + cmd = 'mrbob {} -O {}'.format(template, path) + + if answers: + if not os.path.exists(answers): + # if the path doesn't exist, don't try to load it + raise RuntimeError('{} does not exist'.format(answers)) + cmd += ' -c {}'.format(answers) + + log.info(" ... Defaults: {}".format(answers)) + run(cmd, pty=True)