"""Helpers to fill and submit forms.""" import operator import re from bs4 import BeautifulSoup from collections import OrderedDict from webtest import utils class NoValue: pass class Upload: """ A file to upload:: >>> Upload('filename.txt', 'data', 'application/octet-stream') >>> Upload('filename.txt', 'data') >>> Upload("README.txt") :param filename: Name of the file to upload. :param content: Contents of the file. :param content_type: MIME type of the file. """ def __init__(self, filename, content=None, content_type=None): self.filename = filename self.content = content self.content_type = content_type def __iter__(self): yield self.filename if self.content is not None: yield self.content yield self.content_type # TODO: do we handle the case when we need to get # contents ourselves? def __repr__(self): return '' % self.filename class Field: """Base class for all Field objects. .. attribute:: classes Dictionary of field types (select, radio, etc) .. attribute:: value Set/get value of the field. """ classes = {} def __init__(self, form, tag, name, pos, value=None, id=None, **attrs): self.form = form self.tag = tag self.name = name self.pos = pos self._value = value self.id = id self.attrs = attrs def value__get(self): if self._value is None: return '' else: return self._value def value__set(self, value): self._value = value value = property(value__get, value__set) def force_value(self, value): """Like setting a value, except forces it (even for, say, hidden fields). """ self._value = value def __repr__(self): value = f'<{self.__class__.__name__} name="{self.name}"' if self.id: value += ' id="%s"' % self.id return value + '>' class Select(Field): """Field representing ````""" def __init__(self, *args, **attrs): super().__init__(*args, **attrs) self.options = [] # Undetermined yet: self.selectedIndices = [] self._forced_values = NoValue def force_value(self, values): """Like setting a value, except forces it (even for, say, hidden fields). """ self._forced_values = values self.selectedIndices = [] def select_multiple(self, value=None, texts=None): if value is not None and texts is not None: raise ValueError("Specify only one of value and texts.") if texts is not None: value = self._get_value_for_texts(texts) self.value = value def _get_value_for_texts(self, texts): str_texts = [utils.stringify(text) for text in texts] value = [] for i, (option, checked, text) in enumerate(self.options): if text in str_texts: value.append(option) str_texts.remove(text) if str_texts: raise ValueError( "Option(s) %r not found (from %s)" % (', '.join(str_texts), ', '.join([repr(t) for o, c, t in self.options]))) return value def value__set(self, values): if not values: self._forced_values = None elif self._forced_values is not NoValue: self._forced_values = NoValue str_values = [utils.stringify(value) for value in values] self.selectedIndices = [] for i, (option, checked, text) in enumerate(self.options): if option in str_values: self.selectedIndices.append(i) str_values.remove(option) if str_values: raise ValueError( "Option(s) %r not found (from %s)" % (', '.join(str_values), ', '.join([repr(o) for o, c, t in self.options]))) def value__get(self): if self._forced_values is not NoValue: return self._forced_values elif self.selectedIndices: return [self.options[i][0] for i in self.selectedIndices] else: selected_values = [] for option, checked, text in self.options: if checked: selected_values.append(option) return selected_values if selected_values else None value = property(value__get, value__set) class Radio(Select): """Field representing ````""" def value__get(self): if self._forced_value is not NoValue: return self._forced_value elif self.selectedIndex is not None: return self.options[self.selectedIndex][0] else: for option, checked, text in self.options: if checked: return option else: return None value = property(value__get, Select.value__set) class Checkbox(Field): """Field representing ```` .. attribute:: checked Returns True if checkbox is checked. """ def __init__(self, *args, **attrs): super().__init__(*args, **attrs) self._checked = 'checked' in attrs def value__set(self, value): self._checked = not not value def value__get(self): if self._checked: if self._value is None: return 'on' else: return self._value else: return None value = property(value__get, value__set) def checked__get(self): return bool(self._checked) def checked__set(self, value): self._checked = not not value checked = property(checked__get, checked__set) class Text(Field): """Field representing ````""" class Email(Field): """Field representing ````""" class File(Field): """Field representing ````""" # TODO: This doesn't actually handle file uploads and enctype def value__get(self): if self._value is None: return '' else: return self._value value = property(value__get, Field.value__set) class Textarea(Text): """Field representing ``