Skip to content

Commit

Permalink
DynamicChoiceStringParameter: a base class for dynamic lists in Force…
Browse files Browse the repository at this point in the history
…Scheduler

Provide a better mechanism for generating dynamic content for ForceScheduler, and
adjust the InheritBuildParameter to use this new base class.
  • Loading branch information
jaredgrubb authored and Jared Grubb committed Feb 23, 2013
1 parent 2b15446 commit ff57453
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 16 deletions.
31 changes: 29 additions & 2 deletions master/buildbot/schedulers/forcesched.py
Expand Up @@ -210,13 +210,40 @@ def parse_from_arg(self, s):
return s


class DynamicChoiceStringParameter(ChoiceStringParameter):
"""A list of strings, allowing the selection of one of the predefined values.
The 'strict' parameter controls whether values outside the predefined list
of choices are allowed"""
type = ChoiceStringParameter.type + ["dynamic"]

# Assume not strict. In general, there is no "session" to remember what each
# user was presented with, so there is no way to validate a user's selection.
# Custom validation can be implemented in a derived 'updateFromKwargs', if desired.
strict = False

class InheritBuildParameter(ChoiceStringParameter):
# A functor to return the choices available. This will be called for each
# form generation, and the first element will be presented as the default
# choice.
dynamicChoices = lambda masterStatus, buildername: []


class InheritBuildParameter(DynamicChoiceStringParameter):
"""A parameter that takes its values from another build"""
type = ChoiceStringParameter.type + ["inherit"]
type = DynamicChoiceStringParameter.type + ["inherit"]
name = "inherit"
compatible_builds = None
strict = True

def __init__(self, compatible_builds=None, **kw):
def dynamicChoices(masterStatus, buildername):
# cache the choices because that's what we've always done
self.choices = self.compatible_builds(masterStatus, buildername)
return self.choices

DynamicChoiceStringParameter.__init__(self,
compatible_builds=compatible_builds,
dynamicChoices=dynamicChoices,
**kw)
def getFromKwargs(self, kwargs):
raise ValidationError("InheritBuildParameter can only be used by properties")

Expand Down
25 changes: 12 additions & 13 deletions master/buildbot/status/web/builder.py
Expand Up @@ -25,7 +25,6 @@
map_branches, path_to_authzfail, ActionResource, \
getRequestCharset
from buildbot.schedulers.forcesched import ForceScheduler
from buildbot.schedulers.forcesched import InheritBuildParameter, NestedParameter
from buildbot.schedulers.forcesched import ValidationError
from buildbot.status.web.build import BuildsResource, StatusResourceBuild
from buildbot import util
Expand Down Expand Up @@ -177,23 +176,23 @@ def buildForceContextForField(req, default_props, sch, field, master, buildernam
pname = "%s.%s"%(sch.name, field.fullName)

default = field.default
if isinstance(field, InheritBuildParameter):
# yes, I know, its bad to overwrite the parameter attribute,
# but I dont have any other simple way of doing this atm.
field.choices = field.compatible_builds(master.status, buildername)
if field.choices:
default = field.choices[0]

if "dynamic" in field.type:
choices = field.dynamicChoices(master.status, buildername)
default_props[pname+".choices"] = choices
if choices:
default = choices[0]

default = req.args.get(pname, [default])[0]
if "bool" in field.type:
default_props[pname] = "checked" if default else ""
else:
default = "checked" if default else ""
elif isinstance(default, unicode):
# filter out unicode chars, and html stuff
if isinstance(default, unicode):
default = html.escape(default.encode('utf-8','ignore'))
default_props[pname] = default
default = html.escape(default.encode('utf-8','ignore'))

default_props[pname] = default

if isinstance(field, NestedParameter):
if "nested" in field.type:
for subfield in field.fields:
buildForceContextForField(req, default_props, sch, subfield, master, buildername)

Expand Down
3 changes: 2 additions & 1 deletion master/buildbot/status/web/templates/forms.html
Expand Up @@ -94,7 +94,8 @@
<span class="label">{{f.label}}</span>
<span class="select">
<select name='{{f.fullName}}' {{ f.multiple and "multiple" or ""}}>
{% for c in f.choices %}
{% set choices = f.choices if 'dynamic' not in f.type else default_props[sch.name+"."+f.fullName+".choices"] %}
{% for c in choices %}
<option {{(c in default_props[sch.name+"."+f.fullName]) and "selected" or ""}}>{{c}}</option>
{% endfor %}
</select>
Expand Down
26 changes: 26 additions & 0 deletions master/buildbot/test/unit/test_schedulers_forcesched.py
Expand Up @@ -22,6 +22,7 @@
from buildbot.schedulers.forcesched import ChoiceStringParameter, ValidationError
from buildbot.schedulers.forcesched import NestedParameter, AnyPropertyParameter
from buildbot.schedulers.forcesched import CodebaseParameter, BaseParameter
from buildbot.schedulers.forcesched import DynamicChoiceStringParameter
from buildbot.test.util import scheduler
from buildbot.test.util.config import ConfigErrorsMixin

Expand Down Expand Up @@ -379,6 +380,11 @@ def test_ChoiceParameterError(self):
klass=ChoiceStringParameter, choices=['t1','t2'],
debug=False)

