Skip to content

Commit

Permalink
Merge pull request #42 from izapolsk/fix_include
Browse files Browse the repository at this point in the history
Fix for two issues in View.include
  • Loading branch information
Milan Falešník committed Jul 11, 2017
2 parents 42015e8 + c950089 commit 91e4ae8
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 9 deletions.
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,8 @@ All the included widgets in the structure share their parent with the widget whe
including. So when instantiated, the underlying ``FormButtonsAdd`` has the same parent widget as
the ``ItemAddForm``. I did not think it would be wise to make the including widget a parent for the
included widgets due to the fact widgetastic fences the element lookup if ``ROOT`` is present on a
widget/view.
widget/view. However, ``View.include`` supports ``use_parent=True`` option which makes included
widgets use including widget as a parent for rare cases when it is really necessary.


.. `Switchable conditional views`:
Expand Down
22 changes: 16 additions & 6 deletions src/widgetastic/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,23 +131,25 @@ def __getattr__(self, attr):

class WidgetIncluder(Widgetable):
"""Includes widgets from another widget. Useful for sharing pieces of code."""
def __init__(self, widget_class):
def __init__(self, widget_class, use_parent=False):
self.widget_class = widget_class
self.use_parent = use_parent

def __repr__(self):
return '{}({})'.format(type(self).__name__, self.widget_class.__name__)


class IncludedWidget(object):
def __init__(self, included_id, widget_name):
def __init__(self, included_id, widget_name, use_parent):
self.included_id = included_id
self.widget_name = widget_name
self.use_parent = use_parent

def __get__(self, o, t=None):
if o is None:
return self

return o._get_included_widget(self.included_id, self.widget_name)
return o._get_included_widget(self.included_id, self.widget_name, self.use_parent)

def __repr__(self):
return '{}({}, {!r})'.format(type(self).__name__, self.included_id, self.widget_name)
Expand All @@ -173,6 +175,12 @@ def __new__(cls, name, bases, attrs):
for base in bases:
for key, value in six.iteritems(getattr(base, '_desc_name_mapping', {})):
desc_name_mapping[key] = value
for widget_includer in getattr(base, '_included_widgets', ()):
included_widgets.append(widget_includer)
for widget_name in widget_includer.widget_class.cls_widget_names():
new_attrs[widget_name] = IncludedWidget(widget_includer._seq_id, widget_name,
widget_includer.use_parent)

for key, value in six.iteritems(attrs):
if inspect.isclass(value) and issubclass(value, View):
new_attrs[key] = WidgetDescriptor(value)
Expand All @@ -181,7 +189,8 @@ def __new__(cls, name, bases, attrs):
included_widgets.append(value)
# Now generate accessors for each included widget
for widget_name in value.widget_class.cls_widget_names():
new_attrs[widget_name] = IncludedWidget(value._seq_id, widget_name)
new_attrs[widget_name] = IncludedWidget(value._seq_id, widget_name,
value.use_parent)
elif isinstance(value, Widgetable):
new_attrs[key] = value
desc_name_mapping[value] = key
Expand Down Expand Up @@ -268,12 +277,13 @@ def __init__(self, parent, arg1, arg2, logger=None):
self._widget_cache = {}
self._initialized_included_widgets = {}

def _get_included_widget(self, includer_id, widget_name):
def _get_included_widget(self, includer_id, widget_name, use_parent):
if includer_id not in self._initialized_included_widgets:
for widget_includer in self._included_widgets:
if widget_includer._seq_id == includer_id:
parent = self if use_parent else self.parent
self._initialized_included_widgets[widget_includer._seq_id] =\
widget_includer.widget_class(self.parent, self.logger)
widget_includer.widget_class(parent, self.logger)
break
else:
raise ValueError('Could not find includer #{}'.format(includer_id))
Expand Down
26 changes: 24 additions & 2 deletions testing/test_basic_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,16 +565,26 @@ class TestForm3(View):
fileinput = FileInput(id='fileinput')
inputs = View.include(TestForm2)

class TestForm4(TestForm3):
title = Text(locator='//h1')

class TestForm5(View):
fileinput = FileInput(id='fileinput')
inputs = View.include(TestForm2, use_parent=True)

class TestForm6(TestForm5):
input6 = TextInput(id='input')

class AFillable(Fillable):
def __init__(self, text):
self.text = text

def as_fill_value(self):
return self.text

form = TestForm3(browser)
form = TestForm4(browser)
# This repeats test_basic_widgets
assert isinstance(form, TestForm3)
assert isinstance(form, TestForm4)
data = form.read()
assert data['h3'] == 'test test'
assert data['input1'] == ''
Expand All @@ -596,6 +606,18 @@ def as_fill_value(self):
assert form.input1.fill(AFillable('a_test'))
assert not form.input1.fill(AFillable('a_test'))
assert form.input1.read() == 'a_test'
assert form.title.text == 'Hello'
assert isinstance(form.input1.parent.parent, type(browser))

form2 = TestForm6(browser)
assert isinstance(form2.input1.parent.parent, TestForm6)

form2.fill({'input6': 'some input'})
assert form2.input6.read() == 'some input'
form2.fill({'fileinput': 'blabla'})
assert form2.fill({'input1': 'typed into input 1'})
assert form2.input1.read() == 'typed into input 1'
assert form2.h3.read() == 'test test'

assert form.fileinput.fill('foo')
with pytest.raises(DoNotReadThisWidget):
Expand Down

0 comments on commit 91e4ae8

Please sign in to comment.