# HG changeset patch # User Julien Cristau # Date 1402386585 -7200 # Node ID 112c884b2d8d97443352595456973161f5c660e7 # Parent 5607ef9ab9f59d08f4d1f81abb8b646f892da256# Parent d3f8866a48f5e7172f2a169b303081d0259f2a44 merge 3.18.5 into 3.19 branch diff -r 5607ef9ab9f5 -r 112c884b2d8d .hgtags --- a/.hgtags Wed Apr 09 17:29:25 2014 +0200 +++ b/.hgtags Tue Jun 10 09:49:45 2014 +0200 @@ -329,6 +329,9 @@ fa00fc251d57f61e619d9c905502745fae21c58c cubicweb-centos-version-3.17.14-1 fa00fc251d57f61e619d9c905502745fae21c58c cubicweb-version-3.17.14 fa00fc251d57f61e619d9c905502745fae21c58c cubicweb-debian-version-3.17.14-1 +ee413076752b3e606801ef55e48f7e7ccd1f7238 cubicweb-version-3.17.15 +ee413076752b3e606801ef55e48f7e7ccd1f7238 cubicweb-debian-version-3.17.15-1 +ee413076752b3e606801ef55e48f7e7ccd1f7238 cubicweb-centos-version-3.17.15-1 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-version-3.18.0 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-debian-version-3.18.0-1 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-centos-version-3.18.0-1 @@ -344,6 +347,9 @@ 0176da9bc75293e200de4f7b934c5d4c7c805199 cubicweb-version-3.18.4 0176da9bc75293e200de4f7b934c5d4c7c805199 cubicweb-debian-version-3.18.4-1 0176da9bc75293e200de4f7b934c5d4c7c805199 cubicweb-centos-version-3.18.4-1 +5071b69b6b0b0de937bb231404cbf652a103dbe0 cubicweb-version-3.18.5 +5071b69b6b0b0de937bb231404cbf652a103dbe0 cubicweb-debian-version-3.18.5-1 +5071b69b6b0b0de937bb231404cbf652a103dbe0 cubicweb-centos-version-3.18.5-1 1141927b8494aabd16e31b0d0d9a50fe1fed5f2f cubicweb-version-3.19.0 1141927b8494aabd16e31b0d0d9a50fe1fed5f2f cubicweb-debian-version-3.19.0-1 1141927b8494aabd16e31b0d0d9a50fe1fed5f2f cubicweb-centos-version-3.19.0-1 diff -r 5607ef9ab9f5 -r 112c884b2d8d cwctl.py --- a/cwctl.py Wed Apr 09 17:29:25 2014 +0200 +++ b/cwctl.py Tue Jun 10 09:49:45 2014 +0200 @@ -317,7 +317,7 @@ 'used together') class CreateInstanceCommand(Command): - """Create an instance from a cube. This is an unified + """Create an instance from a cube. This is a unified command which can handle web / server / all-in-one installation according to available parts of the software library and of the desired cube. @@ -849,7 +849,7 @@ in batch mode. By default it will connect to a local instance using an in memory - connection, unless an URL to a running instance is specified. + connection, unless a URL to a running instance is specified. Arguments after bare "--" string will not be processed by the shell command You can use it to pass extra arguments to your script and expect for diff -r 5607ef9ab9f5 -r 112c884b2d8d cwvreg.py --- a/cwvreg.py Wed Apr 09 17:29:25 2014 +0200 +++ b/cwvreg.py Tue Jun 10 09:49:45 2014 +0200 @@ -753,7 +753,7 @@ return self.property_info(key)['default'] def typed_value(self, key, value): - """value is an unicode string, return it correctly typed. Let potential + """value is a unicode string, return it correctly typed. Let potential type error propagates. """ pdef = self.property_info(key) diff -r 5607ef9ab9f5 -r 112c884b2d8d dataimport.py --- a/dataimport.py Wed Apr 09 17:29:25 2014 +0200 +++ b/dataimport.py Tue Jun 10 09:49:45 2014 +0200 @@ -141,13 +141,13 @@ for row in it: decoded = [item.decode(encoding) for item in row] if not skip_empty or any(decoded): - yield [item.decode(encoding) for item in row] + yield decoded else: - # Skip first line - try: - row = it.next() - except csv.Error: - pass + if skipfirst: + try: + row = it.next() + except csv.Error: + pass # Safe version, that can cope with error in CSV file while True: try: @@ -472,11 +472,13 @@ if isinstance(value, unicode): value = value.encode(encoding) elif isinstance(value, (date, datetime)): - # Do not use strftime, as it yields issue - # with date < 1900 value = '%04d-%02d-%02d' % (value.year, value.month, value.day) + if isinstance(value, datetime): + value += ' %02d:%02d:%02d' % (value.hour, + value.minutes, + value.second) else: return None # We push the value to the new formatted row @@ -860,30 +862,38 @@ del entity.cw_extra_kwargs entity.cw_edited = EditedEntity(entity) for attr in self.etype_attrs: - entity.cw_edited.edited_attribute(attr, self.generate(entity, attr)) + genfunc = self.generate(attr) + if genfunc: + entity.cw_edited.edited_attribute(attr, genfunc(entity)) rels = {} for rel in self.etype_rels: - rels[rel] = self.generate(entity, rel) + genfunc = self.generate(rel) + if genfunc: + rels[rel] = genfunc(entity) return entity, rels def init_entity(self, entity): entity.eid = self.source.create_eid(self.session) for attr in self.entity_attrs: - entity.cw_edited.edited_attribute(attr, self.generate(entity, attr)) + genfunc = self.generate(attr) + if genfunc: + entity.cw_edited.edited_attribute(attr, genfunc(entity)) - def generate(self, entity, rtype): - return getattr(self, 'gen_%s' % rtype)(entity) + def generate(self, rtype): + return getattr(self, 'gen_%s' % rtype, None) def gen_cwuri(self, entity): return u'%s%s' % (self.baseurl, entity.eid) def gen_creation_date(self, entity): return self.time + def gen_modification_date(self, entity): return self.time def gen_created_by(self, entity): return self.session.user.eid + def gen_owned_by(self, entity): return self.session.user.eid diff -r 5607ef9ab9f5 -r 112c884b2d8d dbapi.py --- a/dbapi.py Wed Apr 09 17:29:25 2014 +0200 +++ b/dbapi.py Tue Jun 10 09:49:45 2014 +0200 @@ -118,7 +118,7 @@ * a simple instance id for in-memory connection - * an uri like scheme://host:port/instanceid where scheme may be one of + * a uri like scheme://host:port/instanceid where scheme may be one of 'pyro', 'inmemory' or 'zmqpickle' * if scheme is 'pyro', determine the name server address. If @@ -409,7 +409,7 @@ """execute a rql query, return resulting rows and their description in a :class:`~cubicweb.rset.ResultSet` object - * `rql` should be an Unicode string or a plain ASCII string, containing + * `rql` should be a Unicode string or a plain ASCII string, containing the rql query * `args` the optional args dictionary associated to the query, with key diff -r 5607ef9ab9f5 -r 112c884b2d8d debian/changelog --- a/debian/changelog Wed Apr 09 17:29:25 2014 +0200 +++ b/debian/changelog Tue Jun 10 09:49:45 2014 +0200 @@ -10,6 +10,12 @@ -- Julien Cristau Mon, 28 Apr 2014 18:35:27 +0200 +cubicweb (3.18.5-1) unstable; urgency=low + + * new upstream release + + -- Aurelien Campeas Thu, 05 Jun 2014 16:13:03 +0200 + cubicweb (3.18.4-1) unstable; urgency=low * new upstream release @@ -40,6 +46,12 @@ -- Julien Cristau Fri, 10 Jan 2014 17:14:18 +0100 +cubicweb (3.17.15-1) unstable; urgency=low + + * new upstream release + + -- Aurelien Campeas Wed, 13 May 2014 17:47:00 +0200 + cubicweb (3.17.14-1) unstable; urgency=low * new upstream release diff -r 5607ef9ab9f5 -r 112c884b2d8d doc/tutorials/dataimport/diseasome_parser.py --- a/doc/tutorials/dataimport/diseasome_parser.py Wed Apr 09 17:29:25 2014 +0200 +++ b/doc/tutorials/dataimport/diseasome_parser.py Tue Jun 10 09:49:45 2014 +0200 @@ -31,9 +31,9 @@ def _retrieve_reltype(uri): """ - Retrieve a relation type from an URI. + Retrieve a relation type from a URI. - Internal function which takes an URI containing a relation type as input + Internal function which takes a URI containing a relation type as input and returns the name of the relation. If no URI string is given, then the function returns None. """ diff -r 5607ef9ab9f5 -r 112c884b2d8d doc/tutorials/dataimport/schema.py --- a/doc/tutorials/dataimport/schema.py Wed Apr 09 17:29:25 2014 +0200 +++ b/doc/tutorials/dataimport/schema.py Tue Jun 10 09:49:45 2014 +0200 @@ -47,7 +47,7 @@ only has unique omim and omim_page identifiers, when it has them, these attributes have been defined through relations such that for each disease there is at most one omim and one omim_page. - Each such identifier is defined through an URI, that is, through + Each such identifier is defined through a URI, that is, through an ``ExternalUri`` entity. That is, these relations are of cardinality "?*". For optimization purposes, one might be tempted to defined them as inlined, by setting @@ -55,7 +55,7 @@ - chromosomal_location is also defined through a relation of cardinality "?*", since any disease has at most one chromosomal location associated to it. - - same_as is also defined through an URI, and hence through a + - same_as is also defined through a URI, and hence through a relation having ``ExternalUri`` entities as objects. For more information on this data set and the data set itself, @@ -109,12 +109,12 @@ - label, defined through a Yams String. - bio2rdf_symbol, also defined as a Yams String, since it is just an identifier. - - gene_id is an URI identifying a gene, hence it is defined + - gene_id is a URI identifying a gene, hence it is defined as a relation with an ``ExternalUri`` object. - a pair of unique identifiers in the HUGO Gene Nomenclature Committee (http://http://www.genenames.org/). They are defined as ``ExternalUri`` entities as well. - - same_as is also defined through an URI, and hence through a + - same_as is also defined through a URI, and hence through a relation having ``ExternalUri`` entities as objects. """ # Corresponds to http://www.w3.org/2000/01/rdf-schema#label diff -r 5607ef9ab9f5 -r 112c884b2d8d entities/adapters.py --- a/entities/adapters.py Wed Apr 09 17:29:25 2014 +0200 +++ b/entities/adapters.py Tue Jun 10 09:49:45 2014 +0200 @@ -166,7 +166,7 @@ __abstract__ = True def download_url(self, **kwargs): # XXX not really part of this interface - """return an url to download entity's content""" + """return a URL to download entity's content""" raise NotImplementedError def download_content_type(self): @@ -187,13 +187,11 @@ # XXX should propose to use two different relations for children/parent class ITreeAdapter(view.EntityAdapter): - """This adapter has to be overriden to be configured using the - tree_relation, child_role and parent_role class attributes to benefit from - this default implementation. + """This adapter provides a tree interface. - This adapter provides a tree interface. It has to be overriden to be - configured using the tree_relation, child_role and parent_role class - attributes to benefit from this default implementation. + It has to be overriden to be configured using the tree_relation, + child_role and parent_role class attributes to benefit from this default + implementation. This class provides the following methods: diff -r 5607ef9ab9f5 -r 112c884b2d8d req.py --- a/req.py Wed Apr 09 17:29:25 2014 +0200 +++ b/req.py Tue Jun 10 09:49:45 2014 +0200 @@ -299,7 +299,7 @@ return u'%s%s?%s' % (base_url, path, self.build_url_params(**kwargs)) def build_url_params(self, **kwargs): - """return encoded params to incorporate them in an URL""" + """return encoded params to incorporate them in a URL""" args = [] for param, values in kwargs.iteritems(): if not isinstance(values, (list, tuple)): @@ -365,7 +365,20 @@ @cached def user_data(self): - """returns a dictionary with this user's information""" + """returns a dictionary with this user's information. + + The keys are : + + login + The user login + + name + The user name, returned by user.name() + + email + The user principal email + + """ userinfo = {} user = self.user userinfo['login'] = user.login diff -r 5607ef9ab9f5 -r 112c884b2d8d server/migractions.py --- a/server/migractions.py Wed Apr 09 17:29:25 2014 +0200 +++ b/server/migractions.py Tue Jun 10 09:49:45 2014 +0200 @@ -1064,12 +1064,19 @@ if commit: self.commit() - def cmd_rename_relation_type(self, oldname, newname, commit=True): + def cmd_rename_relation_type(self, oldname, newname, commit=True, force=False): """rename an existing relation `oldname` is a string giving the name of the existing relation `newname` is a string giving the name of the renamed relation + + If `force` is True, proceed even if `oldname` still appears in the fs schema """ + if oldname in self.fs_schema and not force: + if not self.confirm('Relation %s is still present in the filesystem schema,' + ' do you really want to drop it?' % oldname, + default='n'): + raise SystemExit(1) self.cmd_add_relation_type(newname, commit=True) self.rqlexec('SET X %s Y WHERE X %s Y' % (newname, oldname), ask_confirm=self.verbosity>=2) diff -r 5607ef9ab9f5 -r 112c884b2d8d server/querier.py --- a/server/querier.py Wed Apr 09 17:29:25 2014 +0200 +++ b/server/querier.py Tue Jun 10 09:49:45 2014 +0200 @@ -505,7 +505,7 @@ """execute a rql query, return resulting rows and their description in a `ResultSet` object - * `rql` should be an Unicode string or a plain ASCII string + * `rql` should be a Unicode string or a plain ASCII string * `args` the optional parameters dictionary associated to the query * `build_descr` is a boolean flag indicating if the description should be built on select queries (if false, the description will be en empty diff -r 5607ef9ab9f5 -r 112c884b2d8d server/repository.py --- a/server/repository.py Wed Apr 09 17:29:25 2014 +0200 +++ b/server/repository.py Tue Jun 10 09:49:45 2014 +0200 @@ -684,7 +684,7 @@ txid=None): """execute a RQL query - * rqlstring should be an unicode string or a plain ascii string + * rqlstring should be a unicode string or a plain ascii string * args the optional parameters used in the query * build_descr is a flag indicating if the description should be built on select queries @@ -1262,11 +1262,7 @@ if relcache is not None: cnx.update_rel_cache_del(entity.eid, attr, prevvalue) del_existing_rel_if_needed(cnx, entity.eid, attr, value) - if relcache is not None: - cnx.update_rel_cache_add(entity.eid, attr, value) - else: - entity.cw_set_relation_cache(attr, 'subject', - cnx.eid_rset(value)) + cnx.update_rel_cache_add(entity.eid, attr, value) hm.call_hooks('after_add_relation', cnx, eidfrom=entity.eid, rtype=attr, eidto=value) finally: diff -r 5607ef9ab9f5 -r 112c884b2d8d server/sources/native.py --- a/server/sources/native.py Wed Apr 09 17:29:25 2014 +0200 +++ b/server/sources/native.py Tue Jun 10 09:49:45 2014 +0200 @@ -709,7 +709,7 @@ # instance print 'exec', query, args, getattr(cnx, '_cnx', cnx) try: - # str(query) to avoid error if it's an unicode string + # str(query) to avoid error if it's a unicode string cursor.execute(str(query), args) except Exception as ex: if self.repo.config.mode != 'test': @@ -756,7 +756,7 @@ print 'execmany', query, 'with', len(args), 'arguments' cursor = cnx.cnxset.cu try: - # str(query) to avoid error if it's an unicode string + # str(query) to avoid error if it's a unicode string cursor.executemany(str(query), args) except Exception as ex: if self.repo.config.mode != 'test': diff -r 5607ef9ab9f5 -r 112c884b2d8d server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Wed Apr 09 17:29:25 2014 +0200 +++ b/server/test/unittest_rql2sql.py Tue Jun 10 09:49:45 2014 +0200 @@ -1882,12 +1882,12 @@ for t in self._parse([("Any X WHERE X creation_date TODAY, X is Affaire", '''SELECT _X.cw_eid FROM cw_Affaire AS _X -WHERE DATE(_X.cw_creation_date)=CURRENT_DATE'''), +WHERE DATE(_X.cw_creation_date)=%s''' % self.dbhelper.sql_current_date()), ("Personne P where not P datenaiss TODAY", '''SELECT _P.cw_eid FROM cw_Personne AS _P -WHERE NOT (DATE(_P.cw_datenaiss)=CURRENT_DATE)'''), +WHERE NOT (DATE(_P.cw_datenaiss)=%s)''' % self.dbhelper.sql_current_date()), ]): yield t diff -r 5607ef9ab9f5 -r 112c884b2d8d sobjects/notification.py --- a/sobjects/notification.py Wed Apr 09 17:29:25 2014 +0200 +++ b/sobjects/notification.py Tue Jun 10 09:49:45 2014 +0200 @@ -130,9 +130,10 @@ # since the same view (eg self) may be called multiple time and we # need a fresh stream at each iteration, reset it explicitly self.w = None - # XXX call render before subject to set .row/.col attributes on the - # view try: + # XXX forcing the row & col here may make the content and + # subject inconsistent because subject will depend on + # self.cw_row & self.cw_col if they are set. content = self.render(row=0, col=0, **kwargs) subject = self.subject() except SkipEmail: diff -r 5607ef9ab9f5 -r 112c884b2d8d test/data/schema.py --- a/test/data/schema.py Wed Apr 09 17:29:25 2014 +0200 +++ b/test/data/schema.py Tue Jun 10 09:49:45 2014 +0200 @@ -60,7 +60,7 @@ class Produit(EntityType): - fabrique_par = SubjectRelation('Usine', cardinality='1*') + fabrique_par = SubjectRelation('Usine', cardinality='1*', inlined=True) class Usine(EntityType): diff -r 5607ef9ab9f5 -r 112c884b2d8d test/unittest_dataimport.py --- a/test/unittest_dataimport.py Wed Apr 09 17:29:25 2014 +0200 +++ b/test/unittest_dataimport.py Tue Jun 10 09:49:45 2014 +0200 @@ -1,6 +1,8 @@ from StringIO import StringIO from logilab.common.testlib import TestCase, unittest_main from cubicweb import dataimport + + class UcsvreaderTC(TestCase): def test_empty_lines_skipped(self): @@ -21,6 +23,34 @@ ], list(dataimport.ucsvreader(stream, skip_empty=False))) + def test_skip_first(self): + stream = StringIO('a,b,c,d,\n' + '1,2,3,4,\n') + reader = dataimport.ucsvreader(stream, skipfirst=True, + ignore_errors=True) + self.assertEqual(list(reader), + [[u'1', u'2', u'3', u'4', u'']]) + + stream.seek(0) + reader = dataimport.ucsvreader(stream, skipfirst=True, + ignore_errors=False) + self.assertEqual(list(reader), + [[u'1', u'2', u'3', u'4', u'']]) + + stream.seek(0) + reader = dataimport.ucsvreader(stream, skipfirst=False, + ignore_errors=True) + self.assertEqual(list(reader), + [[u'a', u'b', u'c', u'd', u''], + [u'1', u'2', u'3', u'4', u'']]) + + stream.seek(0) + reader = dataimport.ucsvreader(stream, skipfirst=False, + ignore_errors=False) + self.assertEqual(list(reader), + [[u'a', u'b', u'c', u'd', u''], + [u'1', u'2', u'3', u'4', u'']]) + if __name__ == '__main__': unittest_main() diff -r 5607ef9ab9f5 -r 112c884b2d8d test/unittest_entity.py --- a/test/unittest_entity.py Wed Apr 09 17:29:25 2014 +0200 +++ b/test/unittest_entity.py Tue Jun 10 09:49:45 2014 +0200 @@ -44,6 +44,18 @@ for cls in self.vreg['etypes'].iter_classes(): cls.fetch_attrs, cls.cw_fetch_order = self.backup_dict[cls] + def test_no_prefill_related_cache_bug(self): + session = self.session + usine = session.create_entity('Usine', lieu=u'Montbeliard') + produit = session.create_entity('Produit') + # usine was prefilled in glob_add_entity + # let's simulate produit creation without prefill + produit._cw_related_cache.clear() + # use add_relations + session.add_relations([('fabrique_par', [(produit.eid, usine.eid)])]) + self.assertEqual(1, len(usine.reverse_fabrique_par)) + self.assertEqual(1, len(produit.fabrique_par)) + def test_boolean_value(self): with self.admin_access.web_request() as req: e = self.vreg['etypes'].etype_class('CWUser')(req) diff -r 5607ef9ab9f5 -r 112c884b2d8d view.py --- a/view.py Wed Apr 09 17:29:25 2014 +0200 +++ b/view.py Tue Jun 10 09:49:45 2014 +0200 @@ -502,7 +502,7 @@ """simple mixin for reloadable parts of UI""" def user_callback(self, cb, args, msg=None, nonify=False): - """register the given user callback and return an url to call it ready to be + """register the given user callback and return a URL to call it ready to be inserted in html """ self._cw.add_js('cubicweb.ajax.js') diff -r 5607ef9ab9f5 -r 112c884b2d8d web/component.py --- a/web/component.py Wed Apr 09 17:29:25 2014 +0200 +++ b/web/component.py Tue Jun 10 09:49:45 2014 +0200 @@ -448,7 +448,7 @@ return domid(self.__regid__) + unicode(self.entity.eid) def lazy_view_holder(self, w, entity, oid, registry='views'): - """add a holder and return an url that may be used to replace this + """add a holder and return a URL that may be used to replace this holder by the html generate by the view specified by registry and identifier. Registry defaults to 'views'. """ diff -r 5607ef9ab9f5 -r 112c884b2d8d web/data/cubicweb.ajax.js --- a/web/data/cubicweb.ajax.js Wed Apr 09 17:29:25 2014 +0200 +++ b/web/data/cubicweb.ajax.js Tue Jun 10 09:49:45 2014 +0200 @@ -373,7 +373,7 @@ /** * .. function:: loadRemote(url, form, reqtype='GET', sync=false) * - * Asynchronously (unless `sync` argument is set to true) load an url or path + * Asynchronously (unless `sync` argument is set to true) load a URL or path * and return a deferred whose callbacks args are decoded according to the * Content-Type response header. `form` should be additional form params * dictionary, `reqtype` the HTTP request type (get 'GET' or 'POST'). diff -r 5607ef9ab9f5 -r 112c884b2d8d web/data/cubicweb.htmlhelpers.js --- a/web/data/cubicweb.htmlhelpers.js Wed Apr 09 17:29:25 2014 +0200 +++ b/web/data/cubicweb.htmlhelpers.js Tue Jun 10 09:49:45 2014 +0200 @@ -64,7 +64,7 @@ /** * .. function:: asURL(props) * - * builds an url from an object (used as a dictionary) + * builds a URL from an object (used as a dictionary) * * >>> asURL({'rql' : "RQL", 'x': [1, 2], 'itemvid' : "oneline"}) * rql=RQL&vid=list&itemvid=oneline&x=1&x=2 diff -r 5607ef9ab9f5 -r 112c884b2d8d web/formfields.py --- a/web/formfields.py Wed Apr 09 17:29:25 2014 +0200 +++ b/web/formfields.py Tue Jun 10 09:49:45 2014 +0200 @@ -382,7 +382,7 @@ called by widgets which requires a vocabulary. It should return a list of tuple (label, value), where value - *must be an unicode string*, not a typed value. + *must be a unicode string*, not a typed value. """ assert self.choices is not None if callable(self.choices): diff -r 5607ef9ab9f5 -r 112c884b2d8d web/formwidgets.py --- a/web/formwidgets.py Wed Apr 09 17:29:25 2014 +0200 +++ b/web/formwidgets.py Tue Jun 10 09:49:45 2014 +0200 @@ -311,7 +311,7 @@ # basic html widgets ########################################################### class TextInput(Input): - """Simple , will return an unicode string.""" + """Simple , will return a unicode string.""" type = 'text' @@ -321,7 +321,7 @@ class PasswordSingleInput(Input): - """Simple , will return an utf-8 encoded string. + """Simple , will return a utf-8 encoded string. You may prefer using the :class:`~cubicweb.web.formwidgets.PasswordInput` widget which handles password confirmation. @@ -338,7 +338,7 @@ class PasswordInput(Input): """ and a confirmation input. Form processing will fail if password and confirmation differs, else it will return the password - as an utf-8 encoded string. + as a utf-8 encoded string. """ type = 'password' @@ -379,7 +379,7 @@ class HiddenInput(Input): - """Simple for hidden value, will return an unicode + """Simple for hidden value, will return a unicode string. """ type = 'hidden' @@ -388,7 +388,7 @@ class ButtonInput(Input): - """Simple , will return an unicode string. + """Simple , will return a unicode string. If you want a global form button, look at the :class:`Button`, :class:`SubmitButton`, :class:`ResetButton` and :class:`ImgButton` below. @@ -397,7 +397,7 @@ class TextArea(FieldWidget): - """Simple