def test_ChoiceParameterError_notStrict(self):
self.do_ParameterTest(value='t1', expect='t1',
strict=False,
klass=ChoiceStringParameter, choices=['t1','t2'])


def test_ChoiceParameterMultiple(self):
self.do_ParameterTest(value=['t1','t2'], expect=['t1','t2'],
Expand All @@ -393,6 +399,26 @@ def test_ChoiceParameterMultipleError(self):
multiple=True, debug=False)


def test_DynamicChoiceStringParameter(self):
self.do_ParameterTest(value='t1', expect='t1',
klass=DynamicChoiceStringParameter, dynamicChoices=lambda m,b: ['t1','t2'])

def test_DynamicChoiceStringParameter_notStrict(self):
# the 'dynamicChoices' parameter really doesnt get called, so it really doesnt apply here
self.do_ParameterTest(value='t3', expect='t3',
strict=False,
klass=DynamicChoiceStringParameter, dynamicChoices=lambda m,b: ['t1','t2'])

def test_DynamicChoiceStringParameter_strict(self):
# the 'dynamicChoices' value doesnt affect the validation, so even a "GOOD" value will
# fail for strict=True tests, unless programmer overrides and provides their own validation
self.do_ParameterTest(value='t1',
strict=True,
expect=ValidationError,
expectKind=Exception,
klass=DynamicChoiceStringParameter, dynamicChoices=lambda m,b: ['t1','t2'],
debug=False)

def test_NestedParameter(self):
fields = [
IntParameter(name="foo")
Expand Down
34 changes: 34 additions & 0 deletions master/docs/manual/cfg-schedulers.rst
Expand Up @@ -1121,6 +1121,10 @@ Example::
CodebaseParameter
#####################

::

CodebaseParameter(codebase="mycode")

This is a parameter group to specify a sourcestamp for a given codebase.

``codebase``
Expand All @@ -1147,6 +1151,36 @@ This is a parameter group to specify a sourcestamp for a given codebase.
A :ref:`parameter <ForceScheduler-Parameters>` specifying the project for
the build. The default value is a string parameter.


.. bb:sched:: DynamicChoiceStringParameter
DynamicChoiceStringParameter
############################

::

DynamicChoiceStringParameter(name="param",
dynamicChoices=functor)

This is a special parameter base class for generating dynamic content. It shares
the same fields as :py:class:`~ChoiceStringParameter`, but the choices will be
generated by a call to ``dynamicChoices``.

By default, this class works with ``strict=False``, and the selection will NOT be
validated. Since there is no method for tracking a user "session", it is not possible
to store the values displayed for a particular user and validate their selection against
"their" values. For a reference of ways to implement custom validation, please refer
to the source for the :py:class:`~InheritBuildParameter` subclass.

``dynamicChoices``

A function to generate the choices to be presented. This function is
given the master :py:class:`~buildbot.status.master.Status` instance as
first argument, and the current builder name as second argument. It should
return a list of values, from which the first element in the list will be
selected by default.


InheritBuildParameter
#####################

Expand Down
2 changes: 2 additions & 0 deletions master/docs/relnotes/index.rst
Expand Up @@ -27,6 +27,8 @@ Features

* :bb:step:`CopyDirectory` has been added.

* :bb:sched:`DynamicChoiceStringParameter` has been added.

* default.css now wraps preformatted text by default.

* Slaves can now be paused through the web status.
Expand Down

0 comments on commit ff57453

Please sign in to comment